diff options
Diffstat (limited to 'src')
73 files changed, 1938 insertions, 1070 deletions
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/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/core/CMakeLists.txt b/src/core/CMakeLists.txt index 5db6a1b3a..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 |
| @@ -207,6 +209,8 @@ add_library(core STATIC | |||
| 207 | hle/kernel/k_memory_region.h | 209 | hle/kernel/k_memory_region.h |
| 208 | hle/kernel/k_memory_region_type.h | 210 | hle/kernel/k_memory_region_type.h |
| 209 | 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 | ||
| 210 | hle/kernel/k_page_heap.cpp | 214 | hle/kernel/k_page_heap.cpp |
| 211 | hle/kernel/k_page_heap.h | 215 | hle/kernel/k_page_heap.h |
| 212 | hle/kernel/k_page_linked_list.h | 216 | hle/kernel/k_page_linked_list.h |
| @@ -244,6 +248,8 @@ add_library(core STATIC | |||
| 244 | hle/kernel/k_system_control.h | 248 | hle/kernel/k_system_control.h |
| 245 | hle/kernel/k_thread.cpp | 249 | hle/kernel/k_thread.cpp |
| 246 | 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 | ||
| 247 | hle/kernel/k_thread_queue.cpp | 253 | hle/kernel/k_thread_queue.cpp |
| 248 | hle/kernel/k_thread_queue.h | 254 | hle/kernel/k_thread_queue.h |
| 249 | hle/kernel/k_trace.h | 255 | hle/kernel/k_trace.h |
| @@ -300,6 +306,8 @@ add_library(core STATIC | |||
| 300 | hle/service/am/applets/applet_error.h | 306 | hle/service/am/applets/applet_error.h |
| 301 | hle/service/am/applets/applet_general_backend.cpp | 307 | hle/service/am/applets/applet_general_backend.cpp |
| 302 | 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 | ||
| 303 | hle/service/am/applets/applet_profile_select.cpp | 311 | hle/service/am/applets/applet_profile_select.cpp |
| 304 | hle/service/am/applets/applet_profile_select.h | 312 | hle/service/am/applets/applet_profile_select.h |
| 305 | hle/service/am/applets/applet_software_keyboard.cpp | 313 | hle/service/am/applets/applet_software_keyboard.cpp |
diff --git a/src/core/arm/dynarmic/arm_dynarmic_32.cpp b/src/core/arm/dynarmic/arm_dynarmic_32.cpp index 286976623..c1c843b8f 100644 --- a/src/core/arm/dynarmic/arm_dynarmic_32.cpp +++ b/src/core/arm/dynarmic/arm_dynarmic_32.cpp | |||
| @@ -148,8 +148,8 @@ std::shared_ptr<Dynarmic::A32::Jit> ARM_Dynarmic_32::MakeJit(Common::PageTable* | |||
| 148 | config.wall_clock_cntpct = uses_wall_clock; | 148 | config.wall_clock_cntpct = uses_wall_clock; |
| 149 | 149 | ||
| 150 | // Code cache size | 150 | // Code cache size |
| 151 | config.code_cache_size = 512_MiB; | 151 | config.code_cache_size = 128_MiB; |
| 152 | config.far_code_offset = 400_MiB; | 152 | config.far_code_offset = 100_MiB; |
| 153 | 153 | ||
| 154 | // Safe optimizations | 154 | // Safe optimizations |
| 155 | if (Settings::values.cpu_debug_mode) { | 155 | if (Settings::values.cpu_debug_mode) { |
diff --git a/src/core/arm/dynarmic/arm_dynarmic_64.cpp b/src/core/arm/dynarmic/arm_dynarmic_64.cpp index d96226c41..aa74fce4d 100644 --- a/src/core/arm/dynarmic/arm_dynarmic_64.cpp +++ b/src/core/arm/dynarmic/arm_dynarmic_64.cpp | |||
| @@ -208,8 +208,8 @@ std::shared_ptr<Dynarmic::A64::Jit> ARM_Dynarmic_64::MakeJit(Common::PageTable* | |||
| 208 | config.wall_clock_cntpct = uses_wall_clock; | 208 | config.wall_clock_cntpct = uses_wall_clock; |
| 209 | 209 | ||
| 210 | // Code cache size | 210 | // Code cache size |
| 211 | config.code_cache_size = 512_MiB; | 211 | config.code_cache_size = 128_MiB; |
| 212 | config.far_code_offset = 400_MiB; | 212 | config.far_code_offset = 100_MiB; |
| 213 | 213 | ||
| 214 | // Safe optimizations | 214 | // Safe optimizations |
| 215 | if (Settings::values.cpu_debug_mode) { | 215 | if (Settings::values.cpu_debug_mode) { |
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/hle/ipc_helpers.h b/src/core/hle/ipc_helpers.h index 026257115..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); |
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/k_address_arbiter.cpp b/src/core/hle/kernel/k_address_arbiter.cpp index 1d1f5e5f8..8cdd0490f 100644 --- a/src/core/hle/kernel/k_address_arbiter.cpp +++ b/src/core/hle/kernel/k_address_arbiter.cpp | |||
| @@ -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_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_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 87004a0f9..dd27689b6 100644 --- a/src/core/hle/kernel/k_handle_table.h +++ b/src/core/hle/kernel/k_handle_table.h | |||
| @@ -42,7 +42,7 @@ public: | |||
| 42 | m_free_head_index = -1; | 42 | m_free_head_index = -1; |
| 43 | 43 | ||
| 44 | // Free all entries. | 44 | // Free all entries. |
| 45 | 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) { |
| 46 | m_objects[i] = nullptr; | 46 | m_objects[i] = nullptr; |
| 47 | m_entry_infos[i].next_free_index = i - 1; | 47 | m_entry_infos[i].next_free_index = i - 1; |
| 48 | m_free_head_index = i; | 48 | m_free_head_index = i; |
| @@ -104,17 +104,8 @@ public: | |||
| 104 | ResultCode Reserve(Handle* out_handle); | 104 | ResultCode Reserve(Handle* out_handle); |
| 105 | void Unreserve(Handle handle); | 105 | void Unreserve(Handle handle); |
| 106 | 106 | ||
| 107 | template <typename T> | 107 | ResultCode Add(Handle* out_handle, KAutoObject* obj); |
| 108 | ResultCode Add(Handle* out_handle, T* obj) { | 108 | void Register(Handle handle, KAutoObject* obj); |
| 109 | static_assert(std::is_base_of_v<KAutoObject, T>); | ||
| 110 | return this->Add(out_handle, obj, obj->GetTypeObj().GetClassToken()); | ||
| 111 | } | ||
| 112 | |||
| 113 | template <typename T> | ||
| 114 | void Register(Handle handle, T* obj) { | ||
| 115 | static_assert(std::is_base_of_v<KAutoObject, T>); | ||
| 116 | return this->Register(handle, obj, obj->GetTypeObj().GetClassToken()); | ||
| 117 | } | ||
| 118 | 109 | ||
| 119 | template <typename T> | 110 | template <typename T> |
| 120 | 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 { |
| @@ -160,9 +151,6 @@ public: | |||
| 160 | } | 151 | } |
| 161 | 152 | ||
| 162 | private: | 153 | private: |
| 163 | ResultCode Add(Handle* out_handle, KAutoObject* obj, u16 type); | ||
| 164 | void Register(Handle handle, KAutoObject* obj, u16 type); | ||
| 165 | |||
| 166 | s32 AllocateEntry() { | 154 | s32 AllocateEntry() { |
| 167 | ASSERT(m_count < m_table_size); | 155 | ASSERT(m_count < m_table_size); |
| 168 | 156 | ||
| @@ -179,7 +167,7 @@ private: | |||
| 179 | ASSERT(m_count > 0); | 167 | ASSERT(m_count > 0); |
| 180 | 168 | ||
| 181 | m_objects[index] = nullptr; | 169 | m_objects[index] = nullptr; |
| 182 | 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); |
| 183 | 171 | ||
| 184 | m_free_head_index = index; | 172 | m_free_head_index = index; |
| 185 | 173 | ||
| @@ -278,19 +266,13 @@ private: | |||
| 278 | } | 266 | } |
| 279 | 267 | ||
| 280 | union EntryInfo { | 268 | union EntryInfo { |
| 281 | struct { | 269 | u16 linear_id; |
| 282 | u16 linear_id; | 270 | s16 next_free_index; |
| 283 | u16 type; | ||
| 284 | } info; | ||
| 285 | s32 next_free_index; | ||
| 286 | 271 | ||
| 287 | constexpr u16 GetLinearId() const { | 272 | constexpr u16 GetLinearId() const { |
| 288 | return info.linear_id; | 273 | return linear_id; |
| 289 | } | ||
| 290 | constexpr u16 GetType() const { | ||
| 291 | return info.type; | ||
| 292 | } | 274 | } |
| 293 | constexpr s32 GetNextFreeIndex() const { | 275 | constexpr s16 GetNextFreeIndex() const { |
| 294 | return next_free_index; | 276 | return next_free_index; |
| 295 | } | 277 | } |
| 296 | }; | 278 | }; |
diff --git a/src/core/hle/kernel/k_memory_layout.h b/src/core/hle/kernel/k_memory_layout.h index bcddb0d62..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; |
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_table.cpp b/src/core/hle/kernel/k_page_table.cpp index 0602de1f7..02d93b12e 100644 --- a/src/core/hle/kernel/k_page_table.cpp +++ b/src/core/hle/kernel/k_page_table.cpp | |||
| @@ -424,6 +424,68 @@ ResultCode KPageTable::UnmapCodeMemory(VAddr dst_address, VAddr src_address, std | |||
| 424 | return ResultSuccess; | 424 | return ResultSuccess; |
| 425 | } | 425 | } |
| 426 | 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 | |||
| 427 | ResultCode KPageTable::UnmapProcessMemory(VAddr dst_addr, std::size_t size, | 489 | ResultCode KPageTable::UnmapProcessMemory(VAddr dst_addr, std::size_t size, |
| 428 | KPageTable& src_page_table, VAddr src_addr) { | 490 | KPageTable& src_page_table, VAddr src_addr) { |
| 429 | KScopedLightLock lk(general_lock); | 491 | KScopedLightLock lk(general_lock); |
| @@ -1055,6 +1117,46 @@ ResultCode KPageTable::MapPages(VAddr address, KPageLinkedList& page_linked_list | |||
| 1055 | return ResultSuccess; | 1117 | return ResultSuccess; |
| 1056 | } | 1118 | } |
| 1057 | 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 | |||
| 1058 | ResultCode KPageTable::UnmapPages(VAddr addr, const KPageLinkedList& page_linked_list) { | 1160 | ResultCode KPageTable::UnmapPages(VAddr addr, const KPageLinkedList& page_linked_list) { |
| 1059 | ASSERT(this->IsLockedByCurrentThread()); | 1161 | ASSERT(this->IsLockedByCurrentThread()); |
| 1060 | 1162 | ||
| @@ -1097,6 +1199,30 @@ ResultCode KPageTable::UnmapPages(VAddr addr, KPageLinkedList& page_linked_list, | |||
| 1097 | return ResultSuccess; | 1199 | return ResultSuccess; |
| 1098 | } | 1200 | } |
| 1099 | 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 | |||
| 1100 | ResultCode KPageTable::SetProcessMemoryPermission(VAddr addr, std::size_t size, | 1226 | ResultCode KPageTable::SetProcessMemoryPermission(VAddr addr, std::size_t size, |
| 1101 | Svc::MemoryPermission svc_perm) { | 1227 | Svc::MemoryPermission svc_perm) { |
| 1102 | const size_t num_pages = size / PageSize; | 1228 | const size_t num_pages = size / PageSize; |
diff --git a/src/core/hle/kernel/k_page_table.h b/src/core/hle/kernel/k_page_table.h index e99abe36a..54c6adf8d 100644 --- a/src/core/hle/kernel/k_page_table.h +++ b/src/core/hle/kernel/k_page_table.h | |||
| @@ -46,7 +46,14 @@ public: | |||
| 46 | 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); |
| 47 | ResultCode MapPages(VAddr addr, KPageLinkedList& page_linked_list, KMemoryState state, | 47 | ResultCode MapPages(VAddr addr, KPageLinkedList& page_linked_list, KMemoryState state, |
| 48 | 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 | } | ||
| 49 | 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); | ||
| 50 | ResultCode SetProcessMemoryPermission(VAddr addr, std::size_t size, | 57 | ResultCode SetProcessMemoryPermission(VAddr addr, std::size_t size, |
| 51 | Svc::MemoryPermission svc_perm); | 58 | Svc::MemoryPermission svc_perm); |
| 52 | KMemoryInfo QueryInfo(VAddr addr); | 59 | KMemoryInfo QueryInfo(VAddr addr); |
| @@ -91,6 +98,9 @@ private: | |||
| 91 | ResultCode InitializeMemoryLayout(VAddr start, VAddr end); | 98 | ResultCode InitializeMemoryLayout(VAddr start, VAddr end); |
| 92 | ResultCode MapPages(VAddr addr, const KPageLinkedList& page_linked_list, | 99 | ResultCode MapPages(VAddr addr, const KPageLinkedList& page_linked_list, |
| 93 | 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); | ||
| 94 | ResultCode UnmapPages(VAddr addr, const KPageLinkedList& page_linked_list); | 104 | ResultCode UnmapPages(VAddr addr, const KPageLinkedList& page_linked_list); |
| 95 | bool IsRegionMapped(VAddr address, u64 size); | 105 | bool IsRegionMapped(VAddr address, u64 size); |
| 96 | bool IsRegionContiguous(VAddr addr, u64 size) const; | 106 | bool IsRegionContiguous(VAddr addr, u64 size) const; |
| @@ -105,6 +115,9 @@ private: | |||
| 105 | VAddr GetRegionAddress(KMemoryState state) const; | 115 | VAddr GetRegionAddress(KMemoryState state) const; |
| 106 | std::size_t GetRegionSize(KMemoryState state) const; | 116 | std::size_t GetRegionSize(KMemoryState state) const; |
| 107 | 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); | ||
| 120 | |||
| 108 | ResultCode CheckMemoryStateContiguous(std::size_t* out_blocks_needed, VAddr addr, | 121 | ResultCode CheckMemoryStateContiguous(std::size_t* out_blocks_needed, VAddr addr, |
| 109 | std::size_t size, KMemoryState state_mask, | 122 | std::size_t size, KMemoryState state_mask, |
| 110 | KMemoryState state, KMemoryPermission perm_mask, | 123 | KMemoryState state, KMemoryPermission perm_mask, |
| @@ -137,7 +150,7 @@ private: | |||
| 137 | return CheckMemoryState(nullptr, nullptr, nullptr, out_blocks_needed, addr, size, | 150 | return CheckMemoryState(nullptr, nullptr, nullptr, out_blocks_needed, addr, size, |
| 138 | state_mask, state, perm_mask, perm, attr_mask, attr, ignore_attr); | 151 | state_mask, state, perm_mask, perm, attr_mask, attr, ignore_attr); |
| 139 | } | 152 | } |
| 140 | ResultCode CheckMemoryState(VAddr addr, size_t size, KMemoryState state_mask, | 153 | ResultCode CheckMemoryState(VAddr addr, std::size_t size, KMemoryState state_mask, |
| 141 | KMemoryState state, KMemoryPermission perm_mask, | 154 | KMemoryState state, KMemoryPermission perm_mask, |
| 142 | KMemoryPermission perm, KMemoryAttribute attr_mask, | 155 | KMemoryPermission perm, KMemoryAttribute attr_mask, |
| 143 | KMemoryAttribute attr, | 156 | KMemoryAttribute attr, |
| @@ -210,7 +223,7 @@ public: | |||
| 210 | constexpr VAddr GetAliasCodeRegionSize() const { | 223 | constexpr VAddr GetAliasCodeRegionSize() const { |
| 211 | return alias_code_region_end - alias_code_region_start; | 224 | return alias_code_region_end - alias_code_region_start; |
| 212 | } | 225 | } |
| 213 | size_t GetNormalMemorySize() { | 226 | std::size_t GetNormalMemorySize() { |
| 214 | KScopedLightLock lk(general_lock); | 227 | KScopedLightLock lk(general_lock); |
| 215 | return GetHeapSize() + mapped_physical_memory_size; | 228 | return GetHeapSize() + mapped_physical_memory_size; |
| 216 | } | 229 | } |
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 9233261cd..b39405496 100644 --- a/src/core/hle/kernel/k_process.cpp +++ b/src/core/hle/kernel/k_process.cpp | |||
| @@ -70,58 +70,6 @@ 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, KResourceLimit* res_limit) { | 74 | ProcessType type, KResourceLimit* res_limit) { |
| 127 | auto& kernel = system.Kernel(); | 75 | auto& kernel = system.Kernel(); |
| @@ -404,7 +352,7 @@ ResultCode KProcess::LoadFromMetadata(const FileSys::ProgramMetadata& metadata, | |||
| 404 | } | 352 | } |
| 405 | 353 | ||
| 406 | // Create TLS region | 354 | // Create TLS region |
| 407 | tls_region_address = CreateTLSRegion(); | 355 | R_TRY(this->CreateThreadLocalRegion(std::addressof(tls_region_address))); |
| 408 | memory_reservation.Commit(); | 356 | memory_reservation.Commit(); |
| 409 | 357 | ||
| 410 | return handle_table.Initialize(capabilities.GetHandleTableSize()); | 358 | return handle_table.Initialize(capabilities.GetHandleTableSize()); |
| @@ -444,7 +392,7 @@ void KProcess::PrepareForTermination() { | |||
| 444 | 392 | ||
| 445 | stop_threads(kernel.System().GlobalSchedulerContext().GetThreadList()); | 393 | stop_threads(kernel.System().GlobalSchedulerContext().GetThreadList()); |
| 446 | 394 | ||
| 447 | FreeTLSRegion(tls_region_address); | 395 | this->DeleteThreadLocalRegion(tls_region_address); |
| 448 | tls_region_address = 0; | 396 | tls_region_address = 0; |
| 449 | 397 | ||
| 450 | if (resource_limit) { | 398 | if (resource_limit) { |
| @@ -456,9 +404,6 @@ void KProcess::PrepareForTermination() { | |||
| 456 | } | 404 | } |
| 457 | 405 | ||
| 458 | void KProcess::Finalize() { | 406 | void KProcess::Finalize() { |
| 459 | // Finalize the handle table and close any open handles. | ||
| 460 | handle_table.Finalize(); | ||
| 461 | |||
| 462 | // Free all shared memory infos. | 407 | // Free all shared memory infos. |
| 463 | { | 408 | { |
| 464 | auto it = shared_memory_list.begin(); | 409 | auto it = shared_memory_list.begin(); |
| @@ -483,67 +428,110 @@ void KProcess::Finalize() { | |||
| 483 | resource_limit = nullptr; | 428 | resource_limit = nullptr; |
| 484 | } | 429 | } |
| 485 | 430 | ||
| 431 | // Finalize the page table. | ||
| 432 | page_table.reset(); | ||
| 433 | |||
| 486 | // Perform inherited finalization. | 434 | // Perform inherited finalization. |
| 487 | KAutoObjectWithSlabHeapAndContainer<KProcess, KWorkerTask>::Finalize(); | 435 | KAutoObjectWithSlabHeapAndContainer<KProcess, KWorkerTask>::Finalize(); |
| 488 | } | 436 | } |
| 489 | 437 | ||
| 490 | /** | 438 | ResultCode KProcess::CreateThreadLocalRegion(VAddr* out) { |
| 491 | * Attempts to find a TLS page that contains a free slot for | 439 | KThreadLocalPage* tlp = nullptr; |
| 492 | * use by a thread. | 440 | VAddr tlr = 0; |
| 493 | * | ||
| 494 | * @returns If a page with an available slot is found, then an iterator | ||
| 495 | * pointing to the page is returned. Otherwise the end iterator | ||
| 496 | * is returned instead. | ||
| 497 | */ | ||
| 498 | static auto FindTLSPageWithAvailableSlots(std::vector<TLSPage>& tls_pages) { | ||
| 499 | return std::find_if(tls_pages.begin(), tls_pages.end(), | ||
| 500 | [](const auto& page) { return page.HasAvailableSlots(); }); | ||
| 501 | } | ||
| 502 | 441 | ||
| 503 | VAddr KProcess::CreateTLSRegion() { | 442 | // See if we can get a region from a partially used TLP. |
| 504 | KScopedSchedulerLock lock(kernel); | 443 | { |
| 505 | if (auto tls_page_iter{FindTLSPageWithAvailableSlots(tls_pages)}; | 444 | KScopedSchedulerLock sl{kernel}; |
| 506 | tls_page_iter != tls_pages.cend()) { | ||
| 507 | return *tls_page_iter->ReserveSlot(); | ||
| 508 | } | ||
| 509 | 445 | ||
| 510 | Page* const tls_page_ptr{kernel.GetUserSlabHeapPages().Allocate()}; | 446 | if (auto it = partially_used_tlp_tree.begin(); it != partially_used_tlp_tree.end()) { |
| 511 | ASSERT(tls_page_ptr); | 447 | tlr = it->Reserve(); |
| 448 | ASSERT(tlr != 0); | ||
| 512 | 449 | ||
| 513 | const VAddr start{page_table->GetKernelMapRegionStart()}; | 450 | if (it->IsAllUsed()) { |
| 514 | const VAddr size{page_table->GetKernelMapRegionEnd() - start}; | 451 | tlp = std::addressof(*it); |
| 515 | const PAddr tls_map_addr{kernel.System().DeviceMemory().GetPhysicalAddr(tls_page_ptr)}; | 452 | partially_used_tlp_tree.erase(it); |
| 516 | const VAddr tls_page_addr{page_table | 453 | fully_used_tlp_tree.insert(*tlp); |
| 517 | ->AllocateAndMapMemory(1, PageSize, true, start, size / PageSize, | 454 | } |
| 518 | KMemoryState::ThreadLocal, | ||
| 519 | KMemoryPermission::UserReadWrite, | ||
| 520 | tls_map_addr) | ||
| 521 | .ValueOr(0)}; | ||
| 522 | 455 | ||
| 523 | ASSERT(tls_page_addr); | 456 | *out = tlr; |
| 457 | return ResultSuccess; | ||
| 458 | } | ||
| 459 | } | ||
| 524 | 460 | ||
| 525 | std::memset(tls_page_ptr, 0, PageSize); | 461 | // Allocate a new page. |
| 526 | tls_pages.emplace_back(tls_page_addr); | 462 | tlp = KThreadLocalPage::Allocate(kernel); |
| 463 | R_UNLESS(tlp != nullptr, ResultOutOfMemory); | ||
| 464 | auto tlp_guard = SCOPE_GUARD({ KThreadLocalPage::Free(kernel, tlp); }); | ||
| 527 | 465 | ||
| 528 | const auto reserve_result{tls_pages.back().ReserveSlot()}; | 466 | // Initialize the new page. |
| 529 | ASSERT(reserve_result.has_value()); | 467 | R_TRY(tlp->Initialize(kernel, this)); |
| 468 | |||
| 469 | // Reserve a TLR. | ||
| 470 | tlr = tlp->Reserve(); | ||
| 471 | ASSERT(tlr != 0); | ||
| 472 | |||
| 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 | } | ||
| 530 | 482 | ||
| 531 | return *reserve_result; | 483 | // We succeeded! |
| 484 | tlp_guard.Cancel(); | ||
| 485 | *out = tlr; | ||
| 486 | return ResultSuccess; | ||
| 532 | } | 487 | } |
| 533 | 488 | ||
| 534 | void KProcess::FreeTLSRegion(VAddr tls_address) { | 489 | ResultCode KProcess::DeleteThreadLocalRegion(VAddr addr) { |
| 535 | KScopedSchedulerLock lock(kernel); | 490 | KThreadLocalPage* page_to_free = nullptr; |
| 536 | const VAddr aligned_address = Common::AlignDown(tls_address, Core::Memory::PAGE_SIZE); | 491 | |
| 537 | auto iter = | 492 | // Release the region. |
| 538 | std::find_if(tls_pages.begin(), tls_pages.end(), [aligned_address](const auto& page) { | 493 | { |
| 539 | return page.GetBaseAddress() == aligned_address; | 494 | KScopedSchedulerLock sl{kernel}; |
| 540 | }); | 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(); | ||
| 541 | 530 | ||
| 542 | // Something has gone very wrong if we're freeing a region | 531 | KThreadLocalPage::Free(kernel, page_to_free); |
| 543 | // with no actual page available. | 532 | } |
| 544 | ASSERT(iter != tls_pages.cend()); | ||
| 545 | 533 | ||
| 546 | iter->ReleaseSlot(tls_address); | 534 | return ResultSuccess; |
| 547 | } | 535 | } |
| 548 | 536 | ||
| 549 | 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 cf1b67428..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" |
| @@ -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_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 05c0bec9c..5690cc757 100644 --- a/src/core/hle/kernel/k_slab_heap.h +++ b/src/core/hle/kernel/k_slab_heap.h | |||
| @@ -16,39 +16,34 @@ class KernelCore; | |||
| 16 | 16 | ||
| 17 | namespace impl { | 17 | namespace impl { |
| 18 | 18 | ||
| 19 | class KSlabHeapImpl final { | 19 | class KSlabHeapImpl { |
| 20 | public: | ||
| 21 | YUZU_NON_COPYABLE(KSlabHeapImpl); | 20 | YUZU_NON_COPYABLE(KSlabHeapImpl); |
| 22 | YUZU_NON_MOVEABLE(KSlabHeapImpl); | 21 | YUZU_NON_MOVEABLE(KSlabHeapImpl); |
| 23 | 22 | ||
| 23 | public: | ||
| 24 | struct Node { | 24 | struct Node { |
| 25 | Node* next{}; | 25 | Node* next{}; |
| 26 | }; | 26 | }; |
| 27 | 27 | ||
| 28 | public: | ||
| 28 | constexpr KSlabHeapImpl() = default; | 29 | constexpr KSlabHeapImpl() = default; |
| 29 | constexpr ~KSlabHeapImpl() = default; | ||
| 30 | 30 | ||
| 31 | void Initialize(std::size_t size) { | 31 | void Initialize() { |
| 32 | ASSERT(head == nullptr); | 32 | ASSERT(m_head == nullptr); |
| 33 | obj_size = size; | ||
| 34 | } | ||
| 35 | |||
| 36 | constexpr std::size_t GetObjectSize() const { | ||
| 37 | return obj_size; | ||
| 38 | } | 33 | } |
| 39 | 34 | ||
| 40 | Node* GetHead() const { | 35 | Node* GetHead() const { |
| 41 | return head; | 36 | return m_head; |
| 42 | } | 37 | } |
| 43 | 38 | ||
| 44 | void* Allocate() { | 39 | void* Allocate() { |
| 45 | Node* ret = head.load(); | 40 | Node* ret = m_head.load(); |
| 46 | 41 | ||
| 47 | do { | 42 | do { |
| 48 | if (ret == nullptr) { | 43 | if (ret == nullptr) { |
| 49 | break; | 44 | break; |
| 50 | } | 45 | } |
| 51 | } while (!head.compare_exchange_weak(ret, ret->next)); | 46 | } while (!m_head.compare_exchange_weak(ret, ret->next)); |
| 52 | 47 | ||
| 53 | return ret; | 48 | return ret; |
| 54 | } | 49 | } |
| @@ -56,170 +51,157 @@ public: | |||
| 56 | void Free(void* obj) { | 51 | void Free(void* obj) { |
| 57 | Node* node = static_cast<Node*>(obj); | 52 | Node* node = static_cast<Node*>(obj); |
| 58 | 53 | ||
| 59 | Node* cur_head = head.load(); | 54 | Node* cur_head = m_head.load(); |
| 60 | do { | 55 | do { |
| 61 | node->next = cur_head; | 56 | node->next = cur_head; |
| 62 | } while (!head.compare_exchange_weak(cur_head, node)); | 57 | } while (!m_head.compare_exchange_weak(cur_head, node)); |
| 63 | } | 58 | } |
| 64 | 59 | ||
| 65 | private: | 60 | private: |
| 66 | std::atomic<Node*> head{}; | 61 | std::atomic<Node*> m_head{}; |
| 67 | std::size_t obj_size{}; | ||
| 68 | }; | 62 | }; |
| 69 | 63 | ||
| 70 | } // namespace impl | 64 | } // namespace impl |
| 71 | 65 | ||
| 72 | class KSlabHeapBase { | 66 | template <bool SupportDynamicExpansion> |
| 73 | public: | 67 | class KSlabHeapBase : protected impl::KSlabHeapImpl { |
| 74 | YUZU_NON_COPYABLE(KSlabHeapBase); | 68 | YUZU_NON_COPYABLE(KSlabHeapBase); |
| 75 | YUZU_NON_MOVEABLE(KSlabHeapBase); | 69 | YUZU_NON_MOVEABLE(KSlabHeapBase); |
| 76 | 70 | ||
| 77 | constexpr KSlabHeapBase() = default; | 71 | private: |
| 78 | constexpr ~KSlabHeapBase() = default; | 72 | size_t m_obj_size{}; |
| 73 | uintptr_t m_peak{}; | ||
| 74 | uintptr_t m_start{}; | ||
| 75 | uintptr_t m_end{}; | ||
| 79 | 76 | ||
| 80 | constexpr bool Contains(uintptr_t addr) const { | 77 | private: |
| 81 | return start <= addr && addr < end; | 78 | void UpdatePeakImpl(uintptr_t obj) { |
| 82 | } | 79 | static_assert(std::atomic_ref<uintptr_t>::is_always_lock_free); |
| 80 | std::atomic_ref<uintptr_t> peak_ref(m_peak); | ||
| 83 | 81 | ||
| 84 | constexpr std::size_t GetSlabHeapSize() const { | 82 | const uintptr_t alloc_peak = obj + this->GetObjectSize(); |
| 85 | return (end - start) / 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)); | ||
| 86 | } | 89 | } |
| 87 | 90 | ||
| 88 | constexpr std::size_t GetObjectSize() const { | 91 | public: |
| 89 | return impl.GetObjectSize(); | 92 | constexpr KSlabHeapBase() = default; |
| 90 | } | ||
| 91 | 93 | ||
| 92 | constexpr uintptr_t GetSlabHeapAddress() const { | 94 | bool Contains(uintptr_t address) const { |
| 93 | return start; | 95 | return m_start <= address && address < m_end; |
| 94 | } | 96 | } |
| 95 | 97 | ||
| 96 | std::size_t GetObjectIndexImpl(const void* obj) const { | 98 | void Initialize(size_t obj_size, void* memory, size_t memory_size) { |
| 97 | return (reinterpret_cast<uintptr_t>(obj) - start) / GetObjectSize(); | 99 | // Ensure we don't initialize a slab using null memory. |
| 100 | ASSERT(memory != nullptr); | ||
| 101 | |||
| 102 | // Set our object size. | ||
| 103 | m_obj_size = obj_size; | ||
| 104 | |||
| 105 | // Initialize the base allocator. | ||
| 106 | KSlabHeapImpl::Initialize(); | ||
| 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; | ||
| 113 | |||
| 114 | // Free the objects. | ||
| 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 | } | ||
| 98 | } | 121 | } |
| 99 | 122 | ||
| 100 | std::size_t GetPeakIndex() const { | 123 | size_t GetSlabHeapSize() const { |
| 101 | return GetObjectIndexImpl(reinterpret_cast<const void*>(peak)); | 124 | return (m_end - m_start) / this->GetObjectSize(); |
| 102 | } | 125 | } |
| 103 | 126 | ||
| 104 | void* AllocateImpl() { | 127 | size_t GetObjectSize() const { |
| 105 | return impl.Allocate(); | 128 | return m_obj_size; |
| 106 | } | 129 | } |
| 107 | 130 | ||
| 108 | void FreeImpl(void* obj) { | 131 | void* Allocate() { |
| 109 | // Don't allow freeing an object that wasn't allocated from this heap | 132 | void* obj = KSlabHeapImpl::Allocate(); |
| 110 | ASSERT(Contains(reinterpret_cast<uintptr_t>(obj))); | ||
| 111 | 133 | ||
| 112 | impl.Free(obj); | 134 | return obj; |
| 113 | } | 135 | } |
| 114 | 136 | ||
| 115 | void InitializeImpl(std::size_t obj_size, void* memory, std::size_t memory_size) { | 137 | void Free(void* obj) { |
| 116 | // Ensure we don't initialize a slab using null memory | 138 | // Don't allow freeing an object that wasn't allocated from this heap. |
| 117 | ASSERT(memory != nullptr); | 139 | const bool contained = this->Contains(reinterpret_cast<uintptr_t>(obj)); |
| 118 | 140 | ASSERT(contained); | |
| 119 | // Initialize the base allocator | 141 | KSlabHeapImpl::Free(obj); |
| 120 | impl.Initialize(obj_size); | 142 | } |
| 121 | 143 | ||
| 122 | // Set our tracking variables | 144 | size_t GetObjectIndex(const void* obj) const { |
| 123 | const std::size_t num_obj = (memory_size / obj_size); | 145 | if constexpr (SupportDynamicExpansion) { |
| 124 | start = reinterpret_cast<uintptr_t>(memory); | 146 | if (!this->Contains(reinterpret_cast<uintptr_t>(obj))) { |
| 125 | end = start + num_obj * obj_size; | 147 | return std::numeric_limits<size_t>::max(); |
| 126 | peak = start; | 148 | } |
| 149 | } | ||
| 127 | 150 | ||
| 128 | // Free the objects | 151 | return (reinterpret_cast<uintptr_t>(obj) - m_start) / this->GetObjectSize(); |
| 129 | u8* cur = reinterpret_cast<u8*>(end); | 152 | } |
| 130 | 153 | ||
| 131 | for (std::size_t i{}; i < num_obj; i++) { | 154 | size_t GetPeakIndex() const { |
| 132 | cur -= obj_size; | 155 | return this->GetObjectIndex(reinterpret_cast<const void*>(m_peak)); |
| 133 | impl.Free(cur); | ||
| 134 | } | ||
| 135 | } | 156 | } |
| 136 | 157 | ||
| 137 | private: | 158 | uintptr_t GetSlabHeapAddress() const { |
| 138 | using Impl = impl::KSlabHeapImpl; | 159 | return m_start; |
| 160 | } | ||
| 139 | 161 | ||
| 140 | Impl impl; | 162 | size_t GetNumRemaining() const { |
| 141 | uintptr_t peak{}; | 163 | // Only calculate the number of remaining objects under debug configuration. |
| 142 | uintptr_t start{}; | 164 | return 0; |
| 143 | uintptr_t end{}; | 165 | } |
| 144 | }; | 166 | }; |
| 145 | 167 | ||
| 146 | template <typename T> | 168 | template <typename T> |
| 147 | class KSlabHeap final : public KSlabHeapBase { | 169 | class KSlabHeap final : public KSlabHeapBase<false> { |
| 148 | public: | 170 | private: |
| 149 | enum class AllocationType { | 171 | using BaseHeap = KSlabHeapBase<false>; |
| 150 | Host, | ||
| 151 | Guest, | ||
| 152 | }; | ||
| 153 | 172 | ||
| 154 | explicit constexpr KSlabHeap(AllocationType allocation_type_ = AllocationType::Host) | 173 | public: |
| 155 | : KSlabHeapBase(), allocation_type{allocation_type_} {} | 174 | constexpr KSlabHeap() = default; |
| 156 | 175 | ||
| 157 | void Initialize(void* memory, std::size_t memory_size) { | 176 | void Initialize(void* memory, size_t memory_size) { |
| 158 | if (allocation_type == AllocationType::Guest) { | 177 | BaseHeap::Initialize(sizeof(T), memory, memory_size); |
| 159 | InitializeImpl(sizeof(T), memory, memory_size); | ||
| 160 | } | ||
| 161 | } | 178 | } |
| 162 | 179 | ||
| 163 | T* Allocate() { | 180 | T* Allocate() { |
| 164 | switch (allocation_type) { | 181 | T* obj = static_cast<T*>(BaseHeap::Allocate()); |
| 165 | case AllocationType::Host: | ||
| 166 | // Fallback for cases where we do not yet support allocating guest memory from the slab | ||
| 167 | // heap, such as for kernel memory regions. | ||
| 168 | return new T; | ||
| 169 | |||
| 170 | case AllocationType::Guest: | ||
| 171 | T* obj = static_cast<T*>(AllocateImpl()); | ||
| 172 | if (obj != nullptr) { | ||
| 173 | new (obj) T(); | ||
| 174 | } | ||
| 175 | return obj; | ||
| 176 | } | ||
| 177 | 182 | ||
| 178 | UNREACHABLE_MSG("Invalid AllocationType {}", allocation_type); | 183 | if (obj != nullptr) [[likely]] { |
| 179 | return nullptr; | 184 | std::construct_at(obj); |
| 185 | } | ||
| 186 | return obj; | ||
| 180 | } | 187 | } |
| 181 | 188 | ||
| 182 | T* AllocateWithKernel(KernelCore& kernel) { | 189 | T* Allocate(KernelCore& kernel) { |
| 183 | switch (allocation_type) { | 190 | T* obj = static_cast<T*>(BaseHeap::Allocate()); |
| 184 | case AllocationType::Host: | ||
| 185 | // Fallback for cases where we do not yet support allocating guest memory from the slab | ||
| 186 | // heap, such as for kernel memory regions. | ||
| 187 | return new T(kernel); | ||
| 188 | 191 | ||
| 189 | case AllocationType::Guest: | 192 | if (obj != nullptr) [[likely]] { |
| 190 | T* obj = static_cast<T*>(AllocateImpl()); | 193 | std::construct_at(obj, kernel); |
| 191 | if (obj != nullptr) { | ||
| 192 | new (obj) T(kernel); | ||
| 193 | } | ||
| 194 | return obj; | ||
| 195 | } | 194 | } |
| 196 | 195 | return obj; | |
| 197 | UNREACHABLE_MSG("Invalid AllocationType {}", allocation_type); | ||
| 198 | return nullptr; | ||
| 199 | } | 196 | } |
| 200 | 197 | ||
| 201 | void Free(T* obj) { | 198 | void Free(T* obj) { |
| 202 | switch (allocation_type) { | 199 | BaseHeap::Free(obj); |
| 203 | case AllocationType::Host: | ||
| 204 | // Fallback for cases where we do not yet support allocating guest memory from the slab | ||
| 205 | // heap, such as for kernel memory regions. | ||
| 206 | delete obj; | ||
| 207 | return; | ||
| 208 | |||
| 209 | case AllocationType::Guest: | ||
| 210 | FreeImpl(obj); | ||
| 211 | return; | ||
| 212 | } | ||
| 213 | |||
| 214 | UNREACHABLE_MSG("Invalid AllocationType {}", allocation_type); | ||
| 215 | } | 200 | } |
| 216 | 201 | ||
| 217 | constexpr std::size_t GetObjectIndex(const T* obj) const { | 202 | size_t GetObjectIndex(const T* obj) const { |
| 218 | return GetObjectIndexImpl(obj); | 203 | return BaseHeap::GetObjectIndex(obj); |
| 219 | } | 204 | } |
| 220 | |||
| 221 | private: | ||
| 222 | const AllocationType allocation_type; | ||
| 223 | }; | 205 | }; |
| 224 | 206 | ||
| 225 | } // 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 71bd466cf..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); |
| @@ -76,7 +77,7 @@ struct KernelCore::Impl { | |||
| 76 | // Initialize kernel memory and resources. | 77 | // Initialize kernel memory and resources. |
| 77 | InitializeSystemResourceLimit(kernel, system.CoreTiming()); | 78 | InitializeSystemResourceLimit(kernel, system.CoreTiming()); |
| 78 | InitializeMemoryLayout(); | 79 | InitializeMemoryLayout(); |
| 79 | InitializePageSlab(); | 80 | Init::InitializeKPageBufferSlabHeap(system); |
| 80 | InitializeSchedulers(); | 81 | InitializeSchedulers(); |
| 81 | InitializeSuspendThreads(); | 82 | InitializeSuspendThreads(); |
| 82 | InitializePreemption(kernel); | 83 | InitializePreemption(kernel); |
| @@ -107,19 +108,6 @@ struct KernelCore::Impl { | |||
| 107 | for (auto* server_port : server_ports_) { | 108 | for (auto* server_port : server_ports_) { |
| 108 | server_port->Close(); | 109 | server_port->Close(); |
| 109 | } | 110 | } |
| 110 | // Close all open server sessions. | ||
| 111 | std::unordered_set<KServerSession*> server_sessions_; | ||
| 112 | { | ||
| 113 | std::lock_guard lk(server_sessions_lock); | ||
| 114 | server_sessions_ = server_sessions; | ||
| 115 | server_sessions.clear(); | ||
| 116 | } | ||
| 117 | for (auto* server_session : server_sessions_) { | ||
| 118 | server_session->Close(); | ||
| 119 | } | ||
| 120 | |||
| 121 | // Ensure that the object list container is finalized and properly shutdown. | ||
| 122 | object_list_container.Finalize(); | ||
| 123 | 111 | ||
| 124 | // Ensures all service threads gracefully shutdown. | 112 | // Ensures all service threads gracefully shutdown. |
| 125 | ClearServiceThreads(); | 113 | ClearServiceThreads(); |
| @@ -194,11 +182,15 @@ struct KernelCore::Impl { | |||
| 194 | { | 182 | { |
| 195 | std::lock_guard lk(registered_objects_lock); | 183 | std::lock_guard lk(registered_objects_lock); |
| 196 | if (registered_objects.size()) { | 184 | if (registered_objects.size()) { |
| 197 | LOG_WARNING(Kernel, "{} kernel objects were dangling on shutdown!", | 185 | LOG_DEBUG(Kernel, "{} kernel objects were dangling on shutdown!", |
| 198 | registered_objects.size()); | 186 | registered_objects.size()); |
| 199 | registered_objects.clear(); | 187 | registered_objects.clear(); |
| 200 | } | 188 | } |
| 201 | } | 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(); | ||
| 202 | } | 194 | } |
| 203 | 195 | ||
| 204 | void InitializePhysicalCores() { | 196 | void InitializePhysicalCores() { |
| @@ -291,15 +283,16 @@ struct KernelCore::Impl { | |||
| 291 | 283 | ||
| 292 | // 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 |
| 293 | KThread* GetHostDummyThread() { | 285 | KThread* GetHostDummyThread() { |
| 294 | auto make_thread = [this]() { | 286 | auto initialize = [this](KThread* thread) { |
| 295 | KThread* thread = KThread::Create(system.Kernel()); | ||
| 296 | ASSERT(KThread::InitializeDummyThread(thread).IsSuccess()); | 287 | ASSERT(KThread::InitializeDummyThread(thread).IsSuccess()); |
| 297 | thread->SetName(fmt::format("DummyThread:{}", GetHostThreadId())); | 288 | thread->SetName(fmt::format("DummyThread:{}", GetHostThreadId())); |
| 298 | return thread; | 289 | return thread; |
| 299 | }; | 290 | }; |
| 300 | 291 | ||
| 301 | thread_local KThread* saved_thread = make_thread(); | 292 | thread_local auto raw_thread = KThread(system.Kernel()); |
| 302 | return saved_thread; | 293 | thread_local auto thread = initialize(&raw_thread); |
| 294 | |||
| 295 | return thread; | ||
| 303 | } | 296 | } |
| 304 | 297 | ||
| 305 | /// 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 |
| @@ -660,22 +653,6 @@ struct KernelCore::Impl { | |||
| 660 | time_phys_addr, time_size, "Time:SharedMemory"); | 653 | time_phys_addr, time_size, "Time:SharedMemory"); |
| 661 | } | 654 | } |
| 662 | 655 | ||
| 663 | void InitializePageSlab() { | ||
| 664 | // Allocate slab heaps | ||
| 665 | user_slab_heap_pages = | ||
| 666 | std::make_unique<KSlabHeap<Page>>(KSlabHeap<Page>::AllocationType::Guest); | ||
| 667 | |||
| 668 | // TODO(ameerj): This should be derived, not hardcoded within the kernel | ||
| 669 | constexpr u64 user_slab_heap_size{0x3de000}; | ||
| 670 | // Reserve slab heaps | ||
| 671 | ASSERT( | ||
| 672 | system_resource_limit->Reserve(LimitableResource::PhysicalMemory, user_slab_heap_size)); | ||
| 673 | // Initialize slab heap | ||
| 674 | user_slab_heap_pages->Initialize( | ||
| 675 | system.DeviceMemory().GetPointer(Core::DramMemoryMap::SlabHeapBase), | ||
| 676 | user_slab_heap_size); | ||
| 677 | } | ||
| 678 | |||
| 679 | KClientPort* CreateNamedServicePort(std::string name) { | 656 | KClientPort* CreateNamedServicePort(std::string name) { |
| 680 | auto search = service_interface_factory.find(name); | 657 | auto search = service_interface_factory.find(name); |
| 681 | if (search == service_interface_factory.end()) { | 658 | if (search == service_interface_factory.end()) { |
| @@ -713,7 +690,6 @@ struct KernelCore::Impl { | |||
| 713 | } | 690 | } |
| 714 | 691 | ||
| 715 | std::mutex server_ports_lock; | 692 | std::mutex server_ports_lock; |
| 716 | std::mutex server_sessions_lock; | ||
| 717 | std::mutex registered_objects_lock; | 693 | std::mutex registered_objects_lock; |
| 718 | std::mutex registered_in_use_objects_lock; | 694 | std::mutex registered_in_use_objects_lock; |
| 719 | 695 | ||
| @@ -737,14 +713,13 @@ struct KernelCore::Impl { | |||
| 737 | // stores all the objects in place. | 713 | // stores all the objects in place. |
| 738 | std::unique_ptr<KHandleTable> global_handle_table; | 714 | std::unique_ptr<KHandleTable> global_handle_table; |
| 739 | 715 | ||
| 740 | KAutoObjectWithListContainer object_list_container; | 716 | std::unique_ptr<KAutoObjectWithListContainer> global_object_list_container; |
| 741 | 717 | ||
| 742 | /// 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 |
| 743 | /// the ConnectToPort SVC. | 719 | /// the ConnectToPort SVC. |
| 744 | std::unordered_map<std::string, ServiceInterfaceFactory> service_interface_factory; | 720 | std::unordered_map<std::string, ServiceInterfaceFactory> service_interface_factory; |
| 745 | NamedPortTable named_ports; | 721 | NamedPortTable named_ports; |
| 746 | std::unordered_set<KServerPort*> server_ports; | 722 | std::unordered_set<KServerPort*> server_ports; |
| 747 | std::unordered_set<KServerSession*> server_sessions; | ||
| 748 | std::unordered_set<KAutoObject*> registered_objects; | 723 | std::unordered_set<KAutoObject*> registered_objects; |
| 749 | std::unordered_set<KAutoObject*> registered_in_use_objects; | 724 | std::unordered_set<KAutoObject*> registered_in_use_objects; |
| 750 | 725 | ||
| @@ -756,7 +731,6 @@ struct KernelCore::Impl { | |||
| 756 | 731 | ||
| 757 | // Kernel memory management | 732 | // Kernel memory management |
| 758 | std::unique_ptr<KMemoryManager> memory_manager; | 733 | std::unique_ptr<KMemoryManager> memory_manager; |
| 759 | std::unique_ptr<KSlabHeap<Page>> user_slab_heap_pages; | ||
| 760 | 734 | ||
| 761 | // Shared memory for services | 735 | // Shared memory for services |
| 762 | Kernel::KSharedMemory* hid_shared_mem{}; | 736 | Kernel::KSharedMemory* hid_shared_mem{}; |
| @@ -915,11 +889,11 @@ const Core::ExclusiveMonitor& KernelCore::GetExclusiveMonitor() const { | |||
| 915 | } | 889 | } |
| 916 | 890 | ||
| 917 | KAutoObjectWithListContainer& KernelCore::ObjectListContainer() { | 891 | KAutoObjectWithListContainer& KernelCore::ObjectListContainer() { |
| 918 | return impl->object_list_container; | 892 | return *impl->global_object_list_container; |
| 919 | } | 893 | } |
| 920 | 894 | ||
| 921 | const KAutoObjectWithListContainer& KernelCore::ObjectListContainer() const { | 895 | const KAutoObjectWithListContainer& KernelCore::ObjectListContainer() const { |
| 922 | return impl->object_list_container; | 896 | return *impl->global_object_list_container; |
| 923 | } | 897 | } |
| 924 | 898 | ||
| 925 | void KernelCore::InvalidateAllInstructionCaches() { | 899 | void KernelCore::InvalidateAllInstructionCaches() { |
| @@ -949,16 +923,6 @@ KClientPort* KernelCore::CreateNamedServicePort(std::string name) { | |||
| 949 | return impl->CreateNamedServicePort(std::move(name)); | 923 | return impl->CreateNamedServicePort(std::move(name)); |
| 950 | } | 924 | } |
| 951 | 925 | ||
| 952 | void KernelCore::RegisterServerSession(KServerSession* server_session) { | ||
| 953 | std::lock_guard lk(impl->server_sessions_lock); | ||
| 954 | impl->server_sessions.insert(server_session); | ||
| 955 | } | ||
| 956 | |||
| 957 | void KernelCore::UnregisterServerSession(KServerSession* server_session) { | ||
| 958 | std::lock_guard lk(impl->server_sessions_lock); | ||
| 959 | impl->server_sessions.erase(server_session); | ||
| 960 | } | ||
| 961 | |||
| 962 | void KernelCore::RegisterKernelObject(KAutoObject* object) { | 926 | void KernelCore::RegisterKernelObject(KAutoObject* object) { |
| 963 | std::lock_guard lk(impl->registered_objects_lock); | 927 | std::lock_guard lk(impl->registered_objects_lock); |
| 964 | impl->registered_objects.insert(object); | 928 | impl->registered_objects.insert(object); |
| @@ -1031,14 +995,6 @@ const KMemoryManager& KernelCore::MemoryManager() const { | |||
| 1031 | return *impl->memory_manager; | 995 | return *impl->memory_manager; |
| 1032 | } | 996 | } |
| 1033 | 997 | ||
| 1034 | KSlabHeap<Page>& KernelCore::GetUserSlabHeapPages() { | ||
| 1035 | return *impl->user_slab_heap_pages; | ||
| 1036 | } | ||
| 1037 | |||
| 1038 | const KSlabHeap<Page>& KernelCore::GetUserSlabHeapPages() const { | ||
| 1039 | return *impl->user_slab_heap_pages; | ||
| 1040 | } | ||
| 1041 | |||
| 1042 | Kernel::KSharedMemory& KernelCore::GetHidSharedMem() { | 998 | Kernel::KSharedMemory& KernelCore::GetHidSharedMem() { |
| 1043 | return *impl->hid_shared_mem; | 999 | return *impl->hid_shared_mem; |
| 1044 | } | 1000 | } |
diff --git a/src/core/hle/kernel/kernel.h b/src/core/hle/kernel/kernel.h index c1254b18d..7087bbda6 100644 --- a/src/core/hle/kernel/kernel.h +++ b/src/core/hle/kernel/kernel.h | |||
| @@ -43,6 +43,7 @@ class KHandleTable; | |||
| 43 | class KLinkedListNode; | 43 | class KLinkedListNode; |
| 44 | class KMemoryLayout; | 44 | class KMemoryLayout; |
| 45 | class KMemoryManager; | 45 | class KMemoryManager; |
| 46 | class KPageBuffer; | ||
| 46 | class KPort; | 47 | class KPort; |
| 47 | class KProcess; | 48 | class KProcess; |
| 48 | class KResourceLimit; | 49 | class KResourceLimit; |
| @@ -52,6 +53,7 @@ class KSession; | |||
| 52 | class KSharedMemory; | 53 | class KSharedMemory; |
| 53 | class KSharedMemoryInfo; | 54 | class KSharedMemoryInfo; |
| 54 | class KThread; | 55 | class KThread; |
| 56 | class KThreadLocalPage; | ||
| 55 | class KTransferMemory; | 57 | class KTransferMemory; |
| 56 | class KWorkerTaskManager; | 58 | class KWorkerTaskManager; |
| 57 | class KWritableEvent; | 59 | class KWritableEvent; |
| @@ -194,14 +196,6 @@ public: | |||
| 194 | /// Opens a port to a service previously registered with RegisterNamedService. | 196 | /// Opens a port to a service previously registered with RegisterNamedService. |
| 195 | KClientPort* CreateNamedServicePort(std::string name); | 197 | KClientPort* CreateNamedServicePort(std::string name); |
| 196 | 198 | ||
| 197 | /// Registers a server session with the gobal emulation state, to be freed on shutdown. This is | ||
| 198 | /// necessary because we do not emulate processes for HLE sessions. | ||
| 199 | void RegisterServerSession(KServerSession* server_session); | ||
| 200 | |||
| 201 | /// Unregisters a server session previously registered with RegisterServerSession when it was | ||
| 202 | /// destroyed during the current emulation session. | ||
| 203 | void UnregisterServerSession(KServerSession* server_session); | ||
| 204 | |||
| 205 | /// 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 |
| 206 | /// leaks after emulation has been shutdown. | 200 | /// leaks after emulation has been shutdown. |
| 207 | void RegisterKernelObject(KAutoObject* object); | 201 | void RegisterKernelObject(KAutoObject* object); |
| @@ -239,12 +233,6 @@ public: | |||
| 239 | /// Gets the virtual memory manager for the kernel. | 233 | /// Gets the virtual memory manager for the kernel. |
| 240 | const KMemoryManager& MemoryManager() const; | 234 | const KMemoryManager& MemoryManager() const; |
| 241 | 235 | ||
| 242 | /// Gets the slab heap allocated for user space pages. | ||
| 243 | KSlabHeap<Page>& GetUserSlabHeapPages(); | ||
| 244 | |||
| 245 | /// Gets the slab heap allocated for user space pages. | ||
| 246 | const KSlabHeap<Page>& GetUserSlabHeapPages() const; | ||
| 247 | |||
| 248 | /// Gets the shared memory object for HID services. | 236 | /// Gets the shared memory object for HID services. |
| 249 | Kernel::KSharedMemory& GetHidSharedMem(); | 237 | Kernel::KSharedMemory& GetHidSharedMem(); |
| 250 | 238 | ||
| @@ -336,6 +324,10 @@ public: | |||
| 336 | return slab_heap_container->writeable_event; | 324 | return slab_heap_container->writeable_event; |
| 337 | } else if constexpr (std::is_same_v<T, KCodeMemory>) { | 325 | } else if constexpr (std::is_same_v<T, KCodeMemory>) { |
| 338 | 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; | ||
| 339 | } | 331 | } |
| 340 | } | 332 | } |
| 341 | 333 | ||
| @@ -397,6 +389,8 @@ private: | |||
| 397 | KSlabHeap<KTransferMemory> transfer_memory; | 389 | KSlabHeap<KTransferMemory> transfer_memory; |
| 398 | KSlabHeap<KWritableEvent> writeable_event; | 390 | KSlabHeap<KWritableEvent> writeable_event; |
| 399 | KSlabHeap<KCodeMemory> code_memory; | 391 | KSlabHeap<KCodeMemory> code_memory; |
| 392 | KSlabHeap<KPageBuffer> page_buffer; | ||
| 393 | KSlabHeap<KThreadLocalPage> thread_local_page; | ||
| 400 | }; | 394 | }; |
| 401 | 395 | ||
| 402 | 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_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/service/am/am.cpp b/src/core/hle/service/am/am.cpp index 2f8e21568..420de3c54 100644 --- a/src/core/hle/service/am/am.cpp +++ b/src/core/hle/service/am/am.cpp | |||
| @@ -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(); |
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/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/kernel_helpers.cpp b/src/core/hle/service/kernel_helpers.cpp index b8c2c6e51..ff0bbb788 100644 --- a/src/core/hle/service/kernel_helpers.cpp +++ b/src/core/hle/service/kernel_helpers.cpp | |||
| @@ -17,21 +17,12 @@ namespace Service::KernelHelpers { | |||
| 17 | 17 | ||
| 18 | ServiceContext::ServiceContext(Core::System& system_, std::string name_) | 18 | ServiceContext::ServiceContext(Core::System& system_, std::string name_) |
| 19 | : kernel(system_.Kernel()) { | 19 | : kernel(system_.Kernel()) { |
| 20 | |||
| 21 | // Create a resource limit for the process. | ||
| 22 | const auto physical_memory_size = | ||
| 23 | kernel.MemoryManager().GetSize(Kernel::KMemoryManager::Pool::System); | ||
| 24 | auto* resource_limit = Kernel::CreateResourceLimitForProcess(system_, physical_memory_size); | ||
| 25 | |||
| 26 | // Create the process. | 20 | // Create the process. |
| 27 | process = Kernel::KProcess::Create(kernel); | 21 | process = Kernel::KProcess::Create(kernel); |
| 28 | ASSERT(Kernel::KProcess::Initialize(process, system_, std::move(name_), | 22 | ASSERT(Kernel::KProcess::Initialize(process, system_, std::move(name_), |
| 29 | Kernel::KProcess::ProcessType::KernelInternal, | 23 | Kernel::KProcess::ProcessType::KernelInternal, |
| 30 | resource_limit) | 24 | kernel.GetSystemResourceLimit()) |
| 31 | .IsSuccess()); | 25 | .IsSuccess()); |
| 32 | |||
| 33 | // Close reference to our resource limit, as the process opens one. | ||
| 34 | resource_limit->Close(); | ||
| 35 | } | 26 | } |
| 36 | 27 | ||
| 37 | ServiceContext::~ServiceContext() { | 28 | ServiceContext::~ServiceContext() { |
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/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_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/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/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_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 bfd2ae650..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); |
| @@ -463,42 +503,18 @@ void VisitUsages(Info& info, IR::Inst& inst) { | |||
| 463 | case IR::Opcode::GetCbufU32x2: { | 503 | case IR::Opcode::GetCbufU32x2: { |
| 464 | const IR::Value index{inst.Arg(0)}; | 504 | const IR::Value index{inst.Arg(0)}; |
| 465 | const IR::Value offset{inst.Arg(1)}; | 505 | const IR::Value offset{inst.Arg(1)}; |
| 466 | if (!index.IsImmediate()) { | 506 | if (index.IsImmediate()) { |
| 467 | throw NotImplementedException("Constant buffer with non-immediate index"); | 507 | AddConstantBufferDescriptor(info, index.U32(), 1); |
| 468 | } | 508 | u32 element_size = GetElementSize(info.used_constant_buffer_types, inst.GetOpcode()); |
| 469 | AddConstantBufferDescriptor(info, index.U32(), 1); | 509 | u32& size{info.constant_buffer_used_sizes[index.U32()]}; |
| 470 | u32 element_size{}; | 510 | if (offset.IsImmediate()) { |
| 471 | switch (inst.GetOpcode()) { | 511 | size = Common::AlignUp(std::max(size, offset.U32() + element_size), 16u); |
| 472 | case IR::Opcode::GetCbufU8: | 512 | } else { |
| 473 | case IR::Opcode::GetCbufS8: | 513 | size = 0x10'000; |
| 474 | info.used_constant_buffer_types |= IR::Type::U8; | 514 | } |
| 475 | element_size = 1; | ||
| 476 | break; | ||
| 477 | case IR::Opcode::GetCbufU16: | ||
| 478 | case IR::Opcode::GetCbufS16: | ||
| 479 | info.used_constant_buffer_types |= IR::Type::U16; | ||
| 480 | element_size = 2; | ||
| 481 | break; | ||
| 482 | case IR::Opcode::GetCbufU32: | ||
| 483 | info.used_constant_buffer_types |= IR::Type::U32; | ||
| 484 | element_size = 4; | ||
| 485 | break; | ||
| 486 | case IR::Opcode::GetCbufF32: | ||
| 487 | info.used_constant_buffer_types |= IR::Type::F32; | ||
| 488 | element_size = 4; | ||
| 489 | break; | ||
| 490 | case IR::Opcode::GetCbufU32x2: | ||
| 491 | info.used_constant_buffer_types |= IR::Type::U32x2; | ||
| 492 | element_size = 8; | ||
| 493 | break; | ||
| 494 | default: | ||
| 495 | break; | ||
| 496 | } | ||
| 497 | u32& size{info.constant_buffer_used_sizes[index.U32()]}; | ||
| 498 | if (offset.IsImmediate()) { | ||
| 499 | size = Common::AlignUp(std::max(size, offset.U32() + element_size), 16u); | ||
| 500 | } else { | 515 | } else { |
| 501 | size = 0x10'000; | 516 | AddRegisterIndexedLdc(info); |
| 517 | GetElementSize(info.used_indirect_cbuf_types, inst.GetOpcode()); | ||
| 502 | } | 518 | } |
| 503 | break; | 519 | break; |
| 504 | } | 520 | } |
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 38592afd0..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 | |||
| @@ -334,7 +334,8 @@ std::optional<LowAddrInfo> TrackLowAddress(IR::Inst* inst) { | |||
| 334 | /// 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 |
| 335 | std::optional<StorageBufferAddr> Track(const IR::Value& value, const Bias* bias) { | 335 | std::optional<StorageBufferAddr> Track(const IR::Value& value, const Bias* bias) { |
| 336 | const auto pred{[bias](const IR::Inst* inst) -> std::optional<StorageBufferAddr> { | 336 | const auto pred{[bias](const IR::Inst* inst) -> std::optional<StorageBufferAddr> { |
| 337 | if (inst->GetOpcode() != IR::Opcode::GetCbufU32) { | 337 | if (inst->GetOpcode() != IR::Opcode::GetCbufU32 && |
| 338 | inst->GetOpcode() != IR::Opcode::GetCbufU32x2) { | ||
| 338 | return std::nullopt; | 339 | return std::nullopt; |
| 339 | } | 340 | } |
| 340 | const IR::Value index{inst->Arg(0)}; | 341 | const IR::Value index{inst->Arg(0)}; |
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/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_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_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/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_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 0f62779de..83a23b66a 100644 --- a/src/video_core/renderer_vulkan/vk_texture_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_texture_cache.cpp | |||
| @@ -781,11 +781,6 @@ bool TextureCacheRuntime::ShouldReinterpret(Image& dst, Image& src) { | |||
| 781 | !device.IsExtShaderStencilExportSupported()) { | 781 | !device.IsExtShaderStencilExportSupported()) { |
| 782 | return true; | 782 | return true; |
| 783 | } | 783 | } |
| 784 | if (VideoCore::Surface::GetFormatType(src.info.format) == | ||
| 785 | VideoCore::Surface::SurfaceType::DepthStencil && | ||
| 786 | !device.IsExtShaderStencilExportSupported()) { | ||
| 787 | return true; | ||
| 788 | } | ||
| 789 | if (dst.info.format == PixelFormat::D32_FLOAT_S8_UINT || | 784 | if (dst.info.format == PixelFormat::D32_FLOAT_S8_UINT || |
| 790 | src.info.format == PixelFormat::D32_FLOAT_S8_UINT) { | 785 | src.info.format == PixelFormat::D32_FLOAT_S8_UINT) { |
| 791 | return true; | 786 | return true; |
| @@ -1070,6 +1065,9 @@ void TextureCacheRuntime::ConvertImage(Framebuffer* dst, ImageView& dst_view, Im | |||
| 1070 | if (src_view.format == PixelFormat::S8_UINT_D24_UNORM) { | 1065 | if (src_view.format == PixelFormat::S8_UINT_D24_UNORM) { |
| 1071 | return blit_image_helper.ConvertD24S8ToABGR8(dst, src_view); | 1066 | return blit_image_helper.ConvertD24S8ToABGR8(dst, src_view); |
| 1072 | } | 1067 | } |
| 1068 | if (src_view.format == PixelFormat::D24_UNORM_S8_UINT) { | ||
| 1069 | return blit_image_helper.ConvertS8D24ToABGR8(dst, src_view); | ||
| 1070 | } | ||
| 1073 | break; | 1071 | break; |
| 1074 | case PixelFormat::R32_FLOAT: | 1072 | case PixelFormat::R32_FLOAT: |
| 1075 | if (src_view.format == PixelFormat::D32_FLOAT) { | 1073 | if (src_view.format == PixelFormat::D32_FLOAT) { |
diff --git a/src/video_core/texture_cache/texture_cache.h b/src/video_core/texture_cache/texture_cache.h index 198bb0cfb..72eeb8bbd 100644 --- a/src/video_core/texture_cache/texture_cache.h +++ b/src/video_core/texture_cache/texture_cache.h | |||
| @@ -343,7 +343,7 @@ template <bool has_blacklists> | |||
| 343 | void TextureCache<P>::FillImageViews(DescriptorTable<TICEntry>& table, | 343 | void TextureCache<P>::FillImageViews(DescriptorTable<TICEntry>& table, |
| 344 | std::span<ImageViewId> cached_image_view_ids, | 344 | std::span<ImageViewId> cached_image_view_ids, |
| 345 | std::span<ImageViewInOut> views) { | 345 | std::span<ImageViewInOut> views) { |
| 346 | bool has_blacklisted; | 346 | bool has_blacklisted = false; |
| 347 | do { | 347 | do { |
| 348 | has_deleted_images = false; | 348 | has_deleted_images = false; |
| 349 | if constexpr (has_blacklists) { | 349 | if constexpr (has_blacklists) { |
| @@ -1725,7 +1725,7 @@ void TextureCache<P>::SynchronizeAliases(ImageId image_id) { | |||
| 1725 | }); | 1725 | }); |
| 1726 | const auto& resolution = Settings::values.resolution_info; | 1726 | const auto& resolution = Settings::values.resolution_info; |
| 1727 | for (const AliasedImage* const aliased : aliased_images) { | 1727 | for (const AliasedImage* const aliased : aliased_images) { |
| 1728 | if (!resolution.active | !any_rescaled) { | 1728 | if (!resolution.active || !any_rescaled) { |
| 1729 | CopyImage(image_id, aliased->id, aliased->copies); | 1729 | CopyImage(image_id, aliased->id, aliased->copies); |
| 1730 | continue; | 1730 | continue; |
| 1731 | } | 1731 | } |
| @@ -1736,19 +1736,7 @@ void TextureCache<P>::SynchronizeAliases(ImageId image_id) { | |||
| 1736 | continue; | 1736 | continue; |
| 1737 | } | 1737 | } |
| 1738 | ScaleUp(aliased_image); | 1738 | ScaleUp(aliased_image); |
| 1739 | 1739 | CopyImage(image_id, aliased->id, aliased->copies); | |
| 1740 | const bool both_2d{image.info.type == ImageType::e2D && | ||
| 1741 | aliased_image.info.type == ImageType::e2D}; | ||
| 1742 | auto copies = aliased->copies; | ||
| 1743 | for (auto copy : copies) { | ||
| 1744 | copy.extent.width = std::max<u32>( | ||
| 1745 | (copy.extent.width * resolution.up_scale) >> resolution.down_shift, 1); | ||
| 1746 | if (both_2d) { | ||
| 1747 | copy.extent.height = std::max<u32>( | ||
| 1748 | (copy.extent.height * resolution.up_scale) >> resolution.down_shift, 1); | ||
| 1749 | } | ||
| 1750 | } | ||
| 1751 | CopyImage(image_id, aliased->id, copies); | ||
| 1752 | } | 1740 | } |
| 1753 | } | 1741 | } |
| 1754 | 1742 | ||
diff --git a/src/yuzu/configuration/config.cpp b/src/yuzu/configuration/config.cpp index c2b66ff14..4b943c6ba 100644 --- a/src/yuzu/configuration/config.cpp +++ b/src/yuzu/configuration/config.cpp | |||
| @@ -1155,6 +1155,8 @@ void Config::SaveCpuValues() { | |||
| 1155 | WriteBasicSetting(Settings::values.cpuopt_misc_ir); | 1155 | WriteBasicSetting(Settings::values.cpuopt_misc_ir); |
| 1156 | WriteBasicSetting(Settings::values.cpuopt_reduce_misalign_checks); | 1156 | WriteBasicSetting(Settings::values.cpuopt_reduce_misalign_checks); |
| 1157 | 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); | ||
| 1158 | } | 1160 | } |
| 1159 | 1161 | ||
| 1160 | qt_config->endGroup(); | 1162 | qt_config->endGroup(); |
diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp index 1d459bdb3..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" |
| @@ -1285,6 +1286,7 @@ bool GMainWindow::LoadROM(const QString& filename, u64 program_id, std::size_t p | |||
| 1285 | std::make_unique<QtControllerSelector>(*this), // Controller Selector | 1286 | std::make_unique<QtControllerSelector>(*this), // Controller Selector |
| 1286 | std::make_unique<QtErrorDisplay>(*this), // Error Display | 1287 | std::make_unique<QtErrorDisplay>(*this), // Error Display |
| 1287 | nullptr, // Parental Controls | 1288 | nullptr, // Parental Controls |
| 1289 | nullptr, // Mii editor | ||
| 1288 | nullptr, // Photo Viewer | 1290 | nullptr, // Photo Viewer |
| 1289 | std::make_unique<QtProfileSelector>(*this), // Profile Selector | 1291 | std::make_unique<QtProfileSelector>(*this), // Profile Selector |
| 1290 | std::make_unique<QtSoftwareKeyboard>(*this), // Software Keyboard | 1292 | std::make_unique<QtSoftwareKeyboard>(*this), // Software Keyboard |
diff --git a/src/yuzu_cmd/config.cpp b/src/yuzu_cmd/config.cpp index b74411c84..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 | ||
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 3ac1440c9..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. |
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 |