summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/common/intrusive_red_black_tree.h391
-rw-r--r--src/common/tree.h625
-rw-r--r--src/core/CMakeLists.txt8
-rw-r--r--src/core/arm/dynarmic/arm_dynarmic_32.cpp4
-rw-r--r--src/core/arm/dynarmic/arm_dynarmic_64.cpp4
-rw-r--r--src/core/frontend/applets/mii.cpp19
-rw-r--r--src/core/frontend/applets/mii.h35
-rw-r--r--src/core/hle/ipc_helpers.h2
-rw-r--r--src/core/hle/kernel/hle_ipc.cpp5
-rw-r--r--src/core/hle/kernel/hle_ipc.h9
-rw-r--r--src/core/hle/kernel/init/init_slab_setup.cpp101
-rw-r--r--src/core/hle/kernel/init/init_slab_setup.h5
-rw-r--r--src/core/hle/kernel/k_address_arbiter.cpp6
-rw-r--r--src/core/hle/kernel/k_condition_variable.cpp2
-rw-r--r--src/core/hle/kernel/k_handle_table.cpp8
-rw-r--r--src/core/hle/kernel/k_handle_table.h34
-rw-r--r--src/core/hle/kernel/k_memory_layout.h6
-rw-r--r--src/core/hle/kernel/k_page_buffer.cpp19
-rw-r--r--src/core/hle/kernel/k_page_buffer.h29
-rw-r--r--src/core/hle/kernel/k_page_table.cpp126
-rw-r--r--src/core/hle/kernel/k_page_table.h17
-rw-r--r--src/core/hle/kernel/k_port.cpp7
-rw-r--r--src/core/hle/kernel/k_process.cpp194
-rw-r--r--src/core/hle/kernel/k_process.h18
-rw-r--r--src/core/hle/kernel/k_server_port.h8
-rw-r--r--src/core/hle/kernel/k_server_session.cpp15
-rw-r--r--src/core/hle/kernel/k_slab_heap.h230
-rw-r--r--src/core/hle/kernel/k_thread.cpp7
-rw-r--r--src/core/hle/kernel/k_thread.h6
-rw-r--r--src/core/hle/kernel/k_thread_local_page.cpp66
-rw-r--r--src/core/hle/kernel/k_thread_local_page.h112
-rw-r--r--src/core/hle/kernel/kernel.cpp78
-rw-r--r--src/core/hle/kernel/kernel.h22
-rw-r--r--src/core/hle/kernel/service_thread.cpp5
-rw-r--r--src/core/hle/kernel/slab_helpers.h2
-rw-r--r--src/core/hle/kernel/svc_types.h2
-rw-r--r--src/core/hle/service/am/am.cpp4
-rw-r--r--src/core/hle/service/am/applets/applet_mii.cpp101
-rw-r--r--src/core/hle/service/am/applets/applet_mii.h90
-rw-r--r--src/core/hle/service/am/applets/applets.cpp17
-rw-r--r--src/core/hle/service/am/applets/applets.h9
-rw-r--r--src/core/hle/service/kernel_helpers.cpp11
-rw-r--r--src/core/hle/service/sm/sm.cpp2
-rw-r--r--src/core/hle/service/sockets/bsd.cpp4
-rw-r--r--src/shader_recompiler/backend/spirv/emit_spirv.h2
-rw-r--r--src/shader_recompiler/backend/spirv/emit_spirv_context_get_set.cpp59
-rw-r--r--src/shader_recompiler/backend/spirv/spirv_emit_context.cpp59
-rw-r--r--src/shader_recompiler/backend/spirv/spirv_emit_context.h8
-rw-r--r--src/shader_recompiler/frontend/maxwell/translate/impl/load_constant.cpp14
-rw-r--r--src/shader_recompiler/frontend/maxwell/translate_program.cpp4
-rw-r--r--src/shader_recompiler/ir_opt/collect_shader_info_pass.cpp86
-rw-r--r--src/shader_recompiler/ir_opt/global_memory_to_storage_buffer_pass.cpp3
-rw-r--r--src/shader_recompiler/ir_opt/rescaling_pass.cpp29
-rw-r--r--src/shader_recompiler/shader_info.h2
-rw-r--r--src/video_core/engines/maxwell_3d.cpp109
-rw-r--r--src/video_core/engines/maxwell_3d.h50
-rw-r--r--src/video_core/fence_manager.h2
-rw-r--r--src/video_core/host_shaders/CMakeLists.txt1
-rw-r--r--src/video_core/host_shaders/convert_s8d24_to_abgr8.frag23
-rw-r--r--src/video_core/query_cache.h1
-rw-r--r--src/video_core/renderer_opengl/gl_state_tracker.h1
-rw-r--r--src/video_core/renderer_vulkan/blit_image.cpp9
-rw-r--r--src/video_core/renderer_vulkan/blit_image.h4
-rw-r--r--src/video_core/renderer_vulkan/vk_compute_pass.cpp3
-rw-r--r--src/video_core/renderer_vulkan/vk_state_tracker.h1
-rw-r--r--src/video_core/renderer_vulkan/vk_texture_cache.cpp8
-rw-r--r--src/video_core/texture_cache/texture_cache.h18
-rw-r--r--src/yuzu/configuration/config.cpp2
-rw-r--r--src/yuzu/main.cpp2
-rw-r--r--src/yuzu_cmd/config.cpp10
-rw-r--r--src/yuzu_cmd/config.h5
-rw-r--r--src/yuzu_cmd/default_ini.h6
-rw-r--r--src/yuzu_cmd/yuzu.cpp22
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)
18struct IntrusiveRedBlackTreeNode { 21struct IntrusiveRedBlackTreeNode {
22 YUZU_NON_COPYABLE(IntrusiveRedBlackTreeNode);
23
19public: 24public:
20 using EntryType = RBEntry<IntrusiveRedBlackTreeNode>; 25 using RBEntry = freebsd::RBEntry<IntrusiveRedBlackTreeNode>;
21 26
22 constexpr IntrusiveRedBlackTreeNode() = default; 27private:
28 RBEntry m_entry;
23 29
24 void SetEntry(const EntryType& new_entry) { 30public:
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
36private: 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};
44static_assert(sizeof(IntrusiveRedBlackTreeNode) ==
45 3 * sizeof(void*) + std::max<size_t>(sizeof(freebsd::RBColor), 4));
46#pragma pack(pop)
44 47
45template <class T, class Traits, class Comparator> 48template <class T, class Traits, class Comparator>
46class IntrusiveRedBlackTree; 49class IntrusiveRedBlackTree;
@@ -48,12 +51,17 @@ class IntrusiveRedBlackTree;
48namespace impl { 51namespace impl {
49 52
50class IntrusiveRedBlackTreeImpl { 53class IntrusiveRedBlackTreeImpl {
54 YUZU_NON_COPYABLE(IntrusiveRedBlackTreeImpl);
55
51private: 56private:
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>; 60private:
56 RootType root; 61 using RootType = freebsd::RBHead<IntrusiveRedBlackTreeNode>;
62
63private:
64 RootType m_root;
57 65
58public: 66public:
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
132private: 140private:
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
150public: 157public:
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
169public: 178public:
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
236template <typename T> 245template <typename T>
237concept HasLightCompareType = requires { 246concept 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
241namespace impl { 250namespace 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
254template <typename T, typename Default> 263template <typename T, typename Default>
255using LightCompareType = std::remove_pointer_t<decltype(impl::GetLightCompareType<T, Default>())>; 264using RedBlackKeyType = std::remove_pointer_t<decltype(impl::GetRedBlackKeyType<T, Default>())>;
256 265
257template <class T, class Traits, class Comparator> 266template <class T, class Traits, class Comparator>
258class IntrusiveRedBlackTree { 267class IntrusiveRedBlackTree {
268 YUZU_NON_COPYABLE(IntrusiveRedBlackTree);
259 269
260public: 270public:
261 using ImplType = impl::IntrusiveRedBlackTreeImpl; 271 using ImplType = impl::IntrusiveRedBlackTreeImpl;
262 272
263private: 273private:
264 ImplType impl{}; 274 ImplType m_impl;
265 275
266public: 276public:
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
358private: 366private:
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
393public: 413public:
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
477template <auto T, class Derived = impl::GetParentType<T>> 505template <auto T, class Derived = Common::impl::GetParentType<T>>
478class IntrusiveRedBlackTreeMemberTraits; 506class IntrusiveRedBlackTreeMemberTraits;
479 507
480template <class Parent, IntrusiveRedBlackTreeNode Parent::*Member, class Derived> 508template <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
509private:
510 static constexpr TypedStorage<Derived> DerivedStorage = {};
511}; 536};
512 537
513template <auto T, class Derived = impl::GetParentType<T>> 538template <auto T, class Derived = Common::impl::GetParentType<T>>
514class IntrusiveRedBlackTreeMemberTraitsDeferredAssert; 539class IntrusiveRedBlackTreeMemberTraitsDeferredAssert;
515 540
516template <class Parent, IntrusiveRedBlackTreeNode Parent::*Member, class Derived> 541template <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
529private: 549private:
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
552template <class Derived> 572template <class Derived>
553class IntrusiveRedBlackTreeBaseNode : public IntrusiveRedBlackTreeNode { 573class alignas(void*) IntrusiveRedBlackTreeBaseNode : public IntrusiveRedBlackTreeNode {
554public: 574public:
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" 46namespace Common::freebsd {
47 47
48namespace Common { 48enum class RBColor {
49 RB_BLACK = 0,
50 RB_RED = 1,
51};
52
53#pragma pack(push, 4)
49template <typename T> 54template <typename T>
50class RBHead { 55class RBEntry {
51public: 56public:
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
68private: 70 [[nodiscard]] constexpr T* Right() {
69 T* rbh_root = nullptr; 71 return m_rbe_right;
70};
71
72enum class EntryColor {
73 Black,
74 Red,
75};
76
77template <typename T>
78class RBEntry {
79public:
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 { 106private:
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 { 114template <typename T>
125 return rbe_color; 115struct CheckRBEntry {
126 } 116 static constexpr bool value = false;
117};
118template <typename T>
119struct CheckRBEntry<RBEntry<T>> {
120 static constexpr bool value = true;
121};
127 122
128 void SetColor(EntryColor color) { 123template <typename T>
129 rbe_color = color; 124concept IsRBEntry = CheckRBEntry<T>::value;
130 }
131 125
126template <typename T>
127concept 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
132template <typename T>
133requires HasRBEntry<T>
134class RBHead {
132private: 135private:
133 T* rbe_left = nullptr; 136 T* m_rbh_root = nullptr;
134 T* rbe_right = nullptr; 137
135 T* rbe_parent = nullptr; 138public:
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
139template <typename Node> 154template <typename T>
140[[nodiscard]] RBEntry<Node>& RB_ENTRY(Node* node) { 155requires HasRBEntry<T>
141 return node->GetEntry(); 156[[nodiscard]] constexpr RBEntry<T>& RB_ENTRY(T* t) {
157 return t->GetRBEntry();
142} 158}
143 159template <typename T>
144template <typename Node> 160requires 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
149template <typename Node> 165template <typename T>
150[[nodiscard]] Node* RB_PARENT(Node* node) { 166requires 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 170template <typename T>
154template <typename Node> 171requires 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
159template <typename Node> 176template <typename T>
160void RB_SET_PARENT(Node* node, Node* parent) { 177requires 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 181template <typename T>
164template <typename Node> 182requires 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
169template <typename Node> 187template <typename T>
170[[nodiscard]] const Node* RB_LEFT(const Node* node) { 188requires 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 192template <typename T>
174template <typename Node> 193requires HasRBEntry<T>
175void 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
179template <typename Node> 198template <typename T>
180[[nodiscard]] Node* RB_RIGHT(Node* node) { 199requires HasRBEntry<T>
181 return RB_ENTRY(node).Right(); 200constexpr void RB_SET_LEFT(T* t, T* e) {
201 RB_ENTRY(t).SetLeft(e);
182} 202}
183 203template <typename T>
184template <typename Node> 204requires HasRBEntry<T>
185[[nodiscard]] const Node* RB_RIGHT(const Node* node) { 205constexpr void RB_SET_RIGHT(T* t, T* e) {
186 return RB_ENTRY(node).Right(); 206 RB_ENTRY(t).SetRight(e);
187} 207}
188 208template <typename T>
189template <typename Node> 209requires HasRBEntry<T>
190void RB_SET_RIGHT(Node* node, Node* right) { 210constexpr 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
194template <typename Node> 214template <typename T>
195[[nodiscard]] bool RB_IS_BLACK(const Node* node) { 215requires 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 219template <typename T>
199template <typename Node> 220requires 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
204template <typename Node> 225template <typename T>
205[[nodiscard]] EntryColor RB_COLOR(const Node* node) { 226requires 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
209template <typename Node> 231template <typename T>
210void RB_SET_COLOR(Node* node, EntryColor color) { 232requires HasRBEntry<T>
211 return RB_ENTRY(node).SetColor(color); 233constexpr void RB_SET_COLOR(T* t, RBColor c) {
234 RB_ENTRY(t).SetColor(c);
212} 235}
213 236
214template <typename Node> 237template <typename T>
215void RB_SET(Node* node, Node* parent) { 238requires HasRBEntry<T>
216 auto& entry = RB_ENTRY(node); 239constexpr 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
223template <typename Node> 247template <typename T>
224void RB_SET_BLACKRED(Node* black, Node* red) { 248requires HasRBEntry<T>
225 RB_SET_COLOR(black, EntryColor::Black); 249constexpr 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
229template <typename Node> 254template <typename T>
230void RB_ROTATE_LEFT(RBHead<Node>* head, Node* elm, Node*& tmp) { 255requires HasRBEntry<T>
256constexpr 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
252template <typename Node> 276template <typename T>
253void RB_ROTATE_RIGHT(RBHead<Node>* head, Node* elm, Node*& tmp) { 277requires HasRBEntry<T>
278constexpr 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
275template <typename Node> 298template <typename T>
276void RB_INSERT_COLOR(RBHead<Node>* head, Node* elm) { 299requires HasRBEntry<T>
277 Node* parent = nullptr; 300constexpr 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
324template <typename Node>
325void 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
416template <typename Node> 382template <typename T>
417Node* RB_REMOVE(RBHead<Node>* head, Node* elm) { 383requires HasRBEntry<T>
418 Node* child = nullptr; 384constexpr 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
475template <typename T>
476requires HasRBEntry<T>
477constexpr 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 523template <typename T, typename Compare>
505template <typename Node, typename CompareFunction> 524requires HasRBEntry<T>
506Node* RB_INSERT(RBHead<Node>* head, Node* elm, CompareFunction cmp) { 525constexpr 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 558template <typename T, typename Compare>
540template <typename Node, typename CompareFunction> 559requires HasRBEntry<T>
541Node* RB_FIND(RBHead<Node>* head, Node* elm, CompareFunction cmp) { 560constexpr 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 577template <typename T, typename Compare>
559template <typename Node, typename CompareFunction> 578requires HasRBEntry<T>
560Node* RB_NFIND(RBHead<Node>* head, Node* elm, CompareFunction cmp) { 579constexpr 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 598template <typename T, typename U, typename Compare>
580template <typename Node, typename CompareFunction> 599requires HasRBEntry<T>
581Node* RB_FIND_LIGHT(RBHead<Node>* head, const void* lelm, CompareFunction lcmp) { 600constexpr 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 617template <typename T, typename U, typename Compare>
599template <typename Node, typename CompareFunction> 618requires HasRBEntry<T>
600Node* RB_NFIND_LIGHT(RBHead<Node>* head, const void* lelm, CompareFunction lcmp) { 619constexpr 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
619template <typename Node> 638template <typename T, typename Compare>
620Node* RB_NEXT(Node* elm) { 639requires HasRBEntry<T>
640constexpr 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
655template <typename T, typename U, typename Compare>
656requires HasRBEntry<T>
657constexpr 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
672template <typename T>
673requires HasRBEntry<T>
674constexpr 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
639template <typename Node> 693template <typename T>
640Node* RB_PREV(Node* elm) { 694requires HasRBEntry<T>
695constexpr 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
659template <typename Node> 714template <typename T>
660Node* RB_MINMAX(RBHead<Node>* head, bool is_min) { 715requires HasRBEntry<T>
661 Node* tmp = head->Root(); 716constexpr 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
676template <typename Node> 728template <typename T>
677Node* RB_MIN(RBHead<Node>* head) { 729requires HasRBEntry<T>
678 return RB_MINMAX(head, true); 730constexpr T* RB_MAX(RBHead<T>& head) {
679} 731 T* tmp = head.Root();
732 T* parent = nullptr;
680 733
681template <typename Node> 734 while (tmp) {
682Node* 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
8namespace Core::Frontend {
9
10MiiApplet::~MiiApplet() = default;
11
12void 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
12namespace Core::Frontend {
13
14struct MiiParameters {
15 bool is_editable;
16 Service::Mii::MiiInfo mii_data{};
17};
18
19class MiiApplet {
20public:
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
28class DefaultMiiApplet final : public MiiApplet {
29public:
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
54void SessionRequestHandler::ClientConnected(KServerSession* session) { 54void 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
61void SessionRequestHandler::ClientDisconnected(KServerSession* session) { 58void 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
97using SessionRequestHandlerWeakPtr = std::weak_ptr<SessionRequestHandler>;
97using SessionRequestHandlerPtr = std::shared_ptr<SessionRequestHandler>; 98using 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
25namespace Kernel::Init { 29namespace 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
40namespace { 48namespace {
@@ -50,38 +58,46 @@ enum KSlabType : u32 {
50// Constexpr counts. 58// Constexpr counts.
51constexpr size_t SlabCountKProcess = 80; 59constexpr size_t SlabCountKProcess = 80;
52constexpr size_t SlabCountKThread = 800; 60constexpr size_t SlabCountKThread = 800;
53constexpr size_t SlabCountKEvent = 700; 61constexpr size_t SlabCountKEvent = 900;
54constexpr size_t SlabCountKInterruptEvent = 100; 62constexpr size_t SlabCountKInterruptEvent = 100;
55constexpr size_t SlabCountKPort = 256 + 0x20; // Extra 0x20 ports over Nintendo for homebrew. 63constexpr size_t SlabCountKPort = 384;
56constexpr size_t SlabCountKSharedMemory = 80; 64constexpr size_t SlabCountKSharedMemory = 80;
57constexpr size_t SlabCountKTransferMemory = 200; 65constexpr size_t SlabCountKTransferMemory = 200;
58constexpr size_t SlabCountKCodeMemory = 10; 66constexpr size_t SlabCountKCodeMemory = 10;
59constexpr size_t SlabCountKDeviceAddressSpace = 300; 67constexpr size_t SlabCountKDeviceAddressSpace = 300;
60constexpr size_t SlabCountKSession = 933; 68constexpr size_t SlabCountKSession = 1133;
61constexpr size_t SlabCountKLightSession = 100; 69constexpr size_t SlabCountKLightSession = 100;
62constexpr size_t SlabCountKObjectName = 7; 70constexpr size_t SlabCountKObjectName = 7;
63constexpr size_t SlabCountKResourceLimit = 5; 71constexpr size_t SlabCountKResourceLimit = 5;
64constexpr size_t SlabCountKDebug = Core::Hardware::NUM_CPU_CORES; 72constexpr size_t SlabCountKDebug = Core::Hardware::NUM_CPU_CORES;
65constexpr size_t SlabCountKAlpha = 1; 73constexpr size_t SlabCountKIoPool = 1;
66constexpr size_t SlabCountKBeta = 6; 74constexpr size_t SlabCountKIoRegion = 6;
67 75
68constexpr size_t SlabCountExtraKThread = 160; 76constexpr size_t SlabCountExtraKThread = 160;
69 77
78/// Helper function to translate from the slab virtual address to the reserved location in physical
79/// memory.
80static PAddr TranslateSlabAddrToPhysical(KMemoryLayout& memory_layout, VAddr slab_addr) {
81 slab_addr -= memory_layout.GetSlabRegionAddress();
82 return slab_addr + Core::DramMemoryMap::SlabHeapBase;
83}
84
70template <typename T> 85template <typename T>
71VAddr InitializeSlabHeap(Core::System& system, KMemoryLayout& memory_layout, VAddr address, 86VAddr 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
110size_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
96KSlabResourceCounts KSlabResourceCounts::CreateDefault() { 118KSlabResourceCounts 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
166void 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
144void InitializeSlabHeaps(Core::System& system, KMemoryLayout& memory_layout) { 189void 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
39void InitializeSlabResourceCounts(KernelCore& kernel); 39void InitializeSlabResourceCounts(KernelCore& kernel);
40size_t CalculateTotalSlabHeapSize(const KernelCore& kernel); 40size_t CalculateTotalSlabHeapSize(const KernelCore& kernel);
41void InitializeKPageBufferSlabHeap(Core::System& system);
41void InitializeSlabHeaps(Core::System& system, KMemoryLayout& memory_layout); 42void 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
66ResultCode KHandleTable::Add(Handle* out_handle, KAutoObject* obj, u16 type) { 66ResultCode 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
119void KHandleTable::Register(Handle handle, KAutoObject* obj, u16 type) { 119void 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
162private: 153private:
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
57constexpr std::size_t KernelInitialPageHeapSize = 128_KiB; 57constexpr std::size_t KernelInitialPageHeapSize = 128_KiB;
58 58
59constexpr std::size_t KernelSlabHeapDataSize = 5_MiB; 59constexpr std::size_t KernelSlabHeapDataSize = 5_MiB;
60constexpr std::size_t KernelSlabHeapGapsSize = 2_MiB - 64_KiB; 60constexpr std::size_t KernelSlabHeapGapsSizeMax = 2_MiB - 64_KiB;
61constexpr std::size_t KernelSlabHeapSize = KernelSlabHeapDataSize + KernelSlabHeapGapsSize; 61constexpr 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.
64constexpr std::size_t KernelSlabHeapAdditionalSize = 416_KiB; 64constexpr std::size_t KernelSlabHeapAdditionalSize = 0x68000;
65 65
66constexpr std::size_t KernelResourceSize = 66constexpr 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
12namespace Kernel {
13
14KPageBuffer* 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
14namespace Kernel {
15
16class KPageBuffer final : public KSlabAllocated<KPageBuffer> {
17public:
18 KPageBuffer() = default;
19
20 static KPageBuffer* FromPhysicalAddress(Core::System& system, PAddr phys_addr);
21
22private:
23 [[maybe_unused]] alignas(PageSize) std::array<u8, PageSize> m_buffer{};
24};
25
26static_assert(sizeof(KPageBuffer) == PageSize);
27static_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
427VAddr 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
427ResultCode KPageTable::UnmapProcessMemory(VAddr dst_addr, std::size_t size, 489ResultCode 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
1120ResultCode 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
1058ResultCode KPageTable::UnmapPages(VAddr addr, const KPageLinkedList& page_linked_list) { 1160ResultCode 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
1202ResultCode 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
1100ResultCode KPageTable::SetProcessMemoryPermission(VAddr addr, std::size_t size, 1226ResultCode 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).
78class TLSPage {
79public:
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
116private:
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
125ResultCode KProcess::Initialize(KProcess* process, Core::System& system, std::string process_name, 73ResultCode 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
458void KProcess::Finalize() { 406void 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/** 438ResultCode 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 */
498static 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
503VAddr 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
534void KProcess::FreeTLSRegion(VAddr tls_address) { 489ResultCode 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
549void KProcess::LoadModule(CodeSet code_set, VAddr base_addr) { 537void 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
370private: 371private:
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
28KServerSession::KServerSession(KernelCore& kernel_) : KSynchronizationObject{kernel_} {} 28KServerSession::KServerSession(KernelCore& kernel_) : KSynchronizationObject{kernel_} {}
29 29
30KServerSession::~KServerSession() { 30KServerSession::~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
35void KServerSession::Initialize(KSession* parent_session_, std::string&& name_, 32void 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
54void KServerSession::OnClientClosed() { 54void 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
17namespace impl { 17namespace impl {
18 18
19class KSlabHeapImpl final { 19class KSlabHeapImpl {
20public:
21 YUZU_NON_COPYABLE(KSlabHeapImpl); 20 YUZU_NON_COPYABLE(KSlabHeapImpl);
22 YUZU_NON_MOVEABLE(KSlabHeapImpl); 21 YUZU_NON_MOVEABLE(KSlabHeapImpl);
23 22
23public:
24 struct Node { 24 struct Node {
25 Node* next{}; 25 Node* next{};
26 }; 26 };
27 27
28public:
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
65private: 60private:
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
72class KSlabHeapBase { 66template <bool SupportDynamicExpansion>
73public: 67class 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; 71private:
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 { 77private:
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 { 91public:
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
137private: 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
146template <typename T> 168template <typename T>
147class KSlabHeap final : public KSlabHeapBase { 169class KSlabHeap final : public KSlabHeapBase<false> {
148public: 170private:
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) 173public:
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
221private:
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
13namespace Kernel {
14
15ResultCode 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
37ResultCode 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
51VAddr 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
62void 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
19namespace Kernel {
20
21class KernelCore;
22class KProcess;
23
24class KThreadLocalPage final : public Common::IntrusiveRedBlackTreeBaseNode<KThreadLocalPage>,
25 public KSlabAllocated<KThreadLocalPage> {
26public:
27 static constexpr size_t RegionsPerPage = PageSize / Svc::ThreadLocalRegionSize;
28 static_assert(RegionsPerPage > 0);
29
30public:
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
63public:
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
90private:
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
105private:
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
53struct KernelCore::Impl { 53struct 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
917KAutoObjectWithListContainer& KernelCore::ObjectListContainer() { 891KAutoObjectWithListContainer& KernelCore::ObjectListContainer() {
918 return impl->object_list_container; 892 return *impl->global_object_list_container;
919} 893}
920 894
921const KAutoObjectWithListContainer& KernelCore::ObjectListContainer() const { 895const KAutoObjectWithListContainer& KernelCore::ObjectListContainer() const {
922 return impl->object_list_container; 896 return *impl->global_object_list_container;
923} 897}
924 898
925void KernelCore::InvalidateAllInstructionCaches() { 899void 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
952void KernelCore::RegisterServerSession(KServerSession* server_session) {
953 std::lock_guard lk(impl->server_sessions_lock);
954 impl->server_sessions.insert(server_session);
955}
956
957void KernelCore::UnregisterServerSession(KServerSession* server_session) {
958 std::lock_guard lk(impl->server_sessions_lock);
959 impl->server_sessions.erase(server_session);
960}
961
962void KernelCore::RegisterKernelObject(KAutoObject* object) { 926void 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
1034KSlabHeap<Page>& KernelCore::GetUserSlabHeapPages() {
1035 return *impl->user_slab_heap_pages;
1036}
1037
1038const KSlabHeap<Page>& KernelCore::GetUserSlabHeapPages() const {
1039 return *impl->user_slab_heap_pages;
1040}
1041
1042Kernel::KSharedMemory& KernelCore::GetHidSharedMem() { 998Kernel::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;
43class KLinkedListNode; 43class KLinkedListNode;
44class KMemoryLayout; 44class KMemoryLayout;
45class KMemoryManager; 45class KMemoryManager;
46class KPageBuffer;
46class KPort; 47class KPort;
47class KProcess; 48class KProcess;
48class KResourceLimit; 49class KResourceLimit;
@@ -52,6 +53,7 @@ class KSession;
52class KSharedMemory; 53class KSharedMemory;
53class KSharedMemoryInfo; 54class KSharedMemoryInfo;
54class KThread; 55class KThread;
56class KThreadLocalPage;
55class KTransferMemory; 57class KTransferMemory;
56class KWorkerTaskManager; 58class KWorkerTaskManager;
57class KWritableEvent; 59class 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
60private: 60private:
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;
96constexpr inline s32 LowestThreadPriority = 63; 96constexpr inline s32 LowestThreadPriority = 63;
97constexpr inline s32 HighestThreadPriority = 0; 97constexpr inline s32 HighestThreadPriority = 0;
98 98
99constexpr 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
13namespace Service::AM::Applets {
14
15Mii::Mii(Core::System& system_, LibraryAppletMode applet_mode_,
16 const Core::Frontend::MiiApplet& frontend_)
17 : Applet{system_, applet_mode_}, frontend{frontend_}, system{system_} {}
18
19Mii::~Mii() = default;
20
21void 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
33bool Mii::TransactionComplete() const {
34 return is_complete;
35}
36
37ResultCode Mii::GetStatus() const {
38 return ResultSuccess;
39}
40
41void Mii::ExecuteInteractive() {
42 UNREACHABLE_MSG("Unexpected interactive applet data!");
43}
44
45void 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
87void 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
13namespace Core {
14class System;
15}
16
17namespace Service::AM::Applets {
18
19// This is nn::mii::AppletMode
20enum class MiiAppletMode : u32 {
21 ShowMiiEdit = 0,
22 AppendMii = 1,
23 AppendMiiImage = 2,
24 UpdateMiiImage = 3,
25 CreateMii = 4,
26 EditMii = 5,
27};
28
29struct MiiCharInfo {
30 Service::Mii::MiiInfo mii_data{};
31 INSERT_PADDING_BYTES(0x28);
32};
33static_assert(sizeof(MiiCharInfo) == 0x80, "MiiCharInfo has incorrect size.");
34
35// This is nn::mii::AppletInput
36struct 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};
47static_assert(sizeof(MiiAppletInput) == 0x100, "MiiAppletInput has incorrect size.");
48
49// This is nn::mii::AppletOutput
50struct MiiAppletOutput {
51 ResultCode result{ResultSuccess};
52 s32 index{};
53 INSERT_PADDING_BYTES(0x18);
54};
55static_assert(sizeof(MiiAppletOutput) == 0x20, "MiiAppletOutput has incorrect size.");
56
57// This is nn::mii::AppletOutputForCharInfoEditing
58struct AppletOutputForCharInfoEditing {
59 ResultCode result{ResultSuccess};
60 Service::Mii::MiiInfo mii_data{};
61 INSERT_PADDING_BYTES(0x24);
62};
63static_assert(sizeof(AppletOutputForCharInfoEditing) == 0x80,
64 "AppletOutputForCharInfoEditing has incorrect size.");
65
66class Mii final : public Applet {
67public:
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
81private:
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
173AppletFrontendSet::AppletFrontendSet(ControllerApplet controller_applet, ErrorApplet error_applet, 175AppletFrontendSet::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;
21class ECommerceApplet; 21class ECommerceApplet;
22class ErrorApplet; 22class ErrorApplet;
23class ParentalControlsApplet; 23class ParentalControlsApplet;
24class MiiApplet;
24class PhotoViewerApplet; 25class PhotoViewerApplet;
25class ProfileSelectApplet; 26class ProfileSelectApplet;
26class SoftwareKeyboardApplet; 27class 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
18ServiceContext::ServiceContext(Core::System& system_, std::string name_) 18ServiceContext::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
37ServiceContext::~ServiceContext() { 28ServiceContext::~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 =
22struct RescalingLayout { 22struct 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};
27constexpr u32 RESCALING_LAYOUT_WORDS_OFFSET = offsetof(RescalingLayout, rescaling_textures); 27constexpr u32 RESCALING_LAYOUT_WORDS_OFFSET = offsetof(RescalingLayout, rescaling_textures);
28constexpr u32 RESCALING_LAYOUT_DOWN_FACTOR_OFFSET = offsetof(RescalingLayout, down_factor); 28constexpr 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
125Id GetCbuf(EmitContext& ctx, Id result_type, Id UniformDefinitions::*member_ptr, u32 element_size, 125Id 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
148Id GetCbufU32(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset) { 148Id 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
152Id GetCbufU32x4(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset) { 153Id 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
156Id GetCbufElement(EmitContext& ctx, Id vector, const IR::Value& offset, u32 index_offset) { 158Id GetCbufElement(EmitContext& ctx, Id vector, const IR::Value& offset, u32 index_offset) {
@@ -201,7 +203,8 @@ void EmitGetIndirectBranchVariable(EmitContext&) {
201 203
202Id EmitGetCbufU8(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset) { 204Id 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
218Id EmitGetCbufS8(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset) { 221Id 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
234Id EmitGetCbufU16(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset) { 238Id 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
251Id EmitGetCbufS16(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset) { 255Id 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
277Id EmitGetCbufF32(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset) { 281Id 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
286Id EmitGetCbufU32x2(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset) { 291Id 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
1031void 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
1030void EmitContext::DefineStorageBuffers(const Info& info, u32& binding) { 1087void 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
297private: 304private:
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 {
11using namespace LDC; 11using namespace LDC;
12namespace { 12namespace {
13std::pair<IR::U32, IR::U32> Slot(IR::IREmitter& ir, Mode mode, const IR::U32& imm_index, 13std::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
32void 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
48u32 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
32void GetPatch(Info& info, IR::Patch patch) { 72void 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
335std::optional<StorageBufferAddr> Track(const IR::Value& value, const Bias* bias) { 335std::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
186void 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
186void SubScaleCoord(IR::IREmitter& ir, IR::Inst& inst, const IR::U1& is_scaled) { 211void 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
226void SubScaleImageRead(IR::Block& block, IR::Inst& inst) { 251void 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
248void PatchImageRead(IR::Block& block, IR::Inst& inst) { 273void 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
250void Maxwell3D::CallMethod(u32 method, u32 method_argument, bool is_last_call) { 259void 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
364void 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
363void Maxwell3D::FlushMMEInlineDraw() { 393void 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
590void Maxwell3D::ProcessCBData(u32 value) { 624void 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
598void 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
607void 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
629void 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; 640void Maxwell3D::ProcessCBData(u32 value) {
644 cb_data_state.current = null_cb_data; 641 ProcessCBMultiData(&value, 1);
645} 642}
646 643
647Texture::TICEntry Maxwell3D::GetTICEntry(u32 tic_index) const { 644Texture::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);
1685ASSERT_REG_POSITION(primitive_restart, 0x591); 1701ASSERT_REG_POSITION(primitive_restart, 0x591);
1686ASSERT_REG_POSITION(provoking_vertex_last, 0x5A1); 1702ASSERT_REG_POSITION(provoking_vertex_last, 0x5A1);
1687ASSERT_REG_POSITION(index_array, 0x5F2); 1703ASSERT_REG_POSITION(index_array, 0x5F2);
1704ASSERT_REG_POSITION(small_index, 0x5F9);
1688ASSERT_REG_POSITION(polygon_offset_clamp, 0x61F); 1705ASSERT_REG_POSITION(polygon_offset_clamp, 0x61F);
1689ASSERT_REG_POSITION(instanced_arrays, 0x620); 1706ASSERT_REG_POSITION(instanced_arrays, 0x620);
1690ASSERT_REG_POSITION(vp_point_size, 0x644); 1707ASSERT_REG_POSITION(vp_point_size, 0x644);
@@ -1694,6 +1711,7 @@ ASSERT_REG_POSITION(cull_face, 0x648);
1694ASSERT_REG_POSITION(pixel_center_integer, 0x649); 1711ASSERT_REG_POSITION(pixel_center_integer, 0x649);
1695ASSERT_REG_POSITION(viewport_transform_enabled, 0x64B); 1712ASSERT_REG_POSITION(viewport_transform_enabled, 0x64B);
1696ASSERT_REG_POSITION(view_volume_clip_control, 0x64F); 1713ASSERT_REG_POSITION(view_volume_clip_control, 0x64F);
1714ASSERT_REG_POSITION(topology_override, 0x65C);
1697ASSERT_REG_POSITION(depth_bounds_enable, 0x66F); 1715ASSERT_REG_POSITION(depth_bounds_enable, 0x66F);
1698ASSERT_REG_POSITION(logic_op, 0x671); 1716ASSERT_REG_POSITION(logic_op, 0x671);
1699ASSERT_REG_POSITION(clear_buffers, 0x674); 1717ASSERT_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
7layout(binding = 0) uniform sampler2D depth_tex;
8layout(binding = 1) uniform isampler2D stencil_tex;
9
10layout(location = 0) out vec4 color;
11
12void 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
479void 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
477void BlitImageHelper::Convert(VkPipeline pipeline, const Framebuffer* dst_framebuffer, 486void 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
59private: 61private:
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>
343void TextureCache<P>::FillImageViews(DescriptorTable<TICEntry>& table, 343void 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
30namespace FS = Common::FS; 31namespace FS = Common::FS;
31 32
32Config::Config() { 33const 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
36Config::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 @@
13class INIReader; 14class INIReader;
14 15
15class Config { 16class 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
22public: 23public:
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
127use_multi_core= 127use_multi_core =
128
129# Enable extended guest system memory layout (6GB DRAM)
130# 0 (default): Disabled, 1: Enabled
131use_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
72static void PrintVersion() { 73static 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