summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/audio_core/common.h4
-rw-r--r--src/common/bit_util.h7
-rw-r--r--src/common/common_types.h10
-rw-r--r--src/common/fs/fs_util.cpp8
-rw-r--r--src/common/fs/fs_util.h18
-rw-r--r--src/common/host_memory.cpp4
-rw-r--r--src/common/intrusive_red_black_tree.h391
-rw-r--r--src/common/logging/backend.cpp18
-rw-r--r--src/common/logging/filter.cpp1
-rw-r--r--src/common/logging/types.h1
-rw-r--r--src/common/page_table.cpp58
-rw-r--r--src/common/page_table.h24
-rw-r--r--src/common/settings.cpp2
-rw-r--r--src/common/settings.h4
-rw-r--r--src/common/telemetry.cpp60
-rw-r--r--src/common/telemetry.h36
-rw-r--r--src/common/tree.h625
-rw-r--r--src/common/uuid.cpp208
-rw-r--r--src/common/uuid.h166
-rw-r--r--src/common/x64/cpu_detect.cpp124
-rw-r--r--src/common/x64/cpu_detect.h79
-rw-r--r--src/core/CMakeLists.txt12
-rw-r--r--src/core/arm/arm_interface.h6
-rw-r--r--src/core/arm/dynarmic/arm_dynarmic_32.cpp16
-rw-r--r--src/core/arm/dynarmic/arm_dynarmic_64.cpp17
-rw-r--r--src/core/arm/dynarmic/arm_exclusive_monitor.cpp4
-rw-r--r--src/core/arm/dynarmic/arm_exclusive_monitor.h2
-rw-r--r--src/core/arm/exclusive_monitor.h2
-rw-r--r--src/core/core.cpp15
-rw-r--r--src/core/device_memory.cpp5
-rw-r--r--src/core/device_memory.h4
-rw-r--r--src/core/file_sys/patch_manager.cpp18
-rw-r--r--src/core/file_sys/vfs.h18
-rw-r--r--src/core/frontend/applets/mii.cpp19
-rw-r--r--src/core/frontend/applets/mii.h35
-rw-r--r--src/core/frontend/applets/profile_select.cpp3
-rw-r--r--src/core/frontend/emu_window.h11
-rw-r--r--src/core/hid/emulated_console.h1
-rw-r--r--src/core/hid/emulated_controller.cpp17
-rw-r--r--src/core/hid/emulated_controller.h13
-rw-r--r--src/core/hid/hid_core.h1
-rw-r--r--src/core/hid/input_converter.cpp2
-rw-r--r--src/core/hle/ipc_helpers.h10
-rw-r--r--src/core/hle/kernel/board/nintendo/nx/k_memory_layout.h13
-rw-r--r--src/core/hle/kernel/board/nintendo/nx/k_system_control.cpp36
-rw-r--r--src/core/hle/kernel/board/nintendo/nx/k_system_control.h1
-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/initial_process.h23
-rw-r--r--src/core/hle/kernel/k_address_arbiter.cpp10
-rw-r--r--src/core/hle/kernel/k_auto_object.h7
-rw-r--r--src/core/hle/kernel/k_auto_object_container.h4
-rw-r--r--src/core/hle/kernel/k_code_memory.cpp6
-rw-r--r--src/core/hle/kernel/k_condition_variable.cpp2
-rw-r--r--src/core/hle/kernel/k_event.cpp14
-rw-r--r--src/core/hle/kernel/k_event.h2
-rw-r--r--src/core/hle/kernel/k_handle_table.cpp8
-rw-r--r--src/core/hle/kernel/k_handle_table.h37
-rw-r--r--src/core/hle/kernel/k_memory_layout.h10
-rw-r--r--src/core/hle/kernel/k_memory_manager.cpp469
-rw-r--r--src/core/hle/kernel/k_memory_manager.h203
-rw-r--r--src/core/hle/kernel/k_memory_region.h80
-rw-r--r--src/core/hle/kernel/k_memory_region_type.h10
-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_heap.cpp126
-rw-r--r--src/core/hle/kernel/k_page_heap.h261
-rw-r--r--src/core/hle/kernel/k_page_table.cpp938
-rw-r--r--src/core/hle/kernel/k_page_table.h54
-rw-r--r--src/core/hle/kernel/k_port.cpp7
-rw-r--r--src/core/hle/kernel/k_process.cpp205
-rw-r--r--src/core/hle/kernel/k_process.h20
-rw-r--r--src/core/hle/kernel/k_resource_limit.cpp19
-rw-r--r--src/core/hle/kernel/k_resource_limit.h3
-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.h231
-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.cpp198
-rw-r--r--src/core/hle/kernel/kernel.h26
-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.cpp176
-rw-r--r--src/core/hle/kernel/svc_types.h2
-rw-r--r--src/core/hle/kernel/svc_wrap.h22
-rw-r--r--src/core/hle/service/acc/acc.cpp42
-rw-r--r--src/core/hle/service/acc/profile_manager.cpp19
-rw-r--r--src/core/hle/service/acc/profile_manager.h6
-rw-r--r--src/core/hle/service/am/am.cpp16
-rw-r--r--src/core/hle/service/am/am.h36
-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/applet_profile_select.cpp4
-rw-r--r--src/core/hle/service/am/applets/applet_profile_select.h2
-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/apm/apm_controller.cpp10
-rw-r--r--src/core/hle/service/apm/apm_controller.h15
-rw-r--r--src/core/hle/service/friend/friend.cpp6
-rw-r--r--src/core/hle/service/hid/hid.cpp12
-rw-r--r--src/core/hle/service/hid/hid.h1
-rw-r--r--src/core/hle/service/kernel_helpers.cpp8
-rw-r--r--src/core/hle/service/ldr/ldr.cpp78
-rw-r--r--src/core/hle/service/mii/mii_manager.cpp12
-rw-r--r--src/core/hle/service/mii/mii_manager.h4
-rw-r--r--src/core/hle/service/mnpp/mnpp_app.cpp45
-rw-r--r--src/core/hle/service/mnpp/mnpp_app.h20
-rw-r--r--src/core/hle/service/nfp/nfp.cpp916
-rw-r--r--src/core/hle/service/nfp/nfp.h242
-rw-r--r--src/core/hle/service/ns/pdm_qry.cpp2
-rw-r--r--src/core/hle/service/pm/pm.cpp45
-rw-r--r--src/core/hle/service/service.cpp2
-rw-r--r--src/core/hle/service/sm/sm.cpp2
-rw-r--r--src/core/hle/service/sockets/bsd.cpp4
-rw-r--r--src/core/hle/service/time/clock_types.h2
-rw-r--r--src/core/hle/service/time/steady_clock_core.h2
-rw-r--r--src/core/hle/service/time/time_manager.cpp2
-rw-r--r--src/core/hle/service/vi/display/vi_display.h2
-rw-r--r--src/core/loader/loader.h6
-rw-r--r--src/core/memory.cpp3
-rw-r--r--src/input_common/drivers/gc_adapter.cpp2
-rw-r--r--src/input_common/drivers/keyboard.cpp6
-rw-r--r--src/input_common/drivers/mouse.cpp2
-rw-r--r--src/input_common/drivers/sdl_driver.cpp28
-rw-r--r--src/input_common/drivers/touch_screen.cpp2
-rw-r--r--src/input_common/drivers/udp_client.cpp18
-rw-r--r--src/input_common/drivers/udp_client.h4
-rw-r--r--src/input_common/helpers/stick_from_buttons.cpp30
-rw-r--r--src/input_common/helpers/touch_from_buttons.cpp1
-rw-r--r--src/input_common/input_engine.cpp18
-rw-r--r--src/input_common/input_engine.h22
-rw-r--r--src/input_common/input_mapping.cpp6
-rw-r--r--src/input_common/input_poller.cpp4
-rw-r--r--src/shader_recompiler/backend/glasm/emit_glasm_instructions.h31
-rw-r--r--src/shader_recompiler/backend/glasm/emit_glasm_memory.cpp107
-rw-r--r--src/shader_recompiler/backend/glsl/emit_glsl_atomic.cpp142
-rw-r--r--src/shader_recompiler/backend/glsl/emit_glsl_instructions.h31
-rw-r--r--src/shader_recompiler/backend/spirv/emit_spirv.h2
-rw-r--r--src/shader_recompiler/backend/spirv/emit_spirv_atomic.cpp119
-rw-r--r--src/shader_recompiler/backend/spirv/emit_spirv_context_get_set.cpp59
-rw-r--r--src/shader_recompiler/backend/spirv/emit_spirv_instructions.h30
-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/ir/microinstruction.cpp19
-rw-r--r--src/shader_recompiler/frontend/ir/opcodes.inc19
-rw-r--r--src/shader_recompiler/frontend/maxwell/translate/impl/load_constant.cpp14
-rw-r--r--src/shader_recompiler/frontend/maxwell/translate/impl/logic_operation_three_input.cpp580
-rw-r--r--src/shader_recompiler/frontend/maxwell/translate/impl/logic_operation_three_input_lut3.py92
-rw-r--r--src/shader_recompiler/frontend/maxwell/translate_program.cpp4
-rw-r--r--src/shader_recompiler/ir_opt/collect_shader_info_pass.cpp104
-rw-r--r--src/shader_recompiler/ir_opt/global_memory_to_storage_buffer_pass.cpp52
-rw-r--r--src/shader_recompiler/ir_opt/lower_int64_to_int32.cpp20
-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/engines/maxwell_dma.cpp20
-rw-r--r--src/video_core/engines/maxwell_dma.h2
-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_base.h8
-rw-r--r--src/video_core/renderer_opengl/gl_fence_manager.cpp3
-rw-r--r--src/video_core/renderer_opengl/gl_graphics_pipeline.cpp46
-rw-r--r--src/video_core/renderer_opengl/gl_graphics_pipeline.h7
-rw-r--r--src/video_core/renderer_opengl/gl_resource_manager.h50
-rw-r--r--src/video_core/renderer_opengl/gl_state_tracker.h1
-rw-r--r--src/video_core/renderer_opengl/maxwell_to_gl.h2
-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/maxwell_to_vk.cpp2
-rw-r--r--src/video_core/renderer_vulkan/vk_blit_screen.cpp2
-rw-r--r--src/video_core/renderer_vulkan/vk_compute_pass.cpp3
-rw-r--r--src/video_core/renderer_vulkan/vk_scheduler.cpp6
-rw-r--r--src/video_core/renderer_vulkan/vk_scheduler.h4
-rw-r--r--src/video_core/renderer_vulkan/vk_state_tracker.h1
-rw-r--r--src/video_core/renderer_vulkan/vk_texture_cache.cpp9
-rw-r--r--src/video_core/video_core.cpp1
-rw-r--r--src/video_core/vulkan_common/vulkan_device.cpp37
-rw-r--r--src/video_core/vulkan_common/vulkan_device.h5
-rw-r--r--src/yuzu/applets/qt_controller.cpp2
-rw-r--r--src/yuzu/applets/qt_profile_select.cpp4
-rw-r--r--src/yuzu/configuration/config.cpp20
-rw-r--r--src/yuzu/configuration/config.h2
-rw-r--r--src/yuzu/configuration/configure_cpu.cpp9
-rw-r--r--src/yuzu/configuration/configure_cpu.h1
-rw-r--r--src/yuzu/configuration/configure_cpu.ui12
-rw-r--r--src/yuzu/configuration/configure_cpu_debug.cpp8
-rw-r--r--src/yuzu/configuration/configure_cpu_debug.ui29
-rw-r--r--src/yuzu/configuration/configure_general.cpp11
-rw-r--r--src/yuzu/configuration/configure_general.h1
-rw-r--r--src/yuzu/configuration/configure_general.ui14
-rw-r--r--src/yuzu/configuration/configure_input.cpp2
-rw-r--r--src/yuzu/configuration/configure_input_player.cpp29
-rw-r--r--src/yuzu/configuration/configure_input_player_widget.cpp89
-rw-r--r--src/yuzu/configuration/configure_input_player_widget.h2
-rw-r--r--src/yuzu/configuration/configure_profile_manager.cpp10
-rw-r--r--src/yuzu/configuration/configure_touch_from_button.cpp3
-rw-r--r--src/yuzu/configuration/configure_vibration.cpp74
-rw-r--r--src/yuzu/configuration/configure_vibration.h18
-rw-r--r--src/yuzu/configuration/configure_vibration.ui7
-rw-r--r--src/yuzu/debugger/console.cpp1
-rw-r--r--src/yuzu/main.cpp176
-rw-r--r--src/yuzu/main.h4
-rw-r--r--src/yuzu/main.ui2
-rw-r--r--src/yuzu/uisettings.h1
-rw-r--r--src/yuzu_cmd/config.cpp50
-rw-r--r--src/yuzu_cmd/config.h5
-rw-r--r--src/yuzu_cmd/default_ini.h19
-rw-r--r--src/yuzu_cmd/yuzu.cpp22
216 files changed, 7431 insertions, 2744 deletions
diff --git a/src/audio_core/common.h b/src/audio_core/common.h
index 1ab537588..e6b95769f 100644
--- a/src/audio_core/common.h
+++ b/src/audio_core/common.h
@@ -15,7 +15,9 @@ constexpr ResultCode ERR_INVALID_PARAMETERS{ErrorModule::Audio, 41};
15constexpr ResultCode ERR_SPLITTER_SORT_FAILED{ErrorModule::Audio, 43}; 15constexpr ResultCode ERR_SPLITTER_SORT_FAILED{ErrorModule::Audio, 43};
16} // namespace Audren 16} // namespace Audren
17 17
18constexpr u32_le CURRENT_PROCESS_REVISION = Common::MakeMagic('R', 'E', 'V', '9'); 18constexpr u8 BASE_REVISION = '0';
19constexpr u32_le CURRENT_PROCESS_REVISION =
20 Common::MakeMagic('R', 'E', 'V', static_cast<u8>(BASE_REVISION + 0xA));
19constexpr std::size_t MAX_MIX_BUFFERS = 24; 21constexpr std::size_t MAX_MIX_BUFFERS = 24;
20constexpr std::size_t MAX_BIQUAD_FILTERS = 2; 22constexpr std::size_t MAX_BIQUAD_FILTERS = 2;
21constexpr std::size_t MAX_CHANNEL_COUNT = 6; 23constexpr std::size_t MAX_CHANNEL_COUNT = 6;
diff --git a/src/common/bit_util.h b/src/common/bit_util.h
index f50d3308a..f37538e06 100644
--- a/src/common/bit_util.h
+++ b/src/common/bit_util.h
@@ -57,4 +57,11 @@ requires std::is_integral_v<T>
57 return static_cast<T>(1ULL << ((8U * sizeof(T)) - std::countl_zero(value - 1U))); 57 return static_cast<T>(1ULL << ((8U * sizeof(T)) - std::countl_zero(value - 1U)));
58} 58}
59 59
60template <size_t bit_index, typename T>
61requires std::is_integral_v<T>
62[[nodiscard]] constexpr bool Bit(const T value) {
63 static_assert(bit_index < BitSize<T>(), "bit_index must be smaller than size of T");
64 return ((value >> bit_index) & T(1)) == T(1);
65}
66
60} // namespace Common 67} // namespace Common
diff --git a/src/common/common_types.h b/src/common/common_types.h
index 4cec89fbd..99bffc460 100644
--- a/src/common/common_types.h
+++ b/src/common/common_types.h
@@ -46,13 +46,3 @@ using GPUVAddr = u64; ///< Represents a pointer in the GPU virtual address space
46 46
47using u128 = std::array<std::uint64_t, 2>; 47using u128 = std::array<std::uint64_t, 2>;
48static_assert(sizeof(u128) == 16, "u128 must be 128 bits wide"); 48static_assert(sizeof(u128) == 16, "u128 must be 128 bits wide");
49
50// An inheritable class to disallow the copy constructor and operator= functions
51class NonCopyable {
52protected:
53 constexpr NonCopyable() = default;
54 ~NonCopyable() = default;
55
56 NonCopyable(const NonCopyable&) = delete;
57 NonCopyable& operator=(const NonCopyable&) = delete;
58};
diff --git a/src/common/fs/fs_util.cpp b/src/common/fs/fs_util.cpp
index 9f8671982..0068112e6 100644
--- a/src/common/fs/fs_util.cpp
+++ b/src/common/fs/fs_util.cpp
@@ -16,6 +16,10 @@ std::u8string BufferToU8String(std::span<const u8> buffer) {
16 return std::u8string{buffer.begin(), std::ranges::find(buffer, u8{0})}; 16 return std::u8string{buffer.begin(), std::ranges::find(buffer, u8{0})};
17} 17}
18 18
19std::u8string_view BufferToU8StringView(std::span<const u8> buffer) {
20 return std::u8string_view{reinterpret_cast<const char8_t*>(buffer.data())};
21}
22
19std::string ToUTF8String(std::u8string_view u8_string) { 23std::string ToUTF8String(std::u8string_view u8_string) {
20 return std::string{u8_string.begin(), u8_string.end()}; 24 return std::string{u8_string.begin(), u8_string.end()};
21} 25}
@@ -24,6 +28,10 @@ std::string BufferToUTF8String(std::span<const u8> buffer) {
24 return std::string{buffer.begin(), std::ranges::find(buffer, u8{0})}; 28 return std::string{buffer.begin(), std::ranges::find(buffer, u8{0})};
25} 29}
26 30
31std::string_view BufferToUTF8StringView(std::span<const u8> buffer) {
32 return std::string_view{reinterpret_cast<const char*>(buffer.data())};
33}
34
27std::string PathToUTF8String(const std::filesystem::path& path) { 35std::string PathToUTF8String(const std::filesystem::path& path) {
28 return ToUTF8String(path.u8string()); 36 return ToUTF8String(path.u8string());
29} 37}
diff --git a/src/common/fs/fs_util.h b/src/common/fs/fs_util.h
index 1ec82eb35..1620d38c9 100644
--- a/src/common/fs/fs_util.h
+++ b/src/common/fs/fs_util.h
@@ -38,6 +38,15 @@ concept IsChar = std::same_as<T, char>;
38[[nodiscard]] std::u8string BufferToU8String(std::span<const u8> buffer); 38[[nodiscard]] std::u8string BufferToU8String(std::span<const u8> buffer);
39 39
40/** 40/**
41 * Same as BufferToU8String, but returns a string view of the buffer.
42 *
43 * @param buffer Buffer of bytes
44 *
45 * @returns UTF-8 encoded std::u8string_view.
46 */
47[[nodiscard]] std::u8string_view BufferToU8StringView(std::span<const u8> buffer);
48
49/**
41 * Converts a std::u8string or std::u8string_view to a UTF-8 encoded std::string. 50 * Converts a std::u8string or std::u8string_view to a UTF-8 encoded std::string.
42 * 51 *
43 * @param u8_string UTF-8 encoded u8string 52 * @param u8_string UTF-8 encoded u8string
@@ -58,6 +67,15 @@ concept IsChar = std::same_as<T, char>;
58[[nodiscard]] std::string BufferToUTF8String(std::span<const u8> buffer); 67[[nodiscard]] std::string BufferToUTF8String(std::span<const u8> buffer);
59 68
60/** 69/**
70 * Same as BufferToUTF8String, but returns a string view of the buffer.
71 *
72 * @param buffer Buffer of bytes
73 *
74 * @returns UTF-8 encoded std::string_view.
75 */
76[[nodiscard]] std::string_view BufferToUTF8StringView(std::span<const u8> buffer);
77
78/**
61 * Converts a filesystem path to a UTF-8 encoded std::string. 79 * Converts a filesystem path to a UTF-8 encoded std::string.
62 * 80 *
63 * @param path Filesystem path 81 * @param path Filesystem path
diff --git a/src/common/host_memory.cpp b/src/common/host_memory.cpp
index 28949fe5e..c465cfc14 100644
--- a/src/common/host_memory.cpp
+++ b/src/common/host_memory.cpp
@@ -327,8 +327,8 @@ private:
327 bool IsNiechePlaceholder(size_t virtual_offset, size_t length) const { 327 bool IsNiechePlaceholder(size_t virtual_offset, size_t length) const {
328 const auto it = placeholders.upper_bound({virtual_offset, virtual_offset + length}); 328 const auto it = placeholders.upper_bound({virtual_offset, virtual_offset + length});
329 if (it != placeholders.end() && it->lower() == virtual_offset + length) { 329 if (it != placeholders.end() && it->lower() == virtual_offset + length) {
330 const bool is_root = it == placeholders.begin() && virtual_offset == 0; 330 return it == placeholders.begin() ? virtual_offset == 0
331 return is_root || std::prev(it)->upper() == virtual_offset; 331 : std::prev(it)->upper() == virtual_offset;
332 } 332 }
333 return false; 333 return false;
334 } 334 }
diff --git a/src/common/intrusive_red_black_tree.h b/src/common/intrusive_red_black_tree.h
index 3173cc449..b296b639e 100644
--- a/src/common/intrusive_red_black_tree.h
+++ b/src/common/intrusive_red_black_tree.h
@@ -4,6 +4,8 @@
4 4
5#pragma once 5#pragma once
6 6
7#include "common/alignment.h"
8#include "common/common_funcs.h"
7#include "common/parent_of_member.h" 9#include "common/parent_of_member.h"
8#include "common/tree.h" 10#include "common/tree.h"
9 11
@@ -15,32 +17,33 @@ class IntrusiveRedBlackTreeImpl;
15 17
16} 18}
17 19
20#pragma pack(push, 4)
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/logging/backend.cpp b/src/common/logging/backend.cpp
index c51c05b28..4a2462ec4 100644
--- a/src/common/logging/backend.cpp
+++ b/src/common/logging/backend.cpp
@@ -218,19 +218,17 @@ private:
218 Impl(const std::filesystem::path& file_backend_filename, const Filter& filter_) 218 Impl(const std::filesystem::path& file_backend_filename, const Filter& filter_)
219 : filter{filter_}, file_backend{file_backend_filename} {} 219 : filter{filter_}, file_backend{file_backend_filename} {}
220 220
221 ~Impl() { 221 ~Impl() = default;
222 StopBackendThread();
223 }
224 222
225 void StartBackendThread() { 223 void StartBackendThread() {
226 backend_thread = std::thread([this] { 224 backend_thread = std::jthread([this](std::stop_token stop_token) {
227 Common::SetCurrentThreadName("yuzu:Log"); 225 Common::SetCurrentThreadName("yuzu:Log");
228 Entry entry; 226 Entry entry;
229 const auto write_logs = [this, &entry]() { 227 const auto write_logs = [this, &entry]() {
230 ForEachBackend([&entry](Backend& backend) { backend.Write(entry); }); 228 ForEachBackend([&entry](Backend& backend) { backend.Write(entry); });
231 }; 229 };
232 while (!stop.stop_requested()) { 230 while (!stop_token.stop_requested()) {
233 entry = message_queue.PopWait(stop.get_token()); 231 entry = message_queue.PopWait(stop_token);
234 if (entry.filename != nullptr) { 232 if (entry.filename != nullptr) {
235 write_logs(); 233 write_logs();
236 } 234 }
@@ -244,11 +242,6 @@ private:
244 }); 242 });
245 } 243 }
246 244
247 void StopBackendThread() {
248 stop.request_stop();
249 backend_thread.join();
250 }
251
252 Entry CreateEntry(Class log_class, Level log_level, const char* filename, unsigned int line_nr, 245 Entry CreateEntry(Class log_class, Level log_level, const char* filename, unsigned int line_nr,
253 const char* function, std::string&& message) const { 246 const char* function, std::string&& message) const {
254 using std::chrono::duration_cast; 247 using std::chrono::duration_cast;
@@ -283,10 +276,9 @@ private:
283 ColorConsoleBackend color_console_backend{}; 276 ColorConsoleBackend color_console_backend{};
284 FileBackend file_backend; 277 FileBackend file_backend;
285 278
286 std::stop_source stop;
287 std::thread backend_thread;
288 MPSCQueue<Entry, true> message_queue{}; 279 MPSCQueue<Entry, true> message_queue{};
289 std::chrono::steady_clock::time_point time_origin{std::chrono::steady_clock::now()}; 280 std::chrono::steady_clock::time_point time_origin{std::chrono::steady_clock::now()};
281 std::jthread backend_thread;
290}; 282};
291} // namespace 283} // namespace
292 284
diff --git a/src/common/logging/filter.cpp b/src/common/logging/filter.cpp
index b898a652c..4afc1369a 100644
--- a/src/common/logging/filter.cpp
+++ b/src/common/logging/filter.cpp
@@ -108,6 +108,7 @@ bool ParseFilterRule(Filter& instance, Iterator begin, Iterator end) {
108 SUB(Service, Migration) \ 108 SUB(Service, Migration) \
109 SUB(Service, Mii) \ 109 SUB(Service, Mii) \
110 SUB(Service, MM) \ 110 SUB(Service, MM) \
111 SUB(Service, MNPP) \
111 SUB(Service, NCM) \ 112 SUB(Service, NCM) \
112 SUB(Service, NFC) \ 113 SUB(Service, NFC) \
113 SUB(Service, NFP) \ 114 SUB(Service, NFP) \
diff --git a/src/common/logging/types.h b/src/common/logging/types.h
index 9ed0c7ad6..2b6e4daa7 100644
--- a/src/common/logging/types.h
+++ b/src/common/logging/types.h
@@ -76,6 +76,7 @@ enum class Class : u8 {
76 Service_Migration, ///< The migration service 76 Service_Migration, ///< The migration service
77 Service_Mii, ///< The Mii service 77 Service_Mii, ///< The Mii service
78 Service_MM, ///< The MM (Multimedia) service 78 Service_MM, ///< The MM (Multimedia) service
79 Service_MNPP, ///< The MNPP service
79 Service_NCM, ///< The NCM service 80 Service_NCM, ///< The NCM service
80 Service_NFC, ///< The NFC (Near-field communication) service 81 Service_NFC, ///< The NFC (Near-field communication) service
81 Service_NFP, ///< The NFP service 82 Service_NFP, ///< The NFP service
diff --git a/src/common/page_table.cpp b/src/common/page_table.cpp
index 9fffd816f..4817b09f9 100644
--- a/src/common/page_table.cpp
+++ b/src/common/page_table.cpp
@@ -10,11 +10,65 @@ PageTable::PageTable() = default;
10 10
11PageTable::~PageTable() noexcept = default; 11PageTable::~PageTable() noexcept = default;
12 12
13void PageTable::Resize(size_t address_space_width_in_bits, size_t page_size_in_bits) { 13bool PageTable::BeginTraversal(TraversalEntry& out_entry, TraversalContext& out_context,
14 const size_t num_page_table_entries{1ULL << (address_space_width_in_bits - page_size_in_bits)}; 14 u64 address) const {
15 // Setup invalid defaults.
16 out_entry.phys_addr = 0;
17 out_entry.block_size = page_size;
18 out_context.next_page = 0;
19
20 // Validate that we can read the actual entry.
21 const auto page = address / page_size;
22 if (page >= backing_addr.size()) {
23 return false;
24 }
25
26 // Validate that the entry is mapped.
27 const auto phys_addr = backing_addr[page];
28 if (phys_addr == 0) {
29 return false;
30 }
31
32 // Populate the results.
33 out_entry.phys_addr = phys_addr + address;
34 out_context.next_page = page + 1;
35 out_context.next_offset = address + page_size;
36
37 return true;
38}
39
40bool PageTable::ContinueTraversal(TraversalEntry& out_entry, TraversalContext& context) const {
41 // Setup invalid defaults.
42 out_entry.phys_addr = 0;
43 out_entry.block_size = page_size;
44
45 // Validate that we can read the actual entry.
46 const auto page = context.next_page;
47 if (page >= backing_addr.size()) {
48 return false;
49 }
50
51 // Validate that the entry is mapped.
52 const auto phys_addr = backing_addr[page];
53 if (phys_addr == 0) {
54 return false;
55 }
56
57 // Populate the results.
58 out_entry.phys_addr = phys_addr + context.next_offset;
59 context.next_page = page + 1;
60 context.next_offset += page_size;
61
62 return true;
63}
64
65void PageTable::Resize(std::size_t address_space_width_in_bits, std::size_t page_size_in_bits) {
66 const std::size_t num_page_table_entries{1ULL
67 << (address_space_width_in_bits - page_size_in_bits)};
15 pointers.resize(num_page_table_entries); 68 pointers.resize(num_page_table_entries);
16 backing_addr.resize(num_page_table_entries); 69 backing_addr.resize(num_page_table_entries);
17 current_address_space_width_in_bits = address_space_width_in_bits; 70 current_address_space_width_in_bits = address_space_width_in_bits;
71 page_size = 1ULL << page_size_in_bits;
18} 72}
19 73
20} // namespace Common 74} // namespace Common
diff --git a/src/common/page_table.h b/src/common/page_table.h
index 8267e8b4d..82d91e9f3 100644
--- a/src/common/page_table.h
+++ b/src/common/page_table.h
@@ -27,6 +27,16 @@ enum class PageType : u8 {
27 * mimics the way a real CPU page table works. 27 * mimics the way a real CPU page table works.
28 */ 28 */
29struct PageTable { 29struct PageTable {
30 struct TraversalEntry {
31 u64 phys_addr{};
32 std::size_t block_size{};
33 };
34
35 struct TraversalContext {
36 u64 next_page{};
37 u64 next_offset{};
38 };
39
30 /// Number of bits reserved for attribute tagging. 40 /// Number of bits reserved for attribute tagging.
31 /// This can be at most the guaranteed alignment of the pointers in the page table. 41 /// This can be at most the guaranteed alignment of the pointers in the page table.
32 static constexpr int ATTRIBUTE_BITS = 2; 42 static constexpr int ATTRIBUTE_BITS = 2;
@@ -89,6 +99,10 @@ struct PageTable {
89 PageTable(PageTable&&) noexcept = default; 99 PageTable(PageTable&&) noexcept = default;
90 PageTable& operator=(PageTable&&) noexcept = default; 100 PageTable& operator=(PageTable&&) noexcept = default;
91 101
102 bool BeginTraversal(TraversalEntry& out_entry, TraversalContext& out_context,
103 u64 address) const;
104 bool ContinueTraversal(TraversalEntry& out_entry, TraversalContext& context) const;
105
92 /** 106 /**
93 * Resizes the page table to be able to accommodate enough pages within 107 * Resizes the page table to be able to accommodate enough pages within
94 * a given address space. 108 * a given address space.
@@ -96,9 +110,9 @@ struct PageTable {
96 * @param address_space_width_in_bits The address size width in bits. 110 * @param address_space_width_in_bits The address size width in bits.
97 * @param page_size_in_bits The page size in bits. 111 * @param page_size_in_bits The page size in bits.
98 */ 112 */
99 void Resize(size_t address_space_width_in_bits, size_t page_size_in_bits); 113 void Resize(std::size_t address_space_width_in_bits, std::size_t page_size_in_bits);
100 114
101 size_t GetAddressSpaceBits() const { 115 std::size_t GetAddressSpaceBits() const {
102 return current_address_space_width_in_bits; 116 return current_address_space_width_in_bits;
103 } 117 }
104 118
@@ -110,9 +124,11 @@ struct PageTable {
110 124
111 VirtualBuffer<u64> backing_addr; 125 VirtualBuffer<u64> backing_addr;
112 126
113 size_t current_address_space_width_in_bits; 127 std::size_t current_address_space_width_in_bits{};
128
129 u8* fastmem_arena{};
114 130
115 u8* fastmem_arena; 131 std::size_t page_size{};
116}; 132};
117 133
118} // namespace Common 134} // namespace Common
diff --git a/src/common/settings.cpp b/src/common/settings.cpp
index 6964a8273..877e0faa4 100644
--- a/src/common/settings.cpp
+++ b/src/common/settings.cpp
@@ -167,6 +167,7 @@ void RestoreGlobalState(bool is_powered_on) {
167 167
168 // Core 168 // Core
169 values.use_multi_core.SetGlobal(true); 169 values.use_multi_core.SetGlobal(true);
170 values.use_extended_memory_layout.SetGlobal(true);
170 171
171 // CPU 172 // CPU
172 values.cpu_accuracy.SetGlobal(true); 173 values.cpu_accuracy.SetGlobal(true);
@@ -175,6 +176,7 @@ void RestoreGlobalState(bool is_powered_on) {
175 values.cpuopt_unsafe_ignore_standard_fpcr.SetGlobal(true); 176 values.cpuopt_unsafe_ignore_standard_fpcr.SetGlobal(true);
176 values.cpuopt_unsafe_inaccurate_nan.SetGlobal(true); 177 values.cpuopt_unsafe_inaccurate_nan.SetGlobal(true);
177 values.cpuopt_unsafe_fastmem_check.SetGlobal(true); 178 values.cpuopt_unsafe_fastmem_check.SetGlobal(true);
179 values.cpuopt_unsafe_ignore_global_monitor.SetGlobal(true);
178 180
179 // Renderer 181 // Renderer
180 values.renderer_backend.SetGlobal(true); 182 values.renderer_backend.SetGlobal(true);
diff --git a/src/common/settings.h b/src/common/settings.h
index 9bee6e10f..a37d83fb3 100644
--- a/src/common/settings.h
+++ b/src/common/settings.h
@@ -466,6 +466,7 @@ struct Values {
466 466
467 // Core 467 // Core
468 Setting<bool> use_multi_core{true, "use_multi_core"}; 468 Setting<bool> use_multi_core{true, "use_multi_core"};
469 Setting<bool> use_extended_memory_layout{false, "use_extended_memory_layout"};
469 470
470 // Cpu 471 // Cpu
471 RangedSetting<CPUAccuracy> cpu_accuracy{CPUAccuracy::Auto, CPUAccuracy::Auto, 472 RangedSetting<CPUAccuracy> cpu_accuracy{CPUAccuracy::Auto, CPUAccuracy::Auto,
@@ -483,12 +484,15 @@ struct Values {
483 BasicSetting<bool> cpuopt_misc_ir{true, "cpuopt_misc_ir"}; 484 BasicSetting<bool> cpuopt_misc_ir{true, "cpuopt_misc_ir"};
484 BasicSetting<bool> cpuopt_reduce_misalign_checks{true, "cpuopt_reduce_misalign_checks"}; 485 BasicSetting<bool> cpuopt_reduce_misalign_checks{true, "cpuopt_reduce_misalign_checks"};
485 BasicSetting<bool> cpuopt_fastmem{true, "cpuopt_fastmem"}; 486 BasicSetting<bool> cpuopt_fastmem{true, "cpuopt_fastmem"};
487 BasicSetting<bool> cpuopt_fastmem_exclusives{true, "cpuopt_fastmem_exclusives"};
488 BasicSetting<bool> cpuopt_recompile_exclusives{true, "cpuopt_recompile_exclusives"};
486 489
487 Setting<bool> cpuopt_unsafe_unfuse_fma{true, "cpuopt_unsafe_unfuse_fma"}; 490 Setting<bool> cpuopt_unsafe_unfuse_fma{true, "cpuopt_unsafe_unfuse_fma"};
488 Setting<bool> cpuopt_unsafe_reduce_fp_error{true, "cpuopt_unsafe_reduce_fp_error"}; 491 Setting<bool> cpuopt_unsafe_reduce_fp_error{true, "cpuopt_unsafe_reduce_fp_error"};
489 Setting<bool> cpuopt_unsafe_ignore_standard_fpcr{true, "cpuopt_unsafe_ignore_standard_fpcr"}; 492 Setting<bool> cpuopt_unsafe_ignore_standard_fpcr{true, "cpuopt_unsafe_ignore_standard_fpcr"};
490 Setting<bool> cpuopt_unsafe_inaccurate_nan{true, "cpuopt_unsafe_inaccurate_nan"}; 493 Setting<bool> cpuopt_unsafe_inaccurate_nan{true, "cpuopt_unsafe_inaccurate_nan"};
491 Setting<bool> cpuopt_unsafe_fastmem_check{true, "cpuopt_unsafe_fastmem_check"}; 494 Setting<bool> cpuopt_unsafe_fastmem_check{true, "cpuopt_unsafe_fastmem_check"};
495 Setting<bool> cpuopt_unsafe_ignore_global_monitor{true, "cpuopt_unsafe_ignore_global_monitor"};
492 496
493 // Renderer 497 // Renderer
494 RangedSetting<RendererBackend> renderer_backend{ 498 RangedSetting<RendererBackend> renderer_backend{
diff --git a/src/common/telemetry.cpp b/src/common/telemetry.cpp
index 6241d08b3..98c82cd17 100644
--- a/src/common/telemetry.cpp
+++ b/src/common/telemetry.cpp
@@ -55,22 +55,50 @@ void AppendBuildInfo(FieldCollection& fc) {
55 55
56void AppendCPUInfo(FieldCollection& fc) { 56void AppendCPUInfo(FieldCollection& fc) {
57#ifdef ARCHITECTURE_x86_64 57#ifdef ARCHITECTURE_x86_64
58 fc.AddField(FieldType::UserSystem, "CPU_Model", Common::GetCPUCaps().cpu_string); 58
59 fc.AddField(FieldType::UserSystem, "CPU_BrandString", Common::GetCPUCaps().brand_string); 59 const auto& caps = Common::GetCPUCaps();
60 fc.AddField(FieldType::UserSystem, "CPU_Extension_x64_AES", Common::GetCPUCaps().aes); 60 const auto add_field = [&fc](std::string_view field_name, const auto& field_value) {
61 fc.AddField(FieldType::UserSystem, "CPU_Extension_x64_AVX", Common::GetCPUCaps().avx); 61 fc.AddField(FieldType::UserSystem, field_name, field_value);
62 fc.AddField(FieldType::UserSystem, "CPU_Extension_x64_AVX2", Common::GetCPUCaps().avx2); 62 };
63 fc.AddField(FieldType::UserSystem, "CPU_Extension_x64_AVX512", Common::GetCPUCaps().avx512); 63 add_field("CPU_Model", caps.cpu_string);
64 fc.AddField(FieldType::UserSystem, "CPU_Extension_x64_BMI1", Common::GetCPUCaps().bmi1); 64 add_field("CPU_BrandString", caps.brand_string);
65 fc.AddField(FieldType::UserSystem, "CPU_Extension_x64_BMI2", Common::GetCPUCaps().bmi2); 65
66 fc.AddField(FieldType::UserSystem, "CPU_Extension_x64_FMA", Common::GetCPUCaps().fma); 66 add_field("CPU_Extension_x64_SSE", caps.sse);
67 fc.AddField(FieldType::UserSystem, "CPU_Extension_x64_FMA4", Common::GetCPUCaps().fma4); 67 add_field("CPU_Extension_x64_SSE2", caps.sse2);
68 fc.AddField(FieldType::UserSystem, "CPU_Extension_x64_SSE", Common::GetCPUCaps().sse); 68 add_field("CPU_Extension_x64_SSE3", caps.sse3);
69 fc.AddField(FieldType::UserSystem, "CPU_Extension_x64_SSE2", Common::GetCPUCaps().sse2); 69 add_field("CPU_Extension_x64_SSSE3", caps.ssse3);
70 fc.AddField(FieldType::UserSystem, "CPU_Extension_x64_SSE3", Common::GetCPUCaps().sse3); 70 add_field("CPU_Extension_x64_SSE41", caps.sse4_1);
71 fc.AddField(FieldType::UserSystem, "CPU_Extension_x64_SSSE3", Common::GetCPUCaps().ssse3); 71 add_field("CPU_Extension_x64_SSE42", caps.sse4_2);
72 fc.AddField(FieldType::UserSystem, "CPU_Extension_x64_SSE41", Common::GetCPUCaps().sse4_1); 72
73 fc.AddField(FieldType::UserSystem, "CPU_Extension_x64_SSE42", Common::GetCPUCaps().sse4_2); 73 add_field("CPU_Extension_x64_AVX", caps.avx);
74 add_field("CPU_Extension_x64_AVX_VNNI", caps.avx_vnni);
75 add_field("CPU_Extension_x64_AVX2", caps.avx2);
76
77 // Skylake-X/SP level AVX512, for compatibility with the previous telemetry field
78 add_field("CPU_Extension_x64_AVX512",
79 caps.avx512f && caps.avx512cd && caps.avx512vl && caps.avx512dq && caps.avx512bw);
80
81 add_field("CPU_Extension_x64_AVX512F", caps.avx512f);
82 add_field("CPU_Extension_x64_AVX512CD", caps.avx512cd);
83 add_field("CPU_Extension_x64_AVX512VL", caps.avx512vl);
84 add_field("CPU_Extension_x64_AVX512DQ", caps.avx512dq);
85 add_field("CPU_Extension_x64_AVX512BW", caps.avx512bw);
86 add_field("CPU_Extension_x64_AVX512BITALG", caps.avx512bitalg);
87 add_field("CPU_Extension_x64_AVX512VBMI", caps.avx512vbmi);
88
89 add_field("CPU_Extension_x64_AES", caps.aes);
90 add_field("CPU_Extension_x64_BMI1", caps.bmi1);
91 add_field("CPU_Extension_x64_BMI2", caps.bmi2);
92 add_field("CPU_Extension_x64_F16C", caps.f16c);
93 add_field("CPU_Extension_x64_FMA", caps.fma);
94 add_field("CPU_Extension_x64_FMA4", caps.fma4);
95 add_field("CPU_Extension_x64_GFNI", caps.gfni);
96 add_field("CPU_Extension_x64_INVARIANT_TSC", caps.invariant_tsc);
97 add_field("CPU_Extension_x64_LZCNT", caps.lzcnt);
98 add_field("CPU_Extension_x64_MOVBE", caps.movbe);
99 add_field("CPU_Extension_x64_PCLMULQDQ", caps.pclmulqdq);
100 add_field("CPU_Extension_x64_POPCNT", caps.popcnt);
101 add_field("CPU_Extension_x64_SHA", caps.sha);
74#else 102#else
75 fc.AddField(FieldType::UserSystem, "CPU_Model", "Other"); 103 fc.AddField(FieldType::UserSystem, "CPU_Model", "Other");
76#endif 104#endif
diff --git a/src/common/telemetry.h b/src/common/telemetry.h
index 49186e848..3524c857e 100644
--- a/src/common/telemetry.h
+++ b/src/common/telemetry.h
@@ -8,6 +8,8 @@
8#include <map> 8#include <map>
9#include <memory> 9#include <memory>
10#include <string> 10#include <string>
11#include <string_view>
12#include "common/common_funcs.h"
11#include "common/common_types.h" 13#include "common/common_types.h"
12 14
13namespace Common::Telemetry { 15namespace Common::Telemetry {
@@ -28,7 +30,7 @@ struct VisitorInterface;
28/** 30/**
29 * Interface class for telemetry data fields. 31 * Interface class for telemetry data fields.
30 */ 32 */
31class FieldInterface : NonCopyable { 33class FieldInterface {
32public: 34public:
33 virtual ~FieldInterface() = default; 35 virtual ~FieldInterface() = default;
34 36
@@ -52,14 +54,15 @@ public:
52template <typename T> 54template <typename T>
53class Field : public FieldInterface { 55class Field : public FieldInterface {
54public: 56public:
55 Field(FieldType type_, std::string name_, T value_) 57 YUZU_NON_COPYABLE(Field);
56 : name(std::move(name_)), type(type_), value(std::move(value_)) {}
57 58
58 Field(const Field&) = default; 59 Field(FieldType type_, std::string_view name_, T value_)
59 Field& operator=(const Field&) = default; 60 : name(name_), type(type_), value(std::move(value_)) {}
60 61
61 Field(Field&&) = default; 62 ~Field() override = default;
62 Field& operator=(Field&& other) = default; 63
64 Field(Field&&) noexcept = default;
65 Field& operator=(Field&& other) noexcept = default;
63 66
64 void Accept(VisitorInterface& visitor) const override; 67 void Accept(VisitorInterface& visitor) const override;
65 68
@@ -98,9 +101,15 @@ private:
98/** 101/**
99 * Collection of data fields that have been logged. 102 * Collection of data fields that have been logged.
100 */ 103 */
101class FieldCollection final : NonCopyable { 104class FieldCollection final {
102public: 105public:
106 YUZU_NON_COPYABLE(FieldCollection);
107
103 FieldCollection() = default; 108 FieldCollection() = default;
109 ~FieldCollection() = default;
110
111 FieldCollection(FieldCollection&&) noexcept = default;
112 FieldCollection& operator=(FieldCollection&&) noexcept = default;
104 113
105 /** 114 /**
106 * Accept method for the visitor pattern, visits each field in the collection. 115 * Accept method for the visitor pattern, visits each field in the collection.
@@ -115,7 +124,7 @@ public:
115 * @param value Value for the field to add. 124 * @param value Value for the field to add.
116 */ 125 */
117 template <typename T> 126 template <typename T>
118 void AddField(FieldType type, const char* name, T value) { 127 void AddField(FieldType type, std::string_view name, T value) {
119 return AddField(std::make_unique<Field<T>>(type, name, std::move(value))); 128 return AddField(std::make_unique<Field<T>>(type, name, std::move(value)));
120 } 129 }
121 130
@@ -133,7 +142,7 @@ private:
133 * Telemetry fields visitor interface class. A backend to log to a web service should implement 142 * Telemetry fields visitor interface class. A backend to log to a web service should implement
134 * this interface. 143 * this interface.
135 */ 144 */
136struct VisitorInterface : NonCopyable { 145struct VisitorInterface {
137 virtual ~VisitorInterface() = default; 146 virtual ~VisitorInterface() = default;
138 147
139 virtual void Visit(const Field<bool>& field) = 0; 148 virtual void Visit(const Field<bool>& field) = 0;
@@ -160,8 +169,11 @@ struct VisitorInterface : NonCopyable {
160 * Empty implementation of VisitorInterface that drops all fields. Used when a functional 169 * Empty implementation of VisitorInterface that drops all fields. Used when a functional
161 * backend implementation is not available. 170 * backend implementation is not available.
162 */ 171 */
163struct NullVisitor : public VisitorInterface { 172struct NullVisitor final : public VisitorInterface {
164 ~NullVisitor() = default; 173 YUZU_NON_COPYABLE(NullVisitor);
174
175 NullVisitor() = default;
176 ~NullVisitor() override = default;
165 177
166 void Visit(const Field<bool>& /*field*/) override {} 178 void Visit(const Field<bool>& /*field*/) override {}
167 void Visit(const Field<double>& /*field*/) override {} 179 void Visit(const Field<double>& /*field*/) override {}
diff --git a/src/common/tree.h b/src/common/tree.h
index 18faa4a48..28370e343 100644
--- a/src/common/tree.h
+++ b/src/common/tree.h
@@ -43,294 +43,265 @@
43 * The maximum height of a red-black tree is 2lg (n+1). 43 * The maximum height of a red-black tree is 2lg (n+1).
44 */ 44 */
45 45
46#include "common/assert.h" 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/common/uuid.cpp b/src/common/uuid.cpp
index d7435a6e9..2b6a530e3 100644
--- a/src/common/uuid.cpp
+++ b/src/common/uuid.cpp
@@ -1,23 +1,25 @@
1// Copyright 2018 yuzu Emulator Project 1// Copyright 2022 yuzu Emulator Project
2// Licensed under GPLv2 or any later version 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <bit>
6#include <optional>
5#include <random> 7#include <random>
6 8
7#include <fmt/format.h> 9#include <fmt/format.h>
8 10
9#include "common/assert.h" 11#include "common/assert.h"
12#include "common/tiny_mt.h"
10#include "common/uuid.h" 13#include "common/uuid.h"
11 14
12namespace Common { 15namespace Common {
13 16
14namespace { 17namespace {
15 18
16bool IsHexDigit(char c) { 19constexpr size_t RawStringSize = sizeof(UUID) * 2;
17 return (c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F'); 20constexpr size_t FormattedStringSize = RawStringSize + 4;
18}
19 21
20u8 HexCharToByte(char c) { 22std::optional<u8> HexCharToByte(char c) {
21 if (c >= '0' && c <= '9') { 23 if (c >= '0' && c <= '9') {
22 return static_cast<u8>(c - '0'); 24 return static_cast<u8>(c - '0');
23 } 25 }
@@ -28,60 +30,184 @@ u8 HexCharToByte(char c) {
28 return static_cast<u8>(c - 'A' + 10); 30 return static_cast<u8>(c - 'A' + 10);
29 } 31 }
30 ASSERT_MSG(false, "{} is not a hexadecimal digit!", c); 32 ASSERT_MSG(false, "{} is not a hexadecimal digit!", c);
31 return u8{0}; 33 return std::nullopt;
32} 34}
33 35
34} // Anonymous namespace 36std::array<u8, 0x10> ConstructFromRawString(std::string_view raw_string) {
37 std::array<u8, 0x10> uuid;
38
39 for (size_t i = 0; i < RawStringSize; i += 2) {
40 const auto upper = HexCharToByte(raw_string[i]);
41 const auto lower = HexCharToByte(raw_string[i + 1]);
42 if (!upper || !lower) {
43 return {};
44 }
45 uuid[i / 2] = static_cast<u8>((*upper << 4) | *lower);
46 }
47
48 return uuid;
49}
35 50
36u128 HexStringToU128(std::string_view hex_string) { 51std::array<u8, 0x10> ConstructFromFormattedString(std::string_view formatted_string) {
37 const size_t length = hex_string.length(); 52 std::array<u8, 0x10> uuid;
38 53
39 // Detect "0x" prefix. 54 size_t i = 0;
40 const bool has_0x_prefix = length > 2 && hex_string[0] == '0' && hex_string[1] == 'x';
41 const size_t offset = has_0x_prefix ? 2 : 0;
42 55
43 // Check length. 56 // Process the first 8 characters.
44 if (length > 32 + offset) { 57 const auto* str = formatted_string.data();
45 ASSERT_MSG(false, "hex_string has more than 32 hexadecimal characters!"); 58
46 return INVALID_UUID; 59 for (; i < 4; ++i) {
60 const auto upper = HexCharToByte(*(str++));
61 const auto lower = HexCharToByte(*(str++));
62 if (!upper || !lower) {
63 return {};
64 }
65 uuid[i] = static_cast<u8>((*upper << 4) | *lower);
66 }
67
68 // Process the next 4 characters.
69 ++str;
70
71 for (; i < 6; ++i) {
72 const auto upper = HexCharToByte(*(str++));
73 const auto lower = HexCharToByte(*(str++));
74 if (!upper || !lower) {
75 return {};
76 }
77 uuid[i] = static_cast<u8>((*upper << 4) | *lower);
47 } 78 }
48 79
49 u64 lo = 0; 80 // Process the next 4 characters.
50 u64 hi = 0; 81 ++str;
51 for (size_t i = 0; i < length - offset; ++i) { 82
52 const char c = hex_string[length - 1 - i]; 83 for (; i < 8; ++i) {
53 if (!IsHexDigit(c)) { 84 const auto upper = HexCharToByte(*(str++));
54 ASSERT_MSG(false, "{} is not a hexadecimal digit!", c); 85 const auto lower = HexCharToByte(*(str++));
55 return INVALID_UUID; 86 if (!upper || !lower) {
87 return {};
56 } 88 }
57 if (i < 16) { 89 uuid[i] = static_cast<u8>((*upper << 4) | *lower);
58 lo |= u64{HexCharToByte(c)} << (i * 4); 90 }
91
92 // Process the next 4 characters.
93 ++str;
94
95 for (; i < 10; ++i) {
96 const auto upper = HexCharToByte(*(str++));
97 const auto lower = HexCharToByte(*(str++));
98 if (!upper || !lower) {
99 return {};
59 } 100 }
60 if (i >= 16) { 101 uuid[i] = static_cast<u8>((*upper << 4) | *lower);
61 hi |= u64{HexCharToByte(c)} << ((i - 16) * 4); 102 }
103
104 // Process the last 12 characters.
105 ++str;
106
107 for (; i < 16; ++i) {
108 const auto upper = HexCharToByte(*(str++));
109 const auto lower = HexCharToByte(*(str++));
110 if (!upper || !lower) {
111 return {};
62 } 112 }
113 uuid[i] = static_cast<u8>((*upper << 4) | *lower);
114 }
115
116 return uuid;
117}
118
119std::array<u8, 0x10> ConstructUUID(std::string_view uuid_string) {
120 const auto length = uuid_string.length();
121
122 if (length == 0) {
123 return {};
124 }
125
126 // Check if the input string contains 32 hexadecimal characters.
127 if (length == RawStringSize) {
128 return ConstructFromRawString(uuid_string);
129 }
130
131 // Check if the input string has the length of a RFC 4122 formatted UUID string.
132 if (length == FormattedStringSize) {
133 return ConstructFromFormattedString(uuid_string);
63 } 134 }
64 return u128{lo, hi}; 135
136 ASSERT_MSG(false, "UUID string has an invalid length of {} characters!", length);
137
138 return {};
139}
140
141} // Anonymous namespace
142
143UUID::UUID(std::string_view uuid_string) : uuid{ConstructUUID(uuid_string)} {}
144
145std::string UUID::RawString() const {
146 return fmt::format("{:02x}{:02x}{:02x}{:02x}{:02x}{:02x}{:02x}{:02x}"
147 "{:02x}{:02x}{:02x}{:02x}{:02x}{:02x}{:02x}{:02x}",
148 uuid[0], uuid[1], uuid[2], uuid[3], uuid[4], uuid[5], uuid[6], uuid[7],
149 uuid[8], uuid[9], uuid[10], uuid[11], uuid[12], uuid[13], uuid[14],
150 uuid[15]);
151}
152
153std::string UUID::FormattedString() const {
154 return fmt::format("{:02x}{:02x}{:02x}{:02x}"
155 "-{:02x}{:02x}-{:02x}{:02x}-{:02x}{:02x}-"
156 "{:02x}{:02x}{:02x}{:02x}{:02x}{:02x}",
157 uuid[0], uuid[1], uuid[2], uuid[3], uuid[4], uuid[5], uuid[6], uuid[7],
158 uuid[8], uuid[9], uuid[10], uuid[11], uuid[12], uuid[13], uuid[14],
159 uuid[15]);
160}
161
162size_t UUID::Hash() const noexcept {
163 u64 upper_hash;
164 u64 lower_hash;
165
166 std::memcpy(&upper_hash, uuid.data(), sizeof(u64));
167 std::memcpy(&lower_hash, uuid.data() + sizeof(u64), sizeof(u64));
168
169 return upper_hash ^ std::rotl(lower_hash, 1);
65} 170}
66 171
67UUID UUID::Generate() { 172u128 UUID::AsU128() const {
173 u128 uuid_old;
174 std::memcpy(&uuid_old, uuid.data(), sizeof(UUID));
175 return uuid_old;
176}
177
178UUID UUID::MakeRandom() {
68 std::random_device device; 179 std::random_device device;
69 std::mt19937 gen(device()); 180
70 std::uniform_int_distribution<u64> distribution(1, std::numeric_limits<u64>::max()); 181 return MakeRandomWithSeed(device());
71 return UUID{distribution(gen), distribution(gen)};
72} 182}
73 183
74std::string UUID::Format() const { 184UUID UUID::MakeRandomWithSeed(u32 seed) {
75 return fmt::format("{:016x}{:016x}", uuid[1], uuid[0]); 185 // Create and initialize our RNG.
186 TinyMT rng;
187 rng.Initialize(seed);
188
189 UUID uuid;
190
191 // Populate the UUID with random bytes.
192 rng.GenerateRandomBytes(uuid.uuid.data(), sizeof(UUID));
193
194 return uuid;
76} 195}
77 196
78std::string UUID::FormatSwitch() const { 197UUID UUID::MakeRandomRFC4122V4() {
79 std::array<u8, 16> s{}; 198 auto uuid = MakeRandom();
80 std::memcpy(s.data(), uuid.data(), sizeof(u128)); 199
81 return fmt::format("{:02x}{:02x}{:02x}{:02x}-{:02x}{:02x}-{:02x}{:02x}-{:02x}{:02x}-{:02x}{" 200 // According to Proposed Standard RFC 4122 Section 4.4, we must:
82 ":02x}{:02x}{:02x}{:02x}{:02x}", 201
83 s[0], s[1], s[2], s[3], s[4], s[5], s[6], s[7], s[8], s[9], s[10], s[11], 202 // 1. Set the two most significant bits (bits 6 and 7) of the
84 s[12], s[13], s[14], s[15]); 203 // clock_seq_hi_and_reserved to zero and one, respectively.
204 uuid.uuid[8] = 0x80 | (uuid.uuid[8] & 0x3F);
205
206 // 2. Set the four most significant bits (bits 12 through 15) of the
207 // time_hi_and_version field to the 4-bit version number from Section 4.1.3.
208 uuid.uuid[6] = 0x40 | (uuid.uuid[6] & 0xF);
209
210 return uuid;
85} 211}
86 212
87} // namespace Common 213} // namespace Common
diff --git a/src/common/uuid.h b/src/common/uuid.h
index 8ea01f8da..fe31e64e6 100644
--- a/src/common/uuid.h
+++ b/src/common/uuid.h
@@ -1,9 +1,11 @@
1// Copyright 2018 yuzu Emulator Project 1// Copyright 2022 yuzu Emulator Project
2// Licensed under GPLv2 or any later version 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#pragma once 5#pragma once
6 6
7#include <array>
8#include <functional>
7#include <string> 9#include <string>
8#include <string_view> 10#include <string_view>
9 11
@@ -11,69 +13,119 @@
11 13
12namespace Common { 14namespace Common {
13 15
14constexpr u128 INVALID_UUID{{0, 0}};
15
16/**
17 * Converts a hex string to a 128-bit unsigned integer.
18 *
19 * The hex string can be formatted in lowercase or uppercase, with or without the "0x" prefix.
20 *
21 * This function will assert and return INVALID_UUID under the following conditions:
22 * - If the hex string is more than 32 characters long
23 * - If the hex string contains non-hexadecimal characters
24 *
25 * @param hex_string Hexadecimal string
26 *
27 * @returns A 128-bit unsigned integer if successfully converted, INVALID_UUID otherwise.
28 */
29[[nodiscard]] u128 HexStringToU128(std::string_view hex_string);
30
31struct UUID { 16struct UUID {
32 // UUIDs which are 0 are considered invalid! 17 std::array<u8, 0x10> uuid{};
33 u128 uuid; 18
34 UUID() = default; 19 /// Constructs an invalid UUID.
35 constexpr explicit UUID(const u128& id) : uuid{id} {} 20 constexpr UUID() = default;
36 constexpr explicit UUID(const u64 lo, const u64 hi) : uuid{{lo, hi}} {} 21
37 explicit UUID(std::string_view hex_string) { 22 /// Constructs a UUID from a reference to a 128 bit array.
38 uuid = HexStringToU128(hex_string); 23 constexpr explicit UUID(const std::array<u8, 16>& uuid_) : uuid{uuid_} {}
39 } 24
40 25 /**
41 [[nodiscard]] constexpr explicit operator bool() const { 26 * Constructs a UUID from either:
42 return uuid != INVALID_UUID; 27 * 1. A 32 hexadecimal character string representing the bytes of the UUID
43 } 28 * 2. A RFC 4122 formatted UUID string, in the format xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
44 29 *
45 [[nodiscard]] constexpr bool operator==(const UUID& rhs) const { 30 * The input string may contain uppercase or lowercase characters, but they must:
46 return uuid == rhs.uuid; 31 * 1. Contain valid hexadecimal characters (0-9, a-f, A-F)
47 } 32 * 2. Not contain the "0x" hexadecimal prefix
48 33 *
49 [[nodiscard]] constexpr bool operator!=(const UUID& rhs) const { 34 * Should the input string not meet the above requirements,
50 return !operator==(rhs); 35 * an assert will be triggered and an invalid UUID is set instead.
51 } 36 */
52 37 explicit UUID(std::string_view uuid_string);
53 // TODO(ogniK): Properly generate uuids based on RFC-4122 38
54 [[nodiscard]] static UUID Generate(); 39 ~UUID() = default;
55 40
56 // Set the UUID to {0,0} to be considered an invalid user 41 constexpr UUID(const UUID&) noexcept = default;
57 constexpr void Invalidate() { 42 constexpr UUID(UUID&&) noexcept = default;
58 uuid = INVALID_UUID; 43
44 constexpr UUID& operator=(const UUID&) noexcept = default;
45 constexpr UUID& operator=(UUID&&) noexcept = default;
46
47 /**
48 * Returns whether the stored UUID is valid or not.
49 *
50 * @returns True if the stored UUID is valid, false otherwise.
51 */
52 constexpr bool IsValid() const {
53 return uuid != std::array<u8, 0x10>{};
59 } 54 }
60 55
61 [[nodiscard]] constexpr bool IsInvalid() const { 56 /**
62 return uuid == INVALID_UUID; 57 * Returns whether the stored UUID is invalid or not.
63 } 58 *
64 [[nodiscard]] constexpr bool IsValid() const { 59 * @returns True if the stored UUID is invalid, false otherwise.
65 return !IsInvalid(); 60 */
61 constexpr bool IsInvalid() const {
62 return !IsValid();
66 } 63 }
67 64
68 // TODO(ogniK): Properly generate a Nintendo ID 65 /**
69 [[nodiscard]] constexpr u64 GetNintendoID() const { 66 * Returns a 32 hexadecimal character string representing the bytes of the UUID.
70 return uuid[0]; 67 *
68 * @returns A 32 hexadecimal character string of the UUID.
69 */
70 std::string RawString() const;
71
72 /**
73 * Returns a RFC 4122 formatted UUID string in the format xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx.
74 *
75 * @returns A RFC 4122 formatted UUID string.
76 */
77 std::string FormattedString() const;
78
79 /**
80 * Returns a 64-bit hash of the UUID for use in hash table data structures.
81 *
82 * @returns A 64-bit hash of the UUID.
83 */
84 size_t Hash() const noexcept;
85
86 /// DO NOT USE. Copies the contents of the UUID into a u128.
87 u128 AsU128() const;
88
89 /**
90 * Creates a default UUID "yuzu Default UID".
91 *
92 * @returns A UUID with its bytes set to the ASCII values of "yuzu Default UID".
93 */
94 static constexpr UUID MakeDefault() {
95 return UUID{
96 {'y', 'u', 'z', 'u', ' ', 'D', 'e', 'f', 'a', 'u', 'l', 't', ' ', 'U', 'I', 'D'},
97 };
71 } 98 }
72 99
73 [[nodiscard]] std::string Format() const; 100 /**
74 [[nodiscard]] std::string FormatSwitch() const; 101 * Creates a random UUID.
102 *
103 * @returns A random UUID.
104 */
105 static UUID MakeRandom();
106
107 /**
108 * Creates a random UUID with a seed.
109 *
110 * @param seed A seed to initialize the Mersenne-Twister RNG
111 *
112 * @returns A random UUID.
113 */
114 static UUID MakeRandomWithSeed(u32 seed);
115
116 /**
117 * Creates a random UUID. The generated UUID is RFC 4122 Version 4 compliant.
118 *
119 * @returns A random UUID that is RFC 4122 Version 4 compliant.
120 */
121 static UUID MakeRandomRFC4122V4();
122
123 friend constexpr bool operator==(const UUID& lhs, const UUID& rhs) = default;
75}; 124};
76static_assert(sizeof(UUID) == 16, "UUID is an invalid size!"); 125static_assert(sizeof(UUID) == 0x10, "UUID has incorrect size.");
126
127/// An invalid UUID. This UUID has all its bytes set to 0.
128constexpr UUID InvalidUUID = {};
77 129
78} // namespace Common 130} // namespace Common
79 131
@@ -82,7 +134,7 @@ namespace std {
82template <> 134template <>
83struct hash<Common::UUID> { 135struct hash<Common::UUID> {
84 size_t operator()(const Common::UUID& uuid) const noexcept { 136 size_t operator()(const Common::UUID& uuid) const noexcept {
85 return uuid.uuid[1] ^ uuid.uuid[0]; 137 return uuid.Hash();
86 } 138 }
87}; 139};
88 140
diff --git a/src/common/x64/cpu_detect.cpp b/src/common/x64/cpu_detect.cpp
index fbeacc7e2..d81edb140 100644
--- a/src/common/x64/cpu_detect.cpp
+++ b/src/common/x64/cpu_detect.cpp
@@ -1,8 +1,12 @@
1// Copyright 2013 Dolphin Emulator Project / 2015 Citra Emulator Project 1// Copyright 2013 Dolphin Emulator Project / 2015 Citra Emulator Project / 2022 Yuzu Emulator
2// Licensed under GPLv2 or any later version 2// Project Licensed under GPLv2 or any later version Refer to the license.txt file included.
3// Refer to the license.txt file included.
4 3
4#include <array>
5#include <cstring> 5#include <cstring>
6#include <iterator>
7#include <span>
8#include <string_view>
9#include "common/bit_util.h"
6#include "common/common_types.h" 10#include "common/common_types.h"
7#include "common/x64/cpu_detect.h" 11#include "common/x64/cpu_detect.h"
8 12
@@ -17,7 +21,7 @@
17// clang-format on 21// clang-format on
18#endif 22#endif
19 23
20static inline void __cpuidex(int info[4], int function_id, int subfunction_id) { 24static inline void __cpuidex(int info[4], u32 function_id, u32 subfunction_id) {
21#if defined(__DragonFly__) || defined(__FreeBSD__) 25#if defined(__DragonFly__) || defined(__FreeBSD__)
22 // Despite the name, this is just do_cpuid() with ECX as second input. 26 // Despite the name, this is just do_cpuid() with ECX as second input.
23 cpuid_count((u_int)function_id, (u_int)subfunction_id, (u_int*)info); 27 cpuid_count((u_int)function_id, (u_int)subfunction_id, (u_int*)info);
@@ -30,7 +34,7 @@ static inline void __cpuidex(int info[4], int function_id, int subfunction_id) {
30#endif 34#endif
31} 35}
32 36
33static inline void __cpuid(int info[4], int function_id) { 37static inline void __cpuid(int info[4], u32 function_id) {
34 return __cpuidex(info, function_id, 0); 38 return __cpuidex(info, function_id, 0);
35} 39}
36 40
@@ -45,6 +49,17 @@ static inline u64 _xgetbv(u32 index) {
45 49
46namespace Common { 50namespace Common {
47 51
52CPUCaps::Manufacturer CPUCaps::ParseManufacturer(std::string_view brand_string) {
53 if (brand_string == "GenuineIntel") {
54 return Manufacturer::Intel;
55 } else if (brand_string == "AuthenticAMD") {
56 return Manufacturer::AMD;
57 } else if (brand_string == "HygonGenuine") {
58 return Manufacturer::Hygon;
59 }
60 return Manufacturer::Unknown;
61}
62
48// Detects the various CPU features 63// Detects the various CPU features
49static CPUCaps Detect() { 64static CPUCaps Detect() {
50 CPUCaps caps = {}; 65 CPUCaps caps = {};
@@ -53,75 +68,74 @@ static CPUCaps Detect() {
53 // yuzu at all anyway 68 // yuzu at all anyway
54 69
55 int cpu_id[4]; 70 int cpu_id[4];
56 memset(caps.brand_string, 0, sizeof(caps.brand_string));
57 71
58 // Detect CPU's CPUID capabilities and grab CPU string 72 // Detect CPU's CPUID capabilities and grab manufacturer string
59 __cpuid(cpu_id, 0x00000000); 73 __cpuid(cpu_id, 0x00000000);
60 u32 max_std_fn = cpu_id[0]; // EAX 74 const u32 max_std_fn = cpu_id[0]; // EAX
61
62 std::memcpy(&caps.brand_string[0], &cpu_id[1], sizeof(int));
63 std::memcpy(&caps.brand_string[4], &cpu_id[3], sizeof(int));
64 std::memcpy(&caps.brand_string[8], &cpu_id[2], sizeof(int));
65 if (cpu_id[1] == 0x756e6547 && cpu_id[2] == 0x6c65746e && cpu_id[3] == 0x49656e69)
66 caps.manufacturer = Manufacturer::Intel;
67 else if (cpu_id[1] == 0x68747541 && cpu_id[2] == 0x444d4163 && cpu_id[3] == 0x69746e65)
68 caps.manufacturer = Manufacturer::AMD;
69 else if (cpu_id[1] == 0x6f677948 && cpu_id[2] == 0x656e6975 && cpu_id[3] == 0x6e65476e)
70 caps.manufacturer = Manufacturer::Hygon;
71 else
72 caps.manufacturer = Manufacturer::Unknown;
73 75
74 __cpuid(cpu_id, 0x80000000); 76 std::memset(caps.brand_string, 0, std::size(caps.brand_string));
77 std::memcpy(&caps.brand_string[0], &cpu_id[1], sizeof(u32));
78 std::memcpy(&caps.brand_string[4], &cpu_id[3], sizeof(u32));
79 std::memcpy(&caps.brand_string[8], &cpu_id[2], sizeof(u32));
80
81 caps.manufacturer = CPUCaps::ParseManufacturer(caps.brand_string);
75 82
76 u32 max_ex_fn = cpu_id[0]; 83 // Set reasonable default cpu string even if brand string not available
84 std::strncpy(caps.cpu_string, caps.brand_string, std::size(caps.brand_string));
77 85
78 // Set reasonable default brand string even if brand string not available 86 __cpuid(cpu_id, 0x80000000);
79 strcpy(caps.cpu_string, caps.brand_string); 87
88 const u32 max_ex_fn = cpu_id[0];
80 89
81 // Detect family and other miscellaneous features 90 // Detect family and other miscellaneous features
82 if (max_std_fn >= 1) { 91 if (max_std_fn >= 1) {
83 __cpuid(cpu_id, 0x00000001); 92 __cpuid(cpu_id, 0x00000001);
84 if ((cpu_id[3] >> 25) & 1) 93 caps.sse = Common::Bit<25>(cpu_id[3]);
85 caps.sse = true; 94 caps.sse2 = Common::Bit<26>(cpu_id[3]);
86 if ((cpu_id[3] >> 26) & 1) 95 caps.sse3 = Common::Bit<0>(cpu_id[2]);
87 caps.sse2 = true; 96 caps.pclmulqdq = Common::Bit<1>(cpu_id[2]);
88 if ((cpu_id[2]) & 1) 97 caps.ssse3 = Common::Bit<9>(cpu_id[2]);
89 caps.sse3 = true; 98 caps.sse4_1 = Common::Bit<19>(cpu_id[2]);
90 if ((cpu_id[2] >> 9) & 1) 99 caps.sse4_2 = Common::Bit<20>(cpu_id[2]);
91 caps.ssse3 = true; 100 caps.movbe = Common::Bit<22>(cpu_id[2]);
92 if ((cpu_id[2] >> 19) & 1) 101 caps.popcnt = Common::Bit<23>(cpu_id[2]);
93 caps.sse4_1 = true; 102 caps.aes = Common::Bit<25>(cpu_id[2]);
94 if ((cpu_id[2] >> 20) & 1) 103 caps.f16c = Common::Bit<29>(cpu_id[2]);
95 caps.sse4_2 = true;
96 if ((cpu_id[2] >> 25) & 1)
97 caps.aes = true;
98 104
99 // AVX support requires 3 separate checks: 105 // AVX support requires 3 separate checks:
100 // - Is the AVX bit set in CPUID? 106 // - Is the AVX bit set in CPUID?
101 // - Is the XSAVE bit set in CPUID? 107 // - Is the XSAVE bit set in CPUID?
102 // - XGETBV result has the XCR bit set. 108 // - XGETBV result has the XCR bit set.
103 if (((cpu_id[2] >> 28) & 1) && ((cpu_id[2] >> 27) & 1)) { 109 if (Common::Bit<28>(cpu_id[2]) && Common::Bit<27>(cpu_id[2])) {
104 if ((_xgetbv(_XCR_XFEATURE_ENABLED_MASK) & 0x6) == 0x6) { 110 if ((_xgetbv(_XCR_XFEATURE_ENABLED_MASK) & 0x6) == 0x6) {
105 caps.avx = true; 111 caps.avx = true;
106 if ((cpu_id[2] >> 12) & 1) 112 if (Common::Bit<12>(cpu_id[2]))
107 caps.fma = true; 113 caps.fma = true;
108 } 114 }
109 } 115 }
110 116
111 if (max_std_fn >= 7) { 117 if (max_std_fn >= 7) {
112 __cpuidex(cpu_id, 0x00000007, 0x00000000); 118 __cpuidex(cpu_id, 0x00000007, 0x00000000);
113 // Can't enable AVX2 unless the XSAVE/XGETBV checks above passed 119 // Can't enable AVX{2,512} unless the XSAVE/XGETBV checks above passed
114 if ((cpu_id[1] >> 5) & 1) 120 if (caps.avx) {
115 caps.avx2 = caps.avx; 121 caps.avx2 = Common::Bit<5>(cpu_id[1]);
116 if ((cpu_id[1] >> 3) & 1) 122 caps.avx512f = Common::Bit<16>(cpu_id[1]);
117 caps.bmi1 = true; 123 caps.avx512dq = Common::Bit<17>(cpu_id[1]);
118 if ((cpu_id[1] >> 8) & 1) 124 caps.avx512cd = Common::Bit<28>(cpu_id[1]);
119 caps.bmi2 = true; 125 caps.avx512bw = Common::Bit<30>(cpu_id[1]);
120 // Checks for AVX512F, AVX512CD, AVX512VL, AVX512DQ, AVX512BW (Intel Skylake-X/SP) 126 caps.avx512vl = Common::Bit<31>(cpu_id[1]);
121 if ((cpu_id[1] >> 16) & 1 && (cpu_id[1] >> 28) & 1 && (cpu_id[1] >> 31) & 1 && 127 caps.avx512vbmi = Common::Bit<1>(cpu_id[2]);
122 (cpu_id[1] >> 17) & 1 && (cpu_id[1] >> 30) & 1) { 128 caps.avx512bitalg = Common::Bit<12>(cpu_id[2]);
123 caps.avx512 = caps.avx2;
124 } 129 }
130
131 caps.bmi1 = Common::Bit<3>(cpu_id[1]);
132 caps.bmi2 = Common::Bit<8>(cpu_id[1]);
133 caps.sha = Common::Bit<29>(cpu_id[1]);
134
135 caps.gfni = Common::Bit<8>(cpu_id[2]);
136
137 __cpuidex(cpu_id, 0x00000007, 0x00000001);
138 caps.avx_vnni = caps.avx && Common::Bit<4>(cpu_id[0]);
125 } 139 }
126 } 140 }
127 141
@@ -138,15 +152,13 @@ static CPUCaps Detect() {
138 if (max_ex_fn >= 0x80000001) { 152 if (max_ex_fn >= 0x80000001) {
139 // Check for more features 153 // Check for more features
140 __cpuid(cpu_id, 0x80000001); 154 __cpuid(cpu_id, 0x80000001);
141 if ((cpu_id[2] >> 16) & 1) 155 caps.lzcnt = Common::Bit<5>(cpu_id[2]);
142 caps.fma4 = true; 156 caps.fma4 = Common::Bit<16>(cpu_id[2]);
143 } 157 }
144 158
145 if (max_ex_fn >= 0x80000007) { 159 if (max_ex_fn >= 0x80000007) {
146 __cpuid(cpu_id, 0x80000007); 160 __cpuid(cpu_id, 0x80000007);
147 if (cpu_id[3] & (1 << 8)) { 161 caps.invariant_tsc = Common::Bit<8>(cpu_id[3]);
148 caps.invariant_tsc = true;
149 }
150 } 162 }
151 163
152 if (max_std_fn >= 0x16) { 164 if (max_std_fn >= 0x16) {
diff --git a/src/common/x64/cpu_detect.h b/src/common/x64/cpu_detect.h
index e3b63302e..40c48b132 100644
--- a/src/common/x64/cpu_detect.h
+++ b/src/common/x64/cpu_detect.h
@@ -1,42 +1,65 @@
1// Copyright 2013 Dolphin Emulator Project / 2015 Citra Emulator Project 1// Copyright 2013 Dolphin Emulator Project / 2015 Citra Emulator Project / 2022 Yuzu Emulator
2// Licensed under GPLv2 or any later version 2// Project Project Licensed under GPLv2 or any later version Refer to the license.txt file included.
3// Refer to the license.txt file included.
4 3
5#pragma once 4#pragma once
6 5
7namespace Common { 6#include <string_view>
7#include "common/common_types.h"
8 8
9enum class Manufacturer : u32 { 9namespace Common {
10 Intel = 0,
11 AMD = 1,
12 Hygon = 2,
13 Unknown = 3,
14};
15 10
16/// x86/x64 CPU capabilities that may be detected by this module 11/// x86/x64 CPU capabilities that may be detected by this module
17struct CPUCaps { 12struct CPUCaps {
13
14 enum class Manufacturer : u8 {
15 Unknown = 0,
16 Intel = 1,
17 AMD = 2,
18 Hygon = 3,
19 };
20
21 static Manufacturer ParseManufacturer(std::string_view brand_string);
22
18 Manufacturer manufacturer; 23 Manufacturer manufacturer;
19 char cpu_string[0x21]; 24 char brand_string[13];
20 char brand_string[0x41]; 25
21 bool sse; 26 char cpu_string[48];
22 bool sse2; 27
23 bool sse3;
24 bool ssse3;
25 bool sse4_1;
26 bool sse4_2;
27 bool lzcnt;
28 bool avx;
29 bool avx2;
30 bool avx512;
31 bool bmi1;
32 bool bmi2;
33 bool fma;
34 bool fma4;
35 bool aes;
36 bool invariant_tsc;
37 u32 base_frequency; 28 u32 base_frequency;
38 u32 max_frequency; 29 u32 max_frequency;
39 u32 bus_frequency; 30 u32 bus_frequency;
31
32 bool sse : 1;
33 bool sse2 : 1;
34 bool sse3 : 1;
35 bool ssse3 : 1;
36 bool sse4_1 : 1;
37 bool sse4_2 : 1;
38
39 bool avx : 1;
40 bool avx_vnni : 1;
41 bool avx2 : 1;
42 bool avx512f : 1;
43 bool avx512dq : 1;
44 bool avx512cd : 1;
45 bool avx512bw : 1;
46 bool avx512vl : 1;
47 bool avx512vbmi : 1;
48 bool avx512bitalg : 1;
49
50 bool aes : 1;
51 bool bmi1 : 1;
52 bool bmi2 : 1;
53 bool f16c : 1;
54 bool fma : 1;
55 bool fma4 : 1;
56 bool gfni : 1;
57 bool invariant_tsc : 1;
58 bool lzcnt : 1;
59 bool movbe : 1;
60 bool pclmulqdq : 1;
61 bool popcnt : 1;
62 bool sha : 1;
40}; 63};
41 64
42/** 65/**
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt
index 6e8d11919..1d4e92edb 100644
--- a/src/core/CMakeLists.txt
+++ b/src/core/CMakeLists.txt
@@ -122,6 +122,8 @@ add_library(core STATIC
122 frontend/applets/error.h 122 frontend/applets/error.h
123 frontend/applets/general_frontend.cpp 123 frontend/applets/general_frontend.cpp
124 frontend/applets/general_frontend.h 124 frontend/applets/general_frontend.h
125 frontend/applets/mii.cpp
126 frontend/applets/mii.h
125 frontend/applets/profile_select.cpp 127 frontend/applets/profile_select.cpp
126 frontend/applets/profile_select.h 128 frontend/applets/profile_select.h
127 frontend/applets/software_keyboard.cpp 129 frontend/applets/software_keyboard.cpp
@@ -152,6 +154,7 @@ add_library(core STATIC
152 hle/api_version.h 154 hle/api_version.h
153 hle/ipc.h 155 hle/ipc.h
154 hle/ipc_helpers.h 156 hle/ipc_helpers.h
157 hle/kernel/board/nintendo/nx/k_memory_layout.h
155 hle/kernel/board/nintendo/nx/k_system_control.cpp 158 hle/kernel/board/nintendo/nx/k_system_control.cpp
156 hle/kernel/board/nintendo/nx/k_system_control.h 159 hle/kernel/board/nintendo/nx/k_system_control.h
157 hle/kernel/board/nintendo/nx/secure_monitor.h 160 hle/kernel/board/nintendo/nx/secure_monitor.h
@@ -164,6 +167,7 @@ add_library(core STATIC
164 hle/kernel/hle_ipc.h 167 hle/kernel/hle_ipc.h
165 hle/kernel/init/init_slab_setup.cpp 168 hle/kernel/init/init_slab_setup.cpp
166 hle/kernel/init/init_slab_setup.h 169 hle/kernel/init/init_slab_setup.h
170 hle/kernel/initial_process.h
167 hle/kernel/k_address_arbiter.cpp 171 hle/kernel/k_address_arbiter.cpp
168 hle/kernel/k_address_arbiter.h 172 hle/kernel/k_address_arbiter.h
169 hle/kernel/k_address_space_info.cpp 173 hle/kernel/k_address_space_info.cpp
@@ -205,6 +209,8 @@ add_library(core STATIC
205 hle/kernel/k_memory_region.h 209 hle/kernel/k_memory_region.h
206 hle/kernel/k_memory_region_type.h 210 hle/kernel/k_memory_region_type.h
207 hle/kernel/k_page_bitmap.h 211 hle/kernel/k_page_bitmap.h
212 hle/kernel/k_page_buffer.cpp
213 hle/kernel/k_page_buffer.h
208 hle/kernel/k_page_heap.cpp 214 hle/kernel/k_page_heap.cpp
209 hle/kernel/k_page_heap.h 215 hle/kernel/k_page_heap.h
210 hle/kernel/k_page_linked_list.h 216 hle/kernel/k_page_linked_list.h
@@ -242,6 +248,8 @@ add_library(core STATIC
242 hle/kernel/k_system_control.h 248 hle/kernel/k_system_control.h
243 hle/kernel/k_thread.cpp 249 hle/kernel/k_thread.cpp
244 hle/kernel/k_thread.h 250 hle/kernel/k_thread.h
251 hle/kernel/k_thread_local_page.cpp
252 hle/kernel/k_thread_local_page.h
245 hle/kernel/k_thread_queue.cpp 253 hle/kernel/k_thread_queue.cpp
246 hle/kernel/k_thread_queue.h 254 hle/kernel/k_thread_queue.h
247 hle/kernel/k_trace.h 255 hle/kernel/k_trace.h
@@ -298,6 +306,8 @@ add_library(core STATIC
298 hle/service/am/applets/applet_error.h 306 hle/service/am/applets/applet_error.h
299 hle/service/am/applets/applet_general_backend.cpp 307 hle/service/am/applets/applet_general_backend.cpp
300 hle/service/am/applets/applet_general_backend.h 308 hle/service/am/applets/applet_general_backend.h
309 hle/service/am/applets/applet_mii.cpp
310 hle/service/am/applets/applet_mii.h
301 hle/service/am/applets/applet_profile_select.cpp 311 hle/service/am/applets/applet_profile_select.cpp
302 hle/service/am/applets/applet_profile_select.h 312 hle/service/am/applets/applet_profile_select.h
303 hle/service/am/applets/applet_software_keyboard.cpp 313 hle/service/am/applets/applet_software_keyboard.cpp
@@ -467,6 +477,8 @@ add_library(core STATIC
467 hle/service/mii/types.h 477 hle/service/mii/types.h
468 hle/service/mm/mm_u.cpp 478 hle/service/mm/mm_u.cpp
469 hle/service/mm/mm_u.h 479 hle/service/mm/mm_u.h
480 hle/service/mnpp/mnpp_app.cpp
481 hle/service/mnpp/mnpp_app.h
470 hle/service/ncm/ncm.cpp 482 hle/service/ncm/ncm.cpp
471 hle/service/ncm/ncm.h 483 hle/service/ncm/ncm.h
472 hle/service/nfc/nfc.cpp 484 hle/service/nfc/nfc.cpp
diff --git a/src/core/arm/arm_interface.h b/src/core/arm/arm_interface.h
index 689e3ceb5..c60322442 100644
--- a/src/core/arm/arm_interface.h
+++ b/src/core/arm/arm_interface.h
@@ -6,6 +6,7 @@
6 6
7#include <array> 7#include <array>
8#include <vector> 8#include <vector>
9#include "common/common_funcs.h"
9#include "common/common_types.h" 10#include "common/common_types.h"
10#include "core/hardware_properties.h" 11#include "core/hardware_properties.h"
11 12
@@ -24,8 +25,11 @@ class CPUInterruptHandler;
24using CPUInterrupts = std::array<CPUInterruptHandler, Core::Hardware::NUM_CPU_CORES>; 25using CPUInterrupts = std::array<CPUInterruptHandler, Core::Hardware::NUM_CPU_CORES>;
25 26
26/// Generic ARMv8 CPU interface 27/// Generic ARMv8 CPU interface
27class ARM_Interface : NonCopyable { 28class ARM_Interface {
28public: 29public:
30 YUZU_NON_COPYABLE(ARM_Interface);
31 YUZU_NON_MOVEABLE(ARM_Interface);
32
29 explicit ARM_Interface(System& system_, CPUInterrupts& interrupt_handlers_, 33 explicit ARM_Interface(System& system_, CPUInterrupts& interrupt_handlers_,
30 bool uses_wall_clock_) 34 bool uses_wall_clock_)
31 : system{system_}, interrupt_handlers{interrupt_handlers_}, uses_wall_clock{ 35 : system{system_}, interrupt_handlers{interrupt_handlers_}, uses_wall_clock{
diff --git a/src/core/arm/dynarmic/arm_dynarmic_32.cpp b/src/core/arm/dynarmic/arm_dynarmic_32.cpp
index b0d89c539..c1c843b8f 100644
--- a/src/core/arm/dynarmic/arm_dynarmic_32.cpp
+++ b/src/core/arm/dynarmic/arm_dynarmic_32.cpp
@@ -137,6 +137,8 @@ std::shared_ptr<Dynarmic::A32::Jit> ARM_Dynarmic_32::MakeJit(Common::PageTable*
137 config.page_table_pointer_mask_bits = Common::PageTable::ATTRIBUTE_BITS; 137 config.page_table_pointer_mask_bits = Common::PageTable::ATTRIBUTE_BITS;
138 config.detect_misaligned_access_via_page_table = 16 | 32 | 64 | 128; 138 config.detect_misaligned_access_via_page_table = 16 | 32 | 64 | 128;
139 config.only_detect_misalignment_via_page_table_on_page_boundary = true; 139 config.only_detect_misalignment_via_page_table_on_page_boundary = true;
140 config.fastmem_exclusive_access = true;
141 config.recompile_on_exclusive_fastmem_failure = true;
140 142
141 // Multi-process state 143 // Multi-process state
142 config.processor_id = core_index; 144 config.processor_id = core_index;
@@ -146,8 +148,8 @@ std::shared_ptr<Dynarmic::A32::Jit> ARM_Dynarmic_32::MakeJit(Common::PageTable*
146 config.wall_clock_cntpct = uses_wall_clock; 148 config.wall_clock_cntpct = uses_wall_clock;
147 149
148 // Code cache size 150 // Code cache size
149 config.code_cache_size = 512_MiB; 151 config.code_cache_size = 128_MiB;
150 config.far_code_offset = 400_MiB; 152 config.far_code_offset = 100_MiB;
151 153
152 // Safe optimizations 154 // Safe optimizations
153 if (Settings::values.cpu_debug_mode) { 155 if (Settings::values.cpu_debug_mode) {
@@ -178,6 +180,12 @@ std::shared_ptr<Dynarmic::A32::Jit> ARM_Dynarmic_32::MakeJit(Common::PageTable*
178 if (!Settings::values.cpuopt_fastmem) { 180 if (!Settings::values.cpuopt_fastmem) {
179 config.fastmem_pointer = nullptr; 181 config.fastmem_pointer = nullptr;
180 } 182 }
183 if (!Settings::values.cpuopt_fastmem_exclusives) {
184 config.fastmem_exclusive_access = false;
185 }
186 if (!Settings::values.cpuopt_recompile_exclusives) {
187 config.recompile_on_exclusive_fastmem_failure = false;
188 }
181 } 189 }
182 190
183 // Unsafe optimizations 191 // Unsafe optimizations
@@ -195,6 +203,9 @@ std::shared_ptr<Dynarmic::A32::Jit> ARM_Dynarmic_32::MakeJit(Common::PageTable*
195 if (Settings::values.cpuopt_unsafe_inaccurate_nan) { 203 if (Settings::values.cpuopt_unsafe_inaccurate_nan) {
196 config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_InaccurateNaN; 204 config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_InaccurateNaN;
197 } 205 }
206 if (Settings::values.cpuopt_unsafe_ignore_global_monitor) {
207 config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_IgnoreGlobalMonitor;
208 }
198 } 209 }
199 210
200 // Curated optimizations 211 // Curated optimizations
@@ -203,6 +214,7 @@ std::shared_ptr<Dynarmic::A32::Jit> ARM_Dynarmic_32::MakeJit(Common::PageTable*
203 config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_UnfuseFMA; 214 config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_UnfuseFMA;
204 config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_IgnoreStandardFPCRValue; 215 config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_IgnoreStandardFPCRValue;
205 config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_InaccurateNaN; 216 config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_InaccurateNaN;
217 config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_IgnoreGlobalMonitor;
206 } 218 }
207 219
208 return std::make_unique<Dynarmic::A32::Jit>(config); 220 return std::make_unique<Dynarmic::A32::Jit>(config);
diff --git a/src/core/arm/dynarmic/arm_dynarmic_64.cpp b/src/core/arm/dynarmic/arm_dynarmic_64.cpp
index 56836bd05..aa74fce4d 100644
--- a/src/core/arm/dynarmic/arm_dynarmic_64.cpp
+++ b/src/core/arm/dynarmic/arm_dynarmic_64.cpp
@@ -185,6 +185,9 @@ std::shared_ptr<Dynarmic::A64::Jit> ARM_Dynarmic_64::MakeJit(Common::PageTable*
185 config.fastmem_pointer = page_table->fastmem_arena; 185 config.fastmem_pointer = page_table->fastmem_arena;
186 config.fastmem_address_space_bits = address_space_bits; 186 config.fastmem_address_space_bits = address_space_bits;
187 config.silently_mirror_fastmem = false; 187 config.silently_mirror_fastmem = false;
188
189 config.fastmem_exclusive_access = true;
190 config.recompile_on_exclusive_fastmem_failure = true;
188 } 191 }
189 192
190 // Multi-process state 193 // Multi-process state
@@ -205,8 +208,8 @@ std::shared_ptr<Dynarmic::A64::Jit> ARM_Dynarmic_64::MakeJit(Common::PageTable*
205 config.wall_clock_cntpct = uses_wall_clock; 208 config.wall_clock_cntpct = uses_wall_clock;
206 209
207 // Code cache size 210 // Code cache size
208 config.code_cache_size = 512_MiB; 211 config.code_cache_size = 128_MiB;
209 config.far_code_offset = 400_MiB; 212 config.far_code_offset = 100_MiB;
210 213
211 // Safe optimizations 214 // Safe optimizations
212 if (Settings::values.cpu_debug_mode) { 215 if (Settings::values.cpu_debug_mode) {
@@ -237,6 +240,12 @@ std::shared_ptr<Dynarmic::A64::Jit> ARM_Dynarmic_64::MakeJit(Common::PageTable*
237 if (!Settings::values.cpuopt_fastmem) { 240 if (!Settings::values.cpuopt_fastmem) {
238 config.fastmem_pointer = nullptr; 241 config.fastmem_pointer = nullptr;
239 } 242 }
243 if (!Settings::values.cpuopt_fastmem_exclusives) {
244 config.fastmem_exclusive_access = false;
245 }
246 if (!Settings::values.cpuopt_recompile_exclusives) {
247 config.recompile_on_exclusive_fastmem_failure = false;
248 }
240 } 249 }
241 250
242 // Unsafe optimizations 251 // Unsafe optimizations
@@ -254,6 +263,9 @@ std::shared_ptr<Dynarmic::A64::Jit> ARM_Dynarmic_64::MakeJit(Common::PageTable*
254 if (Settings::values.cpuopt_unsafe_fastmem_check) { 263 if (Settings::values.cpuopt_unsafe_fastmem_check) {
255 config.fastmem_address_space_bits = 64; 264 config.fastmem_address_space_bits = 64;
256 } 265 }
266 if (Settings::values.cpuopt_unsafe_ignore_global_monitor) {
267 config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_IgnoreGlobalMonitor;
268 }
257 } 269 }
258 270
259 // Curated optimizations 271 // Curated optimizations
@@ -262,6 +274,7 @@ std::shared_ptr<Dynarmic::A64::Jit> ARM_Dynarmic_64::MakeJit(Common::PageTable*
262 config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_UnfuseFMA; 274 config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_UnfuseFMA;
263 config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_InaccurateNaN; 275 config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_InaccurateNaN;
264 config.fastmem_address_space_bits = 64; 276 config.fastmem_address_space_bits = 64;
277 config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_IgnoreGlobalMonitor;
265 } 278 }
266 279
267 return std::make_shared<Dynarmic::A64::Jit>(config); 280 return std::make_shared<Dynarmic::A64::Jit>(config);
diff --git a/src/core/arm/dynarmic/arm_exclusive_monitor.cpp b/src/core/arm/dynarmic/arm_exclusive_monitor.cpp
index 397d054a8..ea6b224e0 100644
--- a/src/core/arm/dynarmic/arm_exclusive_monitor.cpp
+++ b/src/core/arm/dynarmic/arm_exclusive_monitor.cpp
@@ -37,8 +37,8 @@ u128 DynarmicExclusiveMonitor::ExclusiveRead128(std::size_t core_index, VAddr ad
37 }); 37 });
38} 38}
39 39
40void DynarmicExclusiveMonitor::ClearExclusive() { 40void DynarmicExclusiveMonitor::ClearExclusive(std::size_t core_index) {
41 monitor.Clear(); 41 monitor.ClearProcessor(core_index);
42} 42}
43 43
44bool DynarmicExclusiveMonitor::ExclusiveWrite8(std::size_t core_index, VAddr vaddr, u8 value) { 44bool DynarmicExclusiveMonitor::ExclusiveWrite8(std::size_t core_index, VAddr vaddr, u8 value) {
diff --git a/src/core/arm/dynarmic/arm_exclusive_monitor.h b/src/core/arm/dynarmic/arm_exclusive_monitor.h
index 265c4ecef..5a15b43ef 100644
--- a/src/core/arm/dynarmic/arm_exclusive_monitor.h
+++ b/src/core/arm/dynarmic/arm_exclusive_monitor.h
@@ -29,7 +29,7 @@ public:
29 u32 ExclusiveRead32(std::size_t core_index, VAddr addr) override; 29 u32 ExclusiveRead32(std::size_t core_index, VAddr addr) override;
30 u64 ExclusiveRead64(std::size_t core_index, VAddr addr) override; 30 u64 ExclusiveRead64(std::size_t core_index, VAddr addr) override;
31 u128 ExclusiveRead128(std::size_t core_index, VAddr addr) override; 31 u128 ExclusiveRead128(std::size_t core_index, VAddr addr) override;
32 void ClearExclusive() override; 32 void ClearExclusive(std::size_t core_index) override;
33 33
34 bool ExclusiveWrite8(std::size_t core_index, VAddr vaddr, u8 value) override; 34 bool ExclusiveWrite8(std::size_t core_index, VAddr vaddr, u8 value) override;
35 bool ExclusiveWrite16(std::size_t core_index, VAddr vaddr, u16 value) override; 35 bool ExclusiveWrite16(std::size_t core_index, VAddr vaddr, u16 value) override;
diff --git a/src/core/arm/exclusive_monitor.h b/src/core/arm/exclusive_monitor.h
index 62f6e6023..9914ca3da 100644
--- a/src/core/arm/exclusive_monitor.h
+++ b/src/core/arm/exclusive_monitor.h
@@ -23,7 +23,7 @@ public:
23 virtual u32 ExclusiveRead32(std::size_t core_index, VAddr addr) = 0; 23 virtual u32 ExclusiveRead32(std::size_t core_index, VAddr addr) = 0;
24 virtual u64 ExclusiveRead64(std::size_t core_index, VAddr addr) = 0; 24 virtual u64 ExclusiveRead64(std::size_t core_index, VAddr addr) = 0;
25 virtual u128 ExclusiveRead128(std::size_t core_index, VAddr addr) = 0; 25 virtual u128 ExclusiveRead128(std::size_t core_index, VAddr addr) = 0;
26 virtual void ClearExclusive() = 0; 26 virtual void ClearExclusive(std::size_t core_index) = 0;
27 27
28 virtual bool ExclusiveWrite8(std::size_t core_index, VAddr vaddr, u8 value) = 0; 28 virtual bool ExclusiveWrite8(std::size_t core_index, VAddr vaddr, u8 value) = 0;
29 virtual bool ExclusiveWrite16(std::size_t core_index, VAddr vaddr, u16 value) = 0; 29 virtual bool ExclusiveWrite16(std::size_t core_index, VAddr vaddr, u16 value) = 0;
diff --git a/src/core/core.cpp b/src/core/core.cpp
index 3f9a7f44b..c60a784c3 100644
--- a/src/core/core.cpp
+++ b/src/core/core.cpp
@@ -28,7 +28,9 @@
28#include "core/file_sys/vfs_real.h" 28#include "core/file_sys/vfs_real.h"
29#include "core/hardware_interrupt_manager.h" 29#include "core/hardware_interrupt_manager.h"
30#include "core/hid/hid_core.h" 30#include "core/hid/hid_core.h"
31#include "core/hle/kernel/k_memory_manager.h"
31#include "core/hle/kernel/k_process.h" 32#include "core/hle/kernel/k_process.h"
33#include "core/hle/kernel/k_resource_limit.h"
32#include "core/hle/kernel/k_scheduler.h" 34#include "core/hle/kernel/k_scheduler.h"
33#include "core/hle/kernel/kernel.h" 35#include "core/hle/kernel/kernel.h"
34#include "core/hle/kernel/physical_core.h" 36#include "core/hle/kernel/physical_core.h"
@@ -252,9 +254,16 @@ struct System::Impl {
252 } 254 }
253 255
254 telemetry_session->AddInitialInfo(*app_loader, fs_controller, *content_provider); 256 telemetry_session->AddInitialInfo(*app_loader, fs_controller, *content_provider);
257
258 // Create a resource limit for the process.
259 const auto physical_memory_size =
260 kernel.MemoryManager().GetSize(Kernel::KMemoryManager::Pool::Application);
261 auto* resource_limit = Kernel::CreateResourceLimitForProcess(system, physical_memory_size);
262
263 // Create the process.
255 auto main_process = Kernel::KProcess::Create(system.Kernel()); 264 auto main_process = Kernel::KProcess::Create(system.Kernel());
256 ASSERT(Kernel::KProcess::Initialize(main_process, system, "main", 265 ASSERT(Kernel::KProcess::Initialize(main_process, system, "main",
257 Kernel::KProcess::ProcessType::Userland) 266 Kernel::KProcess::ProcessType::Userland, resource_limit)
258 .IsSuccess()); 267 .IsSuccess());
259 const auto [load_result, load_parameters] = app_loader->Load(*main_process, system); 268 const auto [load_result, load_parameters] = app_loader->Load(*main_process, system);
260 if (load_result != Loader::ResultStatus::Success) { 269 if (load_result != Loader::ResultStatus::Success) {
@@ -317,7 +326,9 @@ struct System::Impl {
317 is_powered_on = false; 326 is_powered_on = false;
318 exit_lock = false; 327 exit_lock = false;
319 328
320 gpu_core->NotifyShutdown(); 329 if (gpu_core != nullptr) {
330 gpu_core->NotifyShutdown();
331 }
321 332
322 services.reset(); 333 services.reset();
323 service_manager.reset(); 334 service_manager.reset();
diff --git a/src/core/device_memory.cpp b/src/core/device_memory.cpp
index f19c0515f..e6bc63086 100644
--- a/src/core/device_memory.cpp
+++ b/src/core/device_memory.cpp
@@ -3,10 +3,13 @@
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include "core/device_memory.h" 5#include "core/device_memory.h"
6#include "hle/kernel/board/nintendo/nx/k_system_control.h"
6 7
7namespace Core { 8namespace Core {
8 9
9DeviceMemory::DeviceMemory() : buffer{DramMemoryMap::Size, 1ULL << 39} {} 10DeviceMemory::DeviceMemory()
11 : buffer{Kernel::Board::Nintendo::Nx::KSystemControl::Init::GetIntendedMemorySize(),
12 1ULL << 39} {}
10DeviceMemory::~DeviceMemory() = default; 13DeviceMemory::~DeviceMemory() = default;
11 14
12} // namespace Core 15} // namespace Core
diff --git a/src/core/device_memory.h b/src/core/device_memory.h
index c4d17705f..daeb551ea 100644
--- a/src/core/device_memory.h
+++ b/src/core/device_memory.h
@@ -12,12 +12,8 @@ namespace Core {
12namespace DramMemoryMap { 12namespace DramMemoryMap {
13enum : u64 { 13enum : u64 {
14 Base = 0x80000000ULL, 14 Base = 0x80000000ULL,
15 Size = 0x100000000ULL,
16 End = Base + Size,
17 KernelReserveBase = Base + 0x60000, 15 KernelReserveBase = Base + 0x60000,
18 SlabHeapBase = KernelReserveBase + 0x85000, 16 SlabHeapBase = KernelReserveBase + 0x85000,
19 SlapHeapSize = 0xa21000,
20 SlabHeapEnd = SlabHeapBase + SlapHeapSize,
21}; 17};
22}; // namespace DramMemoryMap 18}; // namespace DramMemoryMap
23 19
diff --git a/src/core/file_sys/patch_manager.cpp b/src/core/file_sys/patch_manager.cpp
index 7c0950bb0..f19ac4607 100644
--- a/src/core/file_sys/patch_manager.cpp
+++ b/src/core/file_sys/patch_manager.cpp
@@ -128,15 +128,6 @@ VirtualDir PatchManager::PatchExeFS(VirtualDir exefs) const {
128 if (exefs == nullptr) 128 if (exefs == nullptr)
129 return exefs; 129 return exefs;
130 130
131 if (Settings::values.dump_exefs) {
132 LOG_INFO(Loader, "Dumping ExeFS for title_id={:016X}", title_id);
133 const auto dump_dir = fs_controller.GetModificationDumpRoot(title_id);
134 if (dump_dir != nullptr) {
135 const auto exefs_dir = GetOrCreateDirectoryRelative(dump_dir, "/exefs");
136 VfsRawCopyD(exefs, exefs_dir);
137 }
138 }
139
140 const auto& disabled = Settings::values.disabled_addons[title_id]; 131 const auto& disabled = Settings::values.disabled_addons[title_id];
141 const auto update_disabled = 132 const auto update_disabled =
142 std::find(disabled.cbegin(), disabled.cend(), "Update") != disabled.cend(); 133 std::find(disabled.cbegin(), disabled.cend(), "Update") != disabled.cend();
@@ -179,6 +170,15 @@ VirtualDir PatchManager::PatchExeFS(VirtualDir exefs) const {
179 } 170 }
180 } 171 }
181 172
173 if (Settings::values.dump_exefs) {
174 LOG_INFO(Loader, "Dumping ExeFS for title_id={:016X}", title_id);
175 const auto dump_dir = fs_controller.GetModificationDumpRoot(title_id);
176 if (dump_dir != nullptr) {
177 const auto exefs_dir = GetOrCreateDirectoryRelative(dump_dir, "/exefs");
178 VfsRawCopyD(exefs, exefs_dir);
179 }
180 }
181
182 return exefs; 182 return exefs;
183} 183}
184 184
diff --git a/src/core/file_sys/vfs.h b/src/core/file_sys/vfs.h
index 3e625fad6..1b9365853 100644
--- a/src/core/file_sys/vfs.h
+++ b/src/core/file_sys/vfs.h
@@ -12,6 +12,7 @@
12#include <type_traits> 12#include <type_traits>
13#include <vector> 13#include <vector>
14 14
15#include "common/common_funcs.h"
15#include "common/common_types.h" 16#include "common/common_types.h"
16#include "core/file_sys/vfs_types.h" 17#include "core/file_sys/vfs_types.h"
17 18
@@ -29,8 +30,11 @@ enum class VfsEntryType {
29// A class representing an abstract filesystem. A default implementation given the root VirtualDir 30// A class representing an abstract filesystem. A default implementation given the root VirtualDir
30// is provided for convenience, but if the Vfs implementation has any additional state or 31// is provided for convenience, but if the Vfs implementation has any additional state or
31// functionality, they will need to override. 32// functionality, they will need to override.
32class VfsFilesystem : NonCopyable { 33class VfsFilesystem {
33public: 34public:
35 YUZU_NON_COPYABLE(VfsFilesystem);
36 YUZU_NON_MOVEABLE(VfsFilesystem);
37
34 explicit VfsFilesystem(VirtualDir root); 38 explicit VfsFilesystem(VirtualDir root);
35 virtual ~VfsFilesystem(); 39 virtual ~VfsFilesystem();
36 40
@@ -77,8 +81,12 @@ protected:
77}; 81};
78 82
79// A class representing a file in an abstract filesystem. 83// A class representing a file in an abstract filesystem.
80class VfsFile : NonCopyable { 84class VfsFile {
81public: 85public:
86 YUZU_NON_COPYABLE(VfsFile);
87 YUZU_NON_MOVEABLE(VfsFile);
88
89 VfsFile() = default;
82 virtual ~VfsFile(); 90 virtual ~VfsFile();
83 91
84 // Retrieves the file name. 92 // Retrieves the file name.
@@ -176,8 +184,12 @@ public:
176}; 184};
177 185
178// A class representing a directory in an abstract filesystem. 186// A class representing a directory in an abstract filesystem.
179class VfsDirectory : NonCopyable { 187class VfsDirectory {
180public: 188public:
189 YUZU_NON_COPYABLE(VfsDirectory);
190 YUZU_NON_MOVEABLE(VfsDirectory);
191
192 VfsDirectory() = default;
181 virtual ~VfsDirectory(); 193 virtual ~VfsDirectory();
182 194
183 // Retrives the file located at path as if the current directory was root. Returns nullptr if 195 // Retrives the file located at path as if the current directory was root. Returns nullptr if
diff --git a/src/core/frontend/applets/mii.cpp b/src/core/frontend/applets/mii.cpp
new file mode 100644
index 000000000..1c05ff412
--- /dev/null
+++ b/src/core/frontend/applets/mii.cpp
@@ -0,0 +1,19 @@
1// Copyright 2022 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include "common/logging/log.h"
6#include "core/frontend/applets/mii.h"
7
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/frontend/applets/profile_select.cpp b/src/core/frontend/applets/profile_select.cpp
index 3e4f90be2..4c58c310f 100644
--- a/src/core/frontend/applets/profile_select.cpp
+++ b/src/core/frontend/applets/profile_select.cpp
@@ -13,8 +13,7 @@ ProfileSelectApplet::~ProfileSelectApplet() = default;
13void DefaultProfileSelectApplet::SelectProfile( 13void DefaultProfileSelectApplet::SelectProfile(
14 std::function<void(std::optional<Common::UUID>)> callback) const { 14 std::function<void(std::optional<Common::UUID>)> callback) const {
15 Service::Account::ProfileManager manager; 15 Service::Account::ProfileManager manager;
16 callback(manager.GetUser(Settings::values.current_user.GetValue()) 16 callback(manager.GetUser(Settings::values.current_user.GetValue()).value_or(Common::UUID{}));
17 .value_or(Common::UUID{Common::INVALID_UUID}));
18 LOG_INFO(Service_ACC, "called, selecting current user instead of prompting..."); 17 LOG_INFO(Service_ACC, "called, selecting current user instead of prompting...");
19} 18}
20 19
diff --git a/src/core/frontend/emu_window.h b/src/core/frontend/emu_window.h
index e413a520a..b3bffecb2 100644
--- a/src/core/frontend/emu_window.h
+++ b/src/core/frontend/emu_window.h
@@ -42,11 +42,20 @@ public:
42 context.MakeCurrent(); 42 context.MakeCurrent();
43 } 43 }
44 ~Scoped() { 44 ~Scoped() {
45 context.DoneCurrent(); 45 if (active) {
46 context.DoneCurrent();
47 }
48 }
49
50 /// In the event that context was destroyed before the Scoped is destroyed, this provides a
51 /// mechanism to prevent calling a destroyed object's method during the deconstructor
52 void Cancel() {
53 active = false;
46 } 54 }
47 55
48 private: 56 private:
49 GraphicsContext& context; 57 GraphicsContext& context;
58 bool active{true};
50 }; 59 };
51 60
52 /// Calls MakeCurrent on the context and calls DoneCurrent when the scope for the returned value 61 /// Calls MakeCurrent on the context and calls DoneCurrent when the scope for the returned value
diff --git a/src/core/hid/emulated_console.h b/src/core/hid/emulated_console.h
index 707419102..5eb170823 100644
--- a/src/core/hid/emulated_console.h
+++ b/src/core/hid/emulated_console.h
@@ -10,6 +10,7 @@
10#include <mutex> 10#include <mutex>
11#include <unordered_map> 11#include <unordered_map>
12 12
13#include "common/common_funcs.h"
13#include "common/common_types.h" 14#include "common/common_types.h"
14#include "common/input.h" 15#include "common/input.h"
15#include "common/param_package.h" 16#include "common/param_package.h"
diff --git a/src/core/hid/emulated_controller.cpp b/src/core/hid/emulated_controller.cpp
index a7cdf45e6..7e05666d6 100644
--- a/src/core/hid/emulated_controller.cpp
+++ b/src/core/hid/emulated_controller.cpp
@@ -269,7 +269,8 @@ void EmulatedController::ReloadInput() {
269 } 269 }
270 270
271 // Use a common UUID for TAS 271 // Use a common UUID for TAS
272 const auto tas_uuid = Common::UUID{0x0, 0x7A5}; 272 static constexpr Common::UUID TAS_UUID = Common::UUID{
273 {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x7, 0xA5, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}};
273 274
274 // Register TAS devices. No need to force update 275 // Register TAS devices. No need to force update
275 for (std::size_t index = 0; index < tas_button_devices.size(); ++index) { 276 for (std::size_t index = 0; index < tas_button_devices.size(); ++index) {
@@ -278,8 +279,8 @@ void EmulatedController::ReloadInput() {
278 } 279 }
279 tas_button_devices[index]->SetCallback({ 280 tas_button_devices[index]->SetCallback({
280 .on_change = 281 .on_change =
281 [this, index, tas_uuid](const Common::Input::CallbackStatus& callback) { 282 [this, index](const Common::Input::CallbackStatus& callback) {
282 SetButton(callback, index, tas_uuid); 283 SetButton(callback, index, TAS_UUID);
283 }, 284 },
284 }); 285 });
285 } 286 }
@@ -290,8 +291,8 @@ void EmulatedController::ReloadInput() {
290 } 291 }
291 tas_stick_devices[index]->SetCallback({ 292 tas_stick_devices[index]->SetCallback({
292 .on_change = 293 .on_change =
293 [this, index, tas_uuid](const Common::Input::CallbackStatus& callback) { 294 [this, index](const Common::Input::CallbackStatus& callback) {
294 SetStick(callback, index, tas_uuid); 295 SetStick(callback, index, TAS_UUID);
295 }, 296 },
296 }); 297 });
297 } 298 }
@@ -884,6 +885,12 @@ bool EmulatedController::TestVibration(std::size_t device_index) {
884 return SetVibration(device_index, DEFAULT_VIBRATION_VALUE); 885 return SetVibration(device_index, DEFAULT_VIBRATION_VALUE);
885} 886}
886 887
888bool EmulatedController::SetPollingMode(Common::Input::PollingMode polling_mode) {
889 LOG_INFO(Service_HID, "Set polling mode {}", polling_mode);
890 auto& output_device = output_devices[static_cast<std::size_t>(DeviceIndex::Right)];
891 return output_device->SetPollingMode(polling_mode) == Common::Input::PollingError::None;
892}
893
887void EmulatedController::SetLedPattern() { 894void EmulatedController::SetLedPattern() {
888 for (auto& device : output_devices) { 895 for (auto& device : output_devices) {
889 if (!device) { 896 if (!device) {
diff --git a/src/core/hid/emulated_controller.h b/src/core/hid/emulated_controller.h
index a63a83cce..aa52f9572 100644
--- a/src/core/hid/emulated_controller.h
+++ b/src/core/hid/emulated_controller.h
@@ -13,8 +13,6 @@
13#include "common/common_types.h" 13#include "common/common_types.h"
14#include "common/input.h" 14#include "common/input.h"
15#include "common/param_package.h" 15#include "common/param_package.h"
16#include "common/point.h"
17#include "common/quaternion.h"
18#include "common/settings.h" 16#include "common/settings.h"
19#include "common/vector_math.h" 17#include "common/vector_math.h"
20#include "core/hid/hid_types.h" 18#include "core/hid/hid_types.h"
@@ -301,16 +299,23 @@ public:
301 299
302 /** 300 /**
303 * Sends a specific vibration to the output device 301 * Sends a specific vibration to the output device
304 * @return returns true if vibration had no errors 302 * @return true if vibration had no errors
305 */ 303 */
306 bool SetVibration(std::size_t device_index, VibrationValue vibration); 304 bool SetVibration(std::size_t device_index, VibrationValue vibration);
307 305
308 /** 306 /**
309 * Sends a small vibration to the output device 307 * Sends a small vibration to the output device
310 * @return returns true if SetVibration was successfull 308 * @return true if SetVibration was successfull
311 */ 309 */
312 bool TestVibration(std::size_t device_index); 310 bool TestVibration(std::size_t device_index);
313 311
312 /**
313 * Sets the desired data to be polled from a controller
314 * @param polling_mode type of input desired buttons, gyro, nfc, ir, etc.
315 * @return true if SetPollingMode was successfull
316 */
317 bool SetPollingMode(Common::Input::PollingMode polling_mode);
318
314 /// Returns the led pattern corresponding to this emulated controller 319 /// Returns the led pattern corresponding to this emulated controller
315 LedPattern GetLedPattern() const; 320 LedPattern GetLedPattern() const;
316 321
diff --git a/src/core/hid/hid_core.h b/src/core/hid/hid_core.h
index 837f7de49..717f605e7 100644
--- a/src/core/hid/hid_core.h
+++ b/src/core/hid/hid_core.h
@@ -6,6 +6,7 @@
6 6
7#include <memory> 7#include <memory>
8 8
9#include "common/common_funcs.h"
9#include "core/hid/hid_types.h" 10#include "core/hid/hid_types.h"
10 11
11namespace Core::HID { 12namespace Core::HID {
diff --git a/src/core/hid/input_converter.cpp b/src/core/hid/input_converter.cpp
index 860aab400..cd41607a7 100644
--- a/src/core/hid/input_converter.cpp
+++ b/src/core/hid/input_converter.cpp
@@ -28,7 +28,7 @@ Common::Input::BatteryStatus TransformToBattery(const Common::Input::CallbackSta
28 if (value > 0.8f) { 28 if (value > 0.8f) {
29 battery = Common::Input::BatteryLevel::Full; 29 battery = Common::Input::BatteryLevel::Full;
30 } 30 }
31 if (value >= 1.0f) { 31 if (value >= 0.95f) {
32 battery = Common::Input::BatteryLevel::Charging; 32 battery = Common::Input::BatteryLevel::Charging;
33 } 33 }
34 break; 34 break;
diff --git a/src/core/hle/ipc_helpers.h b/src/core/hle/ipc_helpers.h
index cf204f570..3c4e45fcd 100644
--- a/src/core/hle/ipc_helpers.h
+++ b/src/core/hle/ipc_helpers.h
@@ -385,7 +385,7 @@ public:
385 T PopRaw(); 385 T PopRaw();
386 386
387 template <class T> 387 template <class T>
388 std::shared_ptr<T> PopIpcInterface() { 388 std::weak_ptr<T> PopIpcInterface() {
389 ASSERT(context->Session()->IsDomain()); 389 ASSERT(context->Session()->IsDomain());
390 ASSERT(context->GetDomainMessageHeader().input_object_count > 0); 390 ASSERT(context->GetDomainMessageHeader().input_object_count > 0);
391 return context->GetDomainHandler<T>(Pop<u32>() - 1); 391 return context->GetDomainHandler<T>(Pop<u32>() - 1);
@@ -404,6 +404,11 @@ inline s32 RequestParser::Pop() {
404 return static_cast<s32>(Pop<u32>()); 404 return static_cast<s32>(Pop<u32>());
405} 405}
406 406
407// Ignore the -Wclass-memaccess warning on memcpy for non-trivially default constructible objects.
408#if defined(__GNUC__)
409#pragma GCC diagnostic push
410#pragma GCC diagnostic ignored "-Wclass-memaccess"
411#endif
407template <typename T> 412template <typename T>
408void RequestParser::PopRaw(T& value) { 413void RequestParser::PopRaw(T& value) {
409 static_assert(std::is_trivially_copyable_v<T>, 414 static_assert(std::is_trivially_copyable_v<T>,
@@ -411,6 +416,9 @@ void RequestParser::PopRaw(T& value) {
411 std::memcpy(&value, cmdbuf + index, sizeof(T)); 416 std::memcpy(&value, cmdbuf + index, sizeof(T));
412 index += (sizeof(T) + 3) / 4; // round up to word length 417 index += (sizeof(T) + 3) / 4; // round up to word length
413} 418}
419#if defined(__GNUC__)
420#pragma GCC diagnostic pop
421#endif
414 422
415template <typename T> 423template <typename T>
416T RequestParser::PopRaw() { 424T RequestParser::PopRaw() {
diff --git a/src/core/hle/kernel/board/nintendo/nx/k_memory_layout.h b/src/core/hle/kernel/board/nintendo/nx/k_memory_layout.h
new file mode 100644
index 000000000..01e225088
--- /dev/null
+++ b/src/core/hle/kernel/board/nintendo/nx/k_memory_layout.h
@@ -0,0 +1,13 @@
1// Copyright 2022 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include "common/common_types.h"
8
9namespace Kernel {
10
11constexpr inline PAddr MainMemoryAddress = 0x80000000;
12
13} // namespace Kernel
diff --git a/src/core/hle/kernel/board/nintendo/nx/k_system_control.cpp b/src/core/hle/kernel/board/nintendo/nx/k_system_control.cpp
index 6f335c251..8027bec00 100644
--- a/src/core/hle/kernel/board/nintendo/nx/k_system_control.cpp
+++ b/src/core/hle/kernel/board/nintendo/nx/k_system_control.cpp
@@ -5,6 +5,7 @@
5#include <random> 5#include <random>
6 6
7#include "common/literals.h" 7#include "common/literals.h"
8#include "common/settings.h"
8 9
9#include "core/hle/kernel/board/nintendo/nx/k_system_control.h" 10#include "core/hle/kernel/board/nintendo/nx/k_system_control.h"
10#include "core/hle/kernel/board/nintendo/nx/secure_monitor.h" 11#include "core/hle/kernel/board/nintendo/nx/secure_monitor.h"
@@ -28,33 +29,20 @@ namespace {
28 29
29using namespace Common::Literals; 30using namespace Common::Literals;
30 31
31u32 GetMemoryModeForInit() {
32 return 0x01;
33}
34
35u32 GetMemorySizeForInit() { 32u32 GetMemorySizeForInit() {
36 return 0; 33 return Settings::values.use_extended_memory_layout ? Smc::MemorySize_6GB : Smc::MemorySize_4GB;
37} 34}
38 35
39Smc::MemoryArrangement GetMemoryArrangeForInit() { 36Smc::MemoryArrangement GetMemoryArrangeForInit() {
40 switch (GetMemoryModeForInit() & 0x3F) { 37 return Settings::values.use_extended_memory_layout ? Smc::MemoryArrangement_6GB
41 case 0x01: 38 : Smc::MemoryArrangement_4GB;
42 default:
43 return Smc::MemoryArrangement_4GB;
44 case 0x02:
45 return Smc::MemoryArrangement_4GBForAppletDev;
46 case 0x03:
47 return Smc::MemoryArrangement_4GBForSystemDev;
48 case 0x11:
49 return Smc::MemoryArrangement_6GB;
50 case 0x12:
51 return Smc::MemoryArrangement_6GBForAppletDev;
52 case 0x21:
53 return Smc::MemoryArrangement_8GB;
54 }
55} 39}
56} // namespace 40} // namespace
57 41
42size_t KSystemControl::Init::GetRealMemorySize() {
43 return GetIntendedMemorySize();
44}
45
58// Initialization. 46// Initialization.
59size_t KSystemControl::Init::GetIntendedMemorySize() { 47size_t KSystemControl::Init::GetIntendedMemorySize() {
60 switch (GetMemorySizeForInit()) { 48 switch (GetMemorySizeForInit()) {
@@ -69,7 +57,13 @@ size_t KSystemControl::Init::GetIntendedMemorySize() {
69} 57}
70 58
71PAddr KSystemControl::Init::GetKernelPhysicalBaseAddress(u64 base_address) { 59PAddr KSystemControl::Init::GetKernelPhysicalBaseAddress(u64 base_address) {
72 return base_address; 60 const size_t real_dram_size = KSystemControl::Init::GetRealMemorySize();
61 const size_t intended_dram_size = KSystemControl::Init::GetIntendedMemorySize();
62 if (intended_dram_size * 2 < real_dram_size) {
63 return base_address;
64 } else {
65 return base_address + ((real_dram_size - intended_dram_size) / 2);
66 }
73} 67}
74 68
75bool KSystemControl::Init::ShouldIncreaseThreadResourceLimit() { 69bool KSystemControl::Init::ShouldIncreaseThreadResourceLimit() {
diff --git a/src/core/hle/kernel/board/nintendo/nx/k_system_control.h b/src/core/hle/kernel/board/nintendo/nx/k_system_control.h
index 52f230ced..df2a17f2a 100644
--- a/src/core/hle/kernel/board/nintendo/nx/k_system_control.h
+++ b/src/core/hle/kernel/board/nintendo/nx/k_system_control.h
@@ -13,6 +13,7 @@ public:
13 class Init { 13 class Init {
14 public: 14 public:
15 // Initialization. 15 // Initialization.
16 static std::size_t GetRealMemorySize();
16 static std::size_t GetIntendedMemorySize(); 17 static std::size_t GetIntendedMemorySize();
17 static PAddr GetKernelPhysicalBaseAddress(u64 base_address); 18 static PAddr GetKernelPhysicalBaseAddress(u64 base_address);
18 static bool ShouldIncreaseThreadResourceLimit(); 19 static bool ShouldIncreaseThreadResourceLimit();
diff --git a/src/core/hle/kernel/hle_ipc.cpp b/src/core/hle/kernel/hle_ipc.cpp
index e19544c54..9f2175f82 100644
--- a/src/core/hle/kernel/hle_ipc.cpp
+++ b/src/core/hle/kernel/hle_ipc.cpp
@@ -45,7 +45,7 @@ bool SessionRequestManager::HasSessionRequestHandler(const HLERequestContext& co
45 LOG_CRITICAL(IPC, "object_id {} is too big!", object_id); 45 LOG_CRITICAL(IPC, "object_id {} is too big!", object_id);
46 return false; 46 return false;
47 } 47 }
48 return DomainHandler(object_id - 1) != nullptr; 48 return DomainHandler(object_id - 1).lock() != nullptr;
49 } else { 49 } else {
50 return session_handler != nullptr; 50 return session_handler != nullptr;
51 } 51 }
@@ -53,9 +53,6 @@ bool SessionRequestManager::HasSessionRequestHandler(const HLERequestContext& co
53 53
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/initial_process.h b/src/core/hle/kernel/initial_process.h
new file mode 100644
index 000000000..25b27909c
--- /dev/null
+++ b/src/core/hle/kernel/initial_process.h
@@ -0,0 +1,23 @@
1// Copyright 2022 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include "common/common_types.h"
8#include "common/literals.h"
9#include "core/hle/kernel/board/nintendo/nx/k_memory_layout.h"
10#include "core/hle/kernel/board/nintendo/nx/k_system_control.h"
11
12namespace Kernel {
13
14using namespace Common::Literals;
15
16constexpr std::size_t InitialProcessBinarySizeMax = 12_MiB;
17
18static inline PAddr GetInitialProcessBinaryPhysicalAddress() {
19 return Kernel::Board::Nintendo::Nx::KSystemControl::Init::GetKernelPhysicalBaseAddress(
20 MainMemoryAddress);
21}
22
23} // namespace Kernel
diff --git a/src/core/hle/kernel/k_address_arbiter.cpp b/src/core/hle/kernel/k_address_arbiter.cpp
index 783c69858..8cdd0490f 100644
--- a/src/core/hle/kernel/k_address_arbiter.cpp
+++ b/src/core/hle/kernel/k_address_arbiter.cpp
@@ -49,7 +49,7 @@ bool DecrementIfLessThan(Core::System& system, s32* out, VAddr address, s32 valu
49 } 49 }
50 } else { 50 } else {
51 // Otherwise, clear our exclusive hold and finish 51 // Otherwise, clear our exclusive hold and finish
52 monitor.ClearExclusive(); 52 monitor.ClearExclusive(current_core);
53 } 53 }
54 54
55 // We're done. 55 // We're done.
@@ -78,7 +78,7 @@ bool UpdateIfEqual(Core::System& system, s32* out, VAddr address, s32 value, s32
78 } 78 }
79 } else { 79 } else {
80 // Otherwise, clear our exclusive hold and finish. 80 // Otherwise, clear our exclusive hold and finish.
81 monitor.ClearExclusive(); 81 monitor.ClearExclusive(current_core);
82 } 82 }
83 83
84 // We're done. 84 // We're done.
@@ -115,7 +115,7 @@ ResultCode KAddressArbiter::Signal(VAddr addr, s32 count) {
115 { 115 {
116 KScopedSchedulerLock sl(kernel); 116 KScopedSchedulerLock sl(kernel);
117 117
118 auto it = thread_tree.nfind_light({addr, -1}); 118 auto it = thread_tree.nfind_key({addr, -1});
119 while ((it != thread_tree.end()) && (count <= 0 || num_waiters < count) && 119 while ((it != thread_tree.end()) && (count <= 0 || num_waiters < count) &&
120 (it->GetAddressArbiterKey() == addr)) { 120 (it->GetAddressArbiterKey() == addr)) {
121 // End the thread's wait. 121 // End the thread's wait.
@@ -148,7 +148,7 @@ ResultCode KAddressArbiter::SignalAndIncrementIfEqual(VAddr addr, s32 value, s32
148 return ResultInvalidState; 148 return ResultInvalidState;
149 } 149 }
150 150
151 auto it = thread_tree.nfind_light({addr, -1}); 151 auto it = thread_tree.nfind_key({addr, -1});
152 while ((it != thread_tree.end()) && (count <= 0 || num_waiters < count) && 152 while ((it != thread_tree.end()) && (count <= 0 || num_waiters < count) &&
153 (it->GetAddressArbiterKey() == addr)) { 153 (it->GetAddressArbiterKey() == addr)) {
154 // End the thread's wait. 154 // End the thread's wait.
@@ -171,7 +171,7 @@ ResultCode KAddressArbiter::SignalAndModifyByWaitingCountIfEqual(VAddr addr, s32
171 { 171 {
172 [[maybe_unused]] const KScopedSchedulerLock sl(kernel); 172 [[maybe_unused]] const KScopedSchedulerLock sl(kernel);
173 173
174 auto it = thread_tree.nfind_light({addr, -1}); 174 auto it = thread_tree.nfind_key({addr, -1});
175 // Determine the updated value. 175 // Determine the updated value.
176 s32 new_value{}; 176 s32 new_value{};
177 if (count <= 0) { 177 if (count <= 0) {
diff --git a/src/core/hle/kernel/k_auto_object.h b/src/core/hle/kernel/k_auto_object.h
index 165b76747..05779f2d5 100644
--- a/src/core/hle/kernel/k_auto_object.h
+++ b/src/core/hle/kernel/k_auto_object.h
@@ -20,8 +20,6 @@ class KernelCore;
20class KProcess; 20class KProcess;
21 21
22#define KERNEL_AUTOOBJECT_TRAITS(CLASS, BASE_CLASS) \ 22#define KERNEL_AUTOOBJECT_TRAITS(CLASS, BASE_CLASS) \
23 YUZU_NON_COPYABLE(CLASS); \
24 YUZU_NON_MOVEABLE(CLASS); \
25 \ 23 \
26private: \ 24private: \
27 friend class ::Kernel::KClassTokenGenerator; \ 25 friend class ::Kernel::KClassTokenGenerator; \
@@ -32,6 +30,9 @@ private:
32 } \ 30 } \
33 \ 31 \
34public: \ 32public: \
33 YUZU_NON_COPYABLE(CLASS); \
34 YUZU_NON_MOVEABLE(CLASS); \
35 \
35 using BaseClass = BASE_CLASS; \ 36 using BaseClass = BASE_CLASS; \
36 static constexpr TypeObj GetStaticTypeObj() { \ 37 static constexpr TypeObj GetStaticTypeObj() { \
37 constexpr ClassTokenType Token = ClassToken(); \ 38 constexpr ClassTokenType Token = ClassToken(); \
@@ -224,9 +225,9 @@ private:
224 225
225template <typename T> 226template <typename T>
226class KScopedAutoObject { 227class KScopedAutoObject {
228public:
227 YUZU_NON_COPYABLE(KScopedAutoObject); 229 YUZU_NON_COPYABLE(KScopedAutoObject);
228 230
229public:
230 constexpr KScopedAutoObject() = default; 231 constexpr KScopedAutoObject() = default;
231 232
232 constexpr KScopedAutoObject(T* o) : m_obj(o) { 233 constexpr KScopedAutoObject(T* o) : m_obj(o) {
diff --git a/src/core/hle/kernel/k_auto_object_container.h b/src/core/hle/kernel/k_auto_object_container.h
index 4eadfe99d..697cc4289 100644
--- a/src/core/hle/kernel/k_auto_object_container.h
+++ b/src/core/hle/kernel/k_auto_object_container.h
@@ -16,13 +16,12 @@ class KernelCore;
16class KProcess; 16class KProcess;
17 17
18class KAutoObjectWithListContainer { 18class KAutoObjectWithListContainer {
19public:
19 YUZU_NON_COPYABLE(KAutoObjectWithListContainer); 20 YUZU_NON_COPYABLE(KAutoObjectWithListContainer);
20 YUZU_NON_MOVEABLE(KAutoObjectWithListContainer); 21 YUZU_NON_MOVEABLE(KAutoObjectWithListContainer);
21 22
22public:
23 using ListType = boost::intrusive::rbtree<KAutoObjectWithList>; 23 using ListType = boost::intrusive::rbtree<KAutoObjectWithList>;
24 24
25public:
26 class ListAccessor : public KScopedLightLock { 25 class ListAccessor : public KScopedLightLock {
27 public: 26 public:
28 explicit ListAccessor(KAutoObjectWithListContainer* container) 27 explicit ListAccessor(KAutoObjectWithListContainer* container)
@@ -48,7 +47,6 @@ public:
48 47
49 friend class ListAccessor; 48 friend class ListAccessor;
50 49
51public:
52 KAutoObjectWithListContainer(KernelCore& kernel) : m_lock(kernel), m_object_list() {} 50 KAutoObjectWithListContainer(KernelCore& kernel) : m_lock(kernel), m_object_list() {}
53 51
54 void Initialize() {} 52 void Initialize() {}
diff --git a/src/core/hle/kernel/k_code_memory.cpp b/src/core/hle/kernel/k_code_memory.cpp
index d69f7ffb7..0b225e8e0 100644
--- a/src/core/hle/kernel/k_code_memory.cpp
+++ b/src/core/hle/kernel/k_code_memory.cpp
@@ -2,6 +2,7 @@
2// Licensed under GPLv2 or any later version 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include "common/alignment.h"
5#include "common/common_types.h" 6#include "common/common_types.h"
6#include "core/device_memory.h" 7#include "core/device_memory.h"
7#include "core/hle/kernel/k_auto_object.h" 8#include "core/hle/kernel/k_auto_object.h"
@@ -28,8 +29,7 @@ ResultCode KCodeMemory::Initialize(Core::DeviceMemory& device_memory, VAddr addr
28 auto& page_table = m_owner->PageTable(); 29 auto& page_table = m_owner->PageTable();
29 30
30 // Construct the page group. 31 // Construct the page group.
31 KMemoryInfo kBlockInfo = page_table.QueryInfo(addr); 32 m_page_group = KPageLinkedList(addr, Common::DivideUp(size, PageSize));
32 m_page_group = KPageLinkedList(kBlockInfo.GetAddress(), kBlockInfo.GetNumPages());
33 33
34 // Lock the memory. 34 // Lock the memory.
35 R_TRY(page_table.LockForCodeMemory(addr, size)) 35 R_TRY(page_table.LockForCodeMemory(addr, size))
@@ -143,4 +143,4 @@ ResultCode KCodeMemory::UnmapFromOwner(VAddr address, size_t size) {
143 return ResultSuccess; 143 return ResultSuccess;
144} 144}
145 145
146} // namespace Kernel \ No newline at end of file 146} // namespace Kernel
diff --git a/src/core/hle/kernel/k_condition_variable.cpp b/src/core/hle/kernel/k_condition_variable.cpp
index aadcc297a..8e2a9593c 100644
--- a/src/core/hle/kernel/k_condition_variable.cpp
+++ b/src/core/hle/kernel/k_condition_variable.cpp
@@ -244,7 +244,7 @@ void KConditionVariable::Signal(u64 cv_key, s32 count) {
244 { 244 {
245 KScopedSchedulerLock sl(kernel); 245 KScopedSchedulerLock sl(kernel);
246 246
247 auto it = thread_tree.nfind_light({cv_key, -1}); 247 auto it = thread_tree.nfind_key({cv_key, -1});
248 while ((it != thread_tree.end()) && (count <= 0 || num_waiters < count) && 248 while ((it != thread_tree.end()) && (count <= 0 || num_waiters < count) &&
249 (it->GetConditionVariableKey() == cv_key)) { 249 (it->GetConditionVariableKey() == cv_key)) {
250 KThread* target_thread = std::addressof(*it); 250 KThread* target_thread = std::addressof(*it);
diff --git a/src/core/hle/kernel/k_event.cpp b/src/core/hle/kernel/k_event.cpp
index 0720efece..2e0e8de80 100644
--- a/src/core/hle/kernel/k_event.cpp
+++ b/src/core/hle/kernel/k_event.cpp
@@ -14,7 +14,7 @@ KEvent::KEvent(KernelCore& kernel_)
14 14
15KEvent::~KEvent() = default; 15KEvent::~KEvent() = default;
16 16
17void KEvent::Initialize(std::string&& name_) { 17void KEvent::Initialize(std::string&& name_, KProcess* owner_) {
18 // Increment reference count. 18 // Increment reference count.
19 // Because reference count is one on creation, this will result 19 // Because reference count is one on creation, this will result
20 // in a reference count of two. Thus, when both readable and 20 // in a reference count of two. Thus, when both readable and
@@ -30,10 +30,8 @@ void KEvent::Initialize(std::string&& name_) {
30 writable_event.Initialize(this, name_ + ":Writable"); 30 writable_event.Initialize(this, name_ + ":Writable");
31 31
32 // Set our owner process. 32 // Set our owner process.
33 owner = kernel.CurrentProcess(); 33 owner = owner_;
34 if (owner) { 34 owner->Open();
35 owner->Open();
36 }
37 35
38 // Mark initialized. 36 // Mark initialized.
39 name = std::move(name_); 37 name = std::move(name_);
@@ -47,10 +45,8 @@ void KEvent::Finalize() {
47void KEvent::PostDestroy(uintptr_t arg) { 45void KEvent::PostDestroy(uintptr_t arg) {
48 // Release the event count resource the owner process holds. 46 // Release the event count resource the owner process holds.
49 KProcess* owner = reinterpret_cast<KProcess*>(arg); 47 KProcess* owner = reinterpret_cast<KProcess*>(arg);
50 if (owner) { 48 owner->GetResourceLimit()->Release(LimitableResource::Events, 1);
51 owner->GetResourceLimit()->Release(LimitableResource::Events, 1); 49 owner->Close();
52 owner->Close();
53 }
54} 50}
55 51
56} // namespace Kernel 52} // namespace Kernel
diff --git a/src/core/hle/kernel/k_event.h b/src/core/hle/kernel/k_event.h
index 3d3ec99e2..de9732ddf 100644
--- a/src/core/hle/kernel/k_event.h
+++ b/src/core/hle/kernel/k_event.h
@@ -22,7 +22,7 @@ public:
22 explicit KEvent(KernelCore& kernel_); 22 explicit KEvent(KernelCore& kernel_);
23 ~KEvent() override; 23 ~KEvent() override;
24 24
25 void Initialize(std::string&& name); 25 void Initialize(std::string&& name, KProcess* owner_);
26 26
27 void Finalize() override; 27 void Finalize() override;
28 28
diff --git a/src/core/hle/kernel/k_handle_table.cpp b/src/core/hle/kernel/k_handle_table.cpp
index cf95f0852..db7512ee7 100644
--- a/src/core/hle/kernel/k_handle_table.cpp
+++ b/src/core/hle/kernel/k_handle_table.cpp
@@ -63,7 +63,7 @@ bool KHandleTable::Remove(Handle handle) {
63 return true; 63 return true;
64} 64}
65 65
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 4b114ec2f..dd27689b6 100644
--- a/src/core/hle/kernel/k_handle_table.h
+++ b/src/core/hle/kernel/k_handle_table.h
@@ -22,13 +22,12 @@ namespace Kernel {
22class KernelCore; 22class KernelCore;
23 23
24class KHandleTable { 24class KHandleTable {
25public:
25 YUZU_NON_COPYABLE(KHandleTable); 26 YUZU_NON_COPYABLE(KHandleTable);
26 YUZU_NON_MOVEABLE(KHandleTable); 27 YUZU_NON_MOVEABLE(KHandleTable);
27 28
28public:
29 static constexpr size_t MaxTableSize = 1024; 29 static constexpr size_t MaxTableSize = 1024;
30 30
31public:
32 explicit KHandleTable(KernelCore& kernel_); 31 explicit KHandleTable(KernelCore& kernel_);
33 ~KHandleTable(); 32 ~KHandleTable();
34 33
@@ -43,7 +42,7 @@ public:
43 m_free_head_index = -1; 42 m_free_head_index = -1;
44 43
45 // Free all entries. 44 // Free all entries.
46 for (s32 i = 0; i < static_cast<s32>(m_table_size); ++i) { 45 for (s16 i = 0; i < static_cast<s16>(m_table_size); ++i) {
47 m_objects[i] = nullptr; 46 m_objects[i] = nullptr;
48 m_entry_infos[i].next_free_index = i - 1; 47 m_entry_infos[i].next_free_index = i - 1;
49 m_free_head_index = i; 48 m_free_head_index = i;
@@ -105,17 +104,8 @@ public:
105 ResultCode Reserve(Handle* out_handle); 104 ResultCode Reserve(Handle* out_handle);
106 void Unreserve(Handle handle); 105 void Unreserve(Handle handle);
107 106
108 template <typename T> 107 ResultCode Add(Handle* out_handle, KAutoObject* obj);
109 ResultCode Add(Handle* out_handle, T* obj) { 108 void Register(Handle handle, KAutoObject* obj);
110 static_assert(std::is_base_of_v<KAutoObject, T>);
111 return this->Add(out_handle, obj, obj->GetTypeObj().GetClassToken());
112 }
113
114 template <typename T>
115 void Register(Handle handle, T* obj) {
116 static_assert(std::is_base_of_v<KAutoObject, T>);
117 return this->Register(handle, obj, obj->GetTypeObj().GetClassToken());
118 }
119 109
120 template <typename T> 110 template <typename T>
121 bool GetMultipleObjects(T** out, const Handle* handles, size_t num_handles) const { 111 bool GetMultipleObjects(T** out, const Handle* handles, size_t num_handles) const {
@@ -161,9 +151,6 @@ public:
161 } 151 }
162 152
163private: 153private:
164 ResultCode Add(Handle* out_handle, KAutoObject* obj, u16 type);
165 void Register(Handle handle, KAutoObject* obj, u16 type);
166
167 s32 AllocateEntry() { 154 s32 AllocateEntry() {
168 ASSERT(m_count < m_table_size); 155 ASSERT(m_count < m_table_size);
169 156
@@ -180,7 +167,7 @@ private:
180 ASSERT(m_count > 0); 167 ASSERT(m_count > 0);
181 168
182 m_objects[index] = nullptr; 169 m_objects[index] = nullptr;
183 m_entry_infos[index].next_free_index = m_free_head_index; 170 m_entry_infos[index].next_free_index = static_cast<s16>(m_free_head_index);
184 171
185 m_free_head_index = index; 172 m_free_head_index = index;
186 173
@@ -279,19 +266,13 @@ private:
279 } 266 }
280 267
281 union EntryInfo { 268 union EntryInfo {
282 struct { 269 u16 linear_id;
283 u16 linear_id; 270 s16 next_free_index;
284 u16 type;
285 } info;
286 s32 next_free_index;
287 271
288 constexpr u16 GetLinearId() const { 272 constexpr u16 GetLinearId() const {
289 return info.linear_id; 273 return linear_id;
290 }
291 constexpr u16 GetType() const {
292 return info.type;
293 } 274 }
294 constexpr s32 GetNextFreeIndex() const { 275 constexpr s16 GetNextFreeIndex() const {
295 return next_free_index; 276 return next_free_index;
296 } 277 }
297 }; 278 };
diff --git a/src/core/hle/kernel/k_memory_layout.h b/src/core/hle/kernel/k_memory_layout.h
index 57ff538cc..0858827b6 100644
--- a/src/core/hle/kernel/k_memory_layout.h
+++ b/src/core/hle/kernel/k_memory_layout.h
@@ -57,11 +57,11 @@ constexpr std::size_t KernelPageTableHeapSize = GetMaximumOverheadSize(MainMemor
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;
@@ -173,6 +173,10 @@ public:
173 return Dereference(FindVirtualLinear(address)); 173 return Dereference(FindVirtualLinear(address));
174 } 174 }
175 175
176 const KMemoryRegion& GetPhysicalLinearRegion(PAddr address) const {
177 return Dereference(FindPhysicalLinear(address));
178 }
179
176 const KMemoryRegion* GetPhysicalKernelTraceBufferRegion() const { 180 const KMemoryRegion* GetPhysicalKernelTraceBufferRegion() const {
177 return GetPhysicalMemoryRegionTree().FindFirstDerived(KMemoryRegionType_KernelTraceBuffer); 181 return GetPhysicalMemoryRegionTree().FindFirstDerived(KMemoryRegionType_KernelTraceBuffer);
178 } 182 }
diff --git a/src/core/hle/kernel/k_memory_manager.cpp b/src/core/hle/kernel/k_memory_manager.cpp
index 1b44541b1..a2f18f643 100644
--- a/src/core/hle/kernel/k_memory_manager.cpp
+++ b/src/core/hle/kernel/k_memory_manager.cpp
@@ -10,189 +10,412 @@
10#include "common/scope_exit.h" 10#include "common/scope_exit.h"
11#include "core/core.h" 11#include "core/core.h"
12#include "core/device_memory.h" 12#include "core/device_memory.h"
13#include "core/hle/kernel/initial_process.h"
13#include "core/hle/kernel/k_memory_manager.h" 14#include "core/hle/kernel/k_memory_manager.h"
14#include "core/hle/kernel/k_page_linked_list.h" 15#include "core/hle/kernel/k_page_linked_list.h"
16#include "core/hle/kernel/kernel.h"
15#include "core/hle/kernel/svc_results.h" 17#include "core/hle/kernel/svc_results.h"
18#include "core/memory.h"
16 19
17namespace Kernel { 20namespace Kernel {
18 21
19KMemoryManager::KMemoryManager(Core::System& system_) : system{system_} {} 22namespace {
23
24constexpr KMemoryManager::Pool GetPoolFromMemoryRegionType(u32 type) {
25 if ((type | KMemoryRegionType_DramApplicationPool) == type) {
26 return KMemoryManager::Pool::Application;
27 } else if ((type | KMemoryRegionType_DramAppletPool) == type) {
28 return KMemoryManager::Pool::Applet;
29 } else if ((type | KMemoryRegionType_DramSystemPool) == type) {
30 return KMemoryManager::Pool::System;
31 } else if ((type | KMemoryRegionType_DramSystemNonSecurePool) == type) {
32 return KMemoryManager::Pool::SystemNonSecure;
33 } else {
34 UNREACHABLE_MSG("InvalidMemoryRegionType for conversion to Pool");
35 return {};
36 }
37}
20 38
21std::size_t KMemoryManager::Impl::Initialize(Pool new_pool, u64 start_address, u64 end_address) { 39} // namespace
22 const auto size{end_address - start_address}; 40
41KMemoryManager::KMemoryManager(Core::System& system_)
42 : system{system_}, pool_locks{
43 KLightLock{system_.Kernel()},
44 KLightLock{system_.Kernel()},
45 KLightLock{system_.Kernel()},
46 KLightLock{system_.Kernel()},
47 } {}
48
49void KMemoryManager::Initialize(VAddr management_region, size_t management_region_size) {
50
51 // Clear the management region to zero.
52 const VAddr management_region_end = management_region + management_region_size;
53
54 // Reset our manager count.
55 num_managers = 0;
56
57 // Traverse the virtual memory layout tree, initializing each manager as appropriate.
58 while (num_managers != MaxManagerCount) {
59 // Locate the region that should initialize the current manager.
60 PAddr region_address = 0;
61 size_t region_size = 0;
62 Pool region_pool = Pool::Count;
63 for (const auto& it : system.Kernel().MemoryLayout().GetPhysicalMemoryRegionTree()) {
64 // We only care about regions that we need to create managers for.
65 if (!it.IsDerivedFrom(KMemoryRegionType_DramUserPool)) {
66 continue;
67 }
23 68
24 // Calculate metadata sizes 69 // We want to initialize the managers in order.
25 const auto ref_count_size{(size / PageSize) * sizeof(u16)}; 70 if (it.GetAttributes() != num_managers) {
26 const auto optimize_map_size{(Common::AlignUp((size / PageSize), 64) / 64) * sizeof(u64)}; 71 continue;
27 const auto manager_size{Common::AlignUp(optimize_map_size + ref_count_size, PageSize)}; 72 }
28 const auto page_heap_size{KPageHeap::CalculateManagementOverheadSize(size)};
29 const auto total_metadata_size{manager_size + page_heap_size};
30 ASSERT(manager_size <= total_metadata_size);
31 ASSERT(Common::IsAligned(total_metadata_size, PageSize));
32 73
33 // Setup region 74 const PAddr cur_start = it.GetAddress();
34 pool = new_pool; 75 const PAddr cur_end = it.GetEndAddress();
76
77 // Validate the region.
78 ASSERT(cur_end != 0);
79 ASSERT(cur_start != 0);
80 ASSERT(it.GetSize() > 0);
81
82 // Update the region's extents.
83 if (region_address == 0) {
84 region_address = cur_start;
85 region_size = it.GetSize();
86 region_pool = GetPoolFromMemoryRegionType(it.GetType());
87 } else {
88 ASSERT(cur_start == region_address + region_size);
89
90 // Update the size.
91 region_size = cur_end - region_address;
92 ASSERT(GetPoolFromMemoryRegionType(it.GetType()) == region_pool);
93 }
94 }
95
96 // If we didn't find a region, we're done.
97 if (region_size == 0) {
98 break;
99 }
35 100
36 // Initialize the manager's KPageHeap 101 // Initialize a new manager for the region.
37 heap.Initialize(start_address, size, page_heap_size); 102 Impl* manager = std::addressof(managers[num_managers++]);
103 ASSERT(num_managers <= managers.size());
104
105 const size_t cur_size = manager->Initialize(region_address, region_size, management_region,
106 management_region_end, region_pool);
107 management_region += cur_size;
108 ASSERT(management_region <= management_region_end);
109
110 // Insert the manager into the pool list.
111 const auto region_pool_index = static_cast<u32>(region_pool);
112 if (pool_managers_tail[region_pool_index] == nullptr) {
113 pool_managers_head[region_pool_index] = manager;
114 } else {
115 pool_managers_tail[region_pool_index]->SetNext(manager);
116 manager->SetPrev(pool_managers_tail[region_pool_index]);
117 }
118 pool_managers_tail[region_pool_index] = manager;
119 }
38 120
39 // Free the memory to the heap 121 // Free each region to its corresponding heap.
40 heap.Free(start_address, size / PageSize); 122 size_t reserved_sizes[MaxManagerCount] = {};
123 const PAddr ini_start = GetInitialProcessBinaryPhysicalAddress();
124 const PAddr ini_end = ini_start + InitialProcessBinarySizeMax;
125 const PAddr ini_last = ini_end - 1;
126 for (const auto& it : system.Kernel().MemoryLayout().GetPhysicalMemoryRegionTree()) {
127 if (it.IsDerivedFrom(KMemoryRegionType_DramUserPool)) {
128 // Get the manager for the region.
129 auto index = it.GetAttributes();
130 auto& manager = managers[index];
131
132 const PAddr cur_start = it.GetAddress();
133 const PAddr cur_last = it.GetLastAddress();
134 const PAddr cur_end = it.GetEndAddress();
135
136 if (cur_start <= ini_start && ini_last <= cur_last) {
137 // Free memory before the ini to the heap.
138 if (cur_start != ini_start) {
139 manager.Free(cur_start, (ini_start - cur_start) / PageSize);
140 }
41 141
42 // Update the heap's used size 142 // Open/reserve the ini memory.
43 heap.UpdateUsedSize(); 143 manager.OpenFirst(ini_start, InitialProcessBinarySizeMax / PageSize);
144 reserved_sizes[it.GetAttributes()] += InitialProcessBinarySizeMax;
44 145
45 return total_metadata_size; 146 // Free memory after the ini to the heap.
46} 147 if (ini_last != cur_last) {
148 ASSERT(cur_end != 0);
149 manager.Free(ini_end, cur_end - ini_end);
150 }
151 } else {
152 // Ensure there's no partial overlap with the ini image.
153 if (cur_start <= ini_last) {
154 ASSERT(cur_last < ini_start);
155 } else {
156 // Otherwise, check the region for general validity.
157 ASSERT(cur_end != 0);
158 }
47 159
48void KMemoryManager::InitializeManager(Pool pool, u64 start_address, u64 end_address) { 160 // Free the memory to the heap.
49 ASSERT(pool < Pool::Count); 161 manager.Free(cur_start, it.GetSize() / PageSize);
50 managers[static_cast<std::size_t>(pool)].Initialize(pool, start_address, end_address); 162 }
163 }
164 }
165
166 // Update the used size for all managers.
167 for (size_t i = 0; i < num_managers; ++i) {
168 managers[i].SetInitialUsedHeapSize(reserved_sizes[i]);
169 }
51} 170}
52 171
53VAddr KMemoryManager::AllocateAndOpenContinuous(std::size_t num_pages, std::size_t align_pages, 172PAddr KMemoryManager::AllocateAndOpenContinuous(size_t num_pages, size_t align_pages, u32 option) {
54 u32 option) { 173 // Early return if we're allocating no pages.
55 // Early return if we're allocating no pages
56 if (num_pages == 0) { 174 if (num_pages == 0) {
57 return {}; 175 return 0;
58 } 176 }
59 177
60 // Lock the pool that we're allocating from 178 // Lock the pool that we're allocating from.
61 const auto [pool, dir] = DecodeOption(option); 179 const auto [pool, dir] = DecodeOption(option);
62 const auto pool_index{static_cast<std::size_t>(pool)}; 180 KScopedLightLock lk(pool_locks[static_cast<std::size_t>(pool)]);
63 std::lock_guard lock{pool_locks[pool_index]}; 181
64 182 // Choose a heap based on our page size request.
65 // Choose a heap based on our page size request 183 const s32 heap_index = KPageHeap::GetAlignedBlockIndex(num_pages, align_pages);
66 const s32 heap_index{KPageHeap::GetAlignedBlockIndex(num_pages, align_pages)}; 184
67 185 // Loop, trying to iterate from each block.
68 // Loop, trying to iterate from each block 186 Impl* chosen_manager = nullptr;
69 // TODO (bunnei): Support multiple managers 187 PAddr allocated_block = 0;
70 Impl& chosen_manager{managers[pool_index]}; 188 for (chosen_manager = this->GetFirstManager(pool, dir); chosen_manager != nullptr;
71 VAddr allocated_block{chosen_manager.AllocateBlock(heap_index, false)}; 189 chosen_manager = this->GetNextManager(chosen_manager, dir)) {
190 allocated_block = chosen_manager->AllocateBlock(heap_index, true);
191 if (allocated_block != 0) {
192 break;
193 }
194 }
72 195
73 // If we failed to allocate, quit now 196 // If we failed to allocate, quit now.
74 if (!allocated_block) { 197 if (allocated_block == 0) {
75 return {}; 198 return 0;
76 } 199 }
77 200
78 // If we allocated more than we need, free some 201 // If we allocated more than we need, free some.
79 const auto allocated_pages{KPageHeap::GetBlockNumPages(heap_index)}; 202 const size_t allocated_pages = KPageHeap::GetBlockNumPages(heap_index);
80 if (allocated_pages > num_pages) { 203 if (allocated_pages > num_pages) {
81 chosen_manager.Free(allocated_block + num_pages * PageSize, allocated_pages - num_pages); 204 chosen_manager->Free(allocated_block + num_pages * PageSize, allocated_pages - num_pages);
82 } 205 }
83 206
207 // Open the first reference to the pages.
208 chosen_manager->OpenFirst(allocated_block, num_pages);
209
84 return allocated_block; 210 return allocated_block;
85} 211}
86 212
87ResultCode KMemoryManager::Allocate(KPageLinkedList& page_list, std::size_t num_pages, Pool pool, 213ResultCode KMemoryManager::AllocatePageGroupImpl(KPageLinkedList* out, size_t num_pages, Pool pool,
88 Direction dir, u32 heap_fill_value) { 214 Direction dir, bool random) {
89 ASSERT(page_list.GetNumPages() == 0); 215 // Choose a heap based on our page size request.
216 const s32 heap_index = KPageHeap::GetBlockIndex(num_pages);
217 R_UNLESS(0 <= heap_index, ResultOutOfMemory);
218
219 // Ensure that we don't leave anything un-freed.
220 auto group_guard = SCOPE_GUARD({
221 for (const auto& it : out->Nodes()) {
222 auto& manager = this->GetManager(system.Kernel().MemoryLayout(), it.GetAddress());
223 const size_t num_pages_to_free =
224 std::min(it.GetNumPages(), (manager.GetEndAddress() - it.GetAddress()) / PageSize);
225 manager.Free(it.GetAddress(), num_pages_to_free);
226 }
227 });
90 228
91 // Early return if we're allocating no pages 229 // Keep allocating until we've allocated all our pages.
92 if (num_pages == 0) { 230 for (s32 index = heap_index; index >= 0 && num_pages > 0; index--) {
93 return ResultSuccess; 231 const size_t pages_per_alloc = KPageHeap::GetBlockNumPages(index);
94 } 232 for (Impl* cur_manager = this->GetFirstManager(pool, dir); cur_manager != nullptr;
233 cur_manager = this->GetNextManager(cur_manager, dir)) {
234 while (num_pages >= pages_per_alloc) {
235 // Allocate a block.
236 PAddr allocated_block = cur_manager->AllocateBlock(index, random);
237 if (allocated_block == 0) {
238 break;
239 }
95 240
96 // Lock the pool that we're allocating from 241 // Safely add it to our group.
97 const auto pool_index{static_cast<std::size_t>(pool)}; 242 {
98 std::lock_guard lock{pool_locks[pool_index]}; 243 auto block_guard =
244 SCOPE_GUARD({ cur_manager->Free(allocated_block, pages_per_alloc); });
245 R_TRY(out->AddBlock(allocated_block, pages_per_alloc));
246 block_guard.Cancel();
247 }
99 248
100 // Choose a heap based on our page size request 249 num_pages -= pages_per_alloc;
101 const s32 heap_index{KPageHeap::GetBlockIndex(num_pages)}; 250 }
102 if (heap_index < 0) { 251 }
103 return ResultOutOfMemory;
104 } 252 }
105 253
106 // TODO (bunnei): Support multiple managers 254 // Only succeed if we allocated as many pages as we wanted.
107 Impl& chosen_manager{managers[pool_index]}; 255 R_UNLESS(num_pages == 0, ResultOutOfMemory);
108 256
109 // Ensure that we don't leave anything un-freed 257 // We succeeded!
110 auto group_guard = detail::ScopeExit([&] { 258 group_guard.Cancel();
111 for (const auto& it : page_list.Nodes()) { 259 return ResultSuccess;
112 const auto min_num_pages{std::min<size_t>( 260}
113 it.GetNumPages(), (chosen_manager.GetEndAddress() - it.GetAddress()) / PageSize)};
114 chosen_manager.Free(it.GetAddress(), min_num_pages);
115 }
116 });
117 261
118 // Keep allocating until we've allocated all our pages 262ResultCode KMemoryManager::AllocateAndOpen(KPageLinkedList* out, size_t num_pages, u32 option) {
119 for (s32 index{heap_index}; index >= 0 && num_pages > 0; index--) { 263 ASSERT(out != nullptr);
120 const auto pages_per_alloc{KPageHeap::GetBlockNumPages(index)}; 264 ASSERT(out->GetNumPages() == 0);
121 265
122 while (num_pages >= pages_per_alloc) { 266 // Early return if we're allocating no pages.
123 // Allocate a block 267 R_SUCCEED_IF(num_pages == 0);
124 VAddr allocated_block{chosen_manager.AllocateBlock(index, false)};
125 if (!allocated_block) {
126 break;
127 }
128 268
129 // Safely add it to our group 269 // Lock the pool that we're allocating from.
130 { 270 const auto [pool, dir] = DecodeOption(option);
131 auto block_guard = detail::ScopeExit( 271 KScopedLightLock lk(pool_locks[static_cast<size_t>(pool)]);
132 [&] { chosen_manager.Free(allocated_block, pages_per_alloc); }); 272
273 // Allocate the page group.
274 R_TRY(this->AllocatePageGroupImpl(out, num_pages, pool, dir, false));
275
276 // Open the first reference to the pages.
277 for (const auto& block : out->Nodes()) {
278 PAddr cur_address = block.GetAddress();
279 size_t remaining_pages = block.GetNumPages();
280 while (remaining_pages > 0) {
281 // Get the manager for the current address.
282 auto& manager = this->GetManager(system.Kernel().MemoryLayout(), cur_address);
283
284 // Process part or all of the block.
285 const size_t cur_pages =
286 std::min(remaining_pages, manager.GetPageOffsetToEnd(cur_address));
287 manager.OpenFirst(cur_address, cur_pages);
288
289 // Advance.
290 cur_address += cur_pages * PageSize;
291 remaining_pages -= cur_pages;
292 }
293 }
133 294
134 if (const ResultCode result{page_list.AddBlock(allocated_block, pages_per_alloc)}; 295 return ResultSuccess;
135 result.IsError()) { 296}
136 return result;
137 }
138 297
139 block_guard.Cancel(); 298ResultCode KMemoryManager::AllocateAndOpenForProcess(KPageLinkedList* out, size_t num_pages,
140 } 299 u32 option, u64 process_id, u8 fill_pattern) {
300 ASSERT(out != nullptr);
301 ASSERT(out->GetNumPages() == 0);
141 302
142 num_pages -= pages_per_alloc; 303 // Decode the option.
143 } 304 const auto [pool, dir] = DecodeOption(option);
144 }
145 305
146 // Clear allocated memory. 306 // Allocate the memory.
147 for (const auto& it : page_list.Nodes()) { 307 {
148 std::memset(system.DeviceMemory().GetPointer(it.GetAddress()), heap_fill_value, 308 // Lock the pool that we're allocating from.
149 it.GetSize()); 309 KScopedLightLock lk(pool_locks[static_cast<size_t>(pool)]);
310
311 // Allocate the page group.
312 R_TRY(this->AllocatePageGroupImpl(out, num_pages, pool, dir, false));
313
314 // Open the first reference to the pages.
315 for (const auto& block : out->Nodes()) {
316 PAddr cur_address = block.GetAddress();
317 size_t remaining_pages = block.GetNumPages();
318 while (remaining_pages > 0) {
319 // Get the manager for the current address.
320 auto& manager = this->GetManager(system.Kernel().MemoryLayout(), cur_address);
321
322 // Process part or all of the block.
323 const size_t cur_pages =
324 std::min(remaining_pages, manager.GetPageOffsetToEnd(cur_address));
325 manager.OpenFirst(cur_address, cur_pages);
326
327 // Advance.
328 cur_address += cur_pages * PageSize;
329 remaining_pages -= cur_pages;
330 }
331 }
150 } 332 }
151 333
152 // Only succeed if we allocated as many pages as we wanted 334 // Set all the allocated memory.
153 if (num_pages) { 335 for (const auto& block : out->Nodes()) {
154 return ResultOutOfMemory; 336 std::memset(system.DeviceMemory().GetPointer(block.GetAddress()), fill_pattern,
337 block.GetSize());
155 } 338 }
156 339
157 // We succeeded!
158 group_guard.Cancel();
159
160 return ResultSuccess; 340 return ResultSuccess;
161} 341}
162 342
163ResultCode KMemoryManager::Free(KPageLinkedList& page_list, std::size_t num_pages, Pool pool, 343void KMemoryManager::Open(PAddr address, size_t num_pages) {
164 Direction dir, u32 heap_fill_value) { 344 // Repeatedly open references until we've done so for all pages.
165 // Early return if we're freeing no pages 345 while (num_pages) {
166 if (!num_pages) { 346 auto& manager = this->GetManager(system.Kernel().MemoryLayout(), address);
167 return ResultSuccess; 347 const size_t cur_pages = std::min(num_pages, manager.GetPageOffsetToEnd(address));
348
349 {
350 KScopedLightLock lk(pool_locks[static_cast<size_t>(manager.GetPool())]);
351 manager.Open(address, cur_pages);
352 }
353
354 num_pages -= cur_pages;
355 address += cur_pages * PageSize;
168 } 356 }
357}
169 358
170 // Lock the pool that we're freeing from 359void KMemoryManager::Close(PAddr address, size_t num_pages) {
171 const auto pool_index{static_cast<std::size_t>(pool)}; 360 // Repeatedly close references until we've done so for all pages.
172 std::lock_guard lock{pool_locks[pool_index]}; 361 while (num_pages) {
362 auto& manager = this->GetManager(system.Kernel().MemoryLayout(), address);
363 const size_t cur_pages = std::min(num_pages, manager.GetPageOffsetToEnd(address));
173 364
174 // TODO (bunnei): Support multiple managers 365 {
175 Impl& chosen_manager{managers[pool_index]}; 366 KScopedLightLock lk(pool_locks[static_cast<size_t>(manager.GetPool())]);
367 manager.Close(address, cur_pages);
368 }
176 369
177 // Free all of the pages 370 num_pages -= cur_pages;
178 for (const auto& it : page_list.Nodes()) { 371 address += cur_pages * PageSize;
179 const auto min_num_pages{std::min<size_t>(
180 it.GetNumPages(), (chosen_manager.GetEndAddress() - it.GetAddress()) / PageSize)};
181 chosen_manager.Free(it.GetAddress(), min_num_pages);
182 } 372 }
373}
183 374
184 return ResultSuccess; 375void KMemoryManager::Close(const KPageLinkedList& pg) {
376 for (const auto& node : pg.Nodes()) {
377 Close(node.GetAddress(), node.GetNumPages());
378 }
379}
380void KMemoryManager::Open(const KPageLinkedList& pg) {
381 for (const auto& node : pg.Nodes()) {
382 Open(node.GetAddress(), node.GetNumPages());
383 }
384}
385
386size_t KMemoryManager::Impl::Initialize(PAddr address, size_t size, VAddr management,
387 VAddr management_end, Pool p) {
388 // Calculate management sizes.
389 const size_t ref_count_size = (size / PageSize) * sizeof(u16);
390 const size_t optimize_map_size = CalculateOptimizedProcessOverheadSize(size);
391 const size_t manager_size = Common::AlignUp(optimize_map_size + ref_count_size, PageSize);
392 const size_t page_heap_size = KPageHeap::CalculateManagementOverheadSize(size);
393 const size_t total_management_size = manager_size + page_heap_size;
394 ASSERT(manager_size <= total_management_size);
395 ASSERT(management + total_management_size <= management_end);
396 ASSERT(Common::IsAligned(total_management_size, PageSize));
397
398 // Setup region.
399 pool = p;
400 management_region = management;
401 page_reference_counts.resize(
402 Kernel::Board::Nintendo::Nx::KSystemControl::Init::GetIntendedMemorySize() / PageSize);
403 ASSERT(Common::IsAligned(management_region, PageSize));
404
405 // Initialize the manager's KPageHeap.
406 heap.Initialize(address, size, management + manager_size, page_heap_size);
407
408 return total_management_size;
185} 409}
186 410
187std::size_t KMemoryManager::Impl::CalculateManagementOverheadSize(std::size_t region_size) { 411size_t KMemoryManager::Impl::CalculateManagementOverheadSize(size_t region_size) {
188 const std::size_t ref_count_size = (region_size / PageSize) * sizeof(u16); 412 const size_t ref_count_size = (region_size / PageSize) * sizeof(u16);
189 const std::size_t optimize_map_size = 413 const size_t optimize_map_size =
190 (Common::AlignUp((region_size / PageSize), Common::BitSize<u64>()) / 414 (Common::AlignUp((region_size / PageSize), Common::BitSize<u64>()) /
191 Common::BitSize<u64>()) * 415 Common::BitSize<u64>()) *
192 sizeof(u64); 416 sizeof(u64);
193 const std::size_t manager_meta_size = 417 const size_t manager_meta_size = Common::AlignUp(optimize_map_size + ref_count_size, PageSize);
194 Common::AlignUp(optimize_map_size + ref_count_size, PageSize); 418 const size_t page_heap_size = KPageHeap::CalculateManagementOverheadSize(region_size);
195 const std::size_t page_heap_size = KPageHeap::CalculateManagementOverheadSize(region_size);
196 return manager_meta_size + page_heap_size; 419 return manager_meta_size + page_heap_size;
197} 420}
198 421
diff --git a/src/core/hle/kernel/k_memory_manager.h b/src/core/hle/kernel/k_memory_manager.h
index abd6c8ace..18775b262 100644
--- a/src/core/hle/kernel/k_memory_manager.h
+++ b/src/core/hle/kernel/k_memory_manager.h
@@ -5,10 +5,12 @@
5#pragma once 5#pragma once
6 6
7#include <array> 7#include <array>
8#include <mutex>
9#include <tuple> 8#include <tuple>
10 9
10#include "common/common_funcs.h"
11#include "common/common_types.h" 11#include "common/common_types.h"
12#include "core/hle/kernel/k_light_lock.h"
13#include "core/hle/kernel/k_memory_layout.h"
12#include "core/hle/kernel/k_page_heap.h" 14#include "core/hle/kernel/k_page_heap.h"
13#include "core/hle/result.h" 15#include "core/hle/result.h"
14 16
@@ -20,8 +22,11 @@ namespace Kernel {
20 22
21class KPageLinkedList; 23class KPageLinkedList;
22 24
23class KMemoryManager final : NonCopyable { 25class KMemoryManager final {
24public: 26public:
27 YUZU_NON_COPYABLE(KMemoryManager);
28 YUZU_NON_MOVEABLE(KMemoryManager);
29
25 enum class Pool : u32 { 30 enum class Pool : u32 {
26 Application = 0, 31 Application = 0,
27 Applet = 1, 32 Applet = 1,
@@ -48,22 +53,33 @@ public:
48 53
49 explicit KMemoryManager(Core::System& system_); 54 explicit KMemoryManager(Core::System& system_);
50 55
51 constexpr std::size_t GetSize(Pool pool) const { 56 void Initialize(VAddr management_region, size_t management_region_size);
52 return managers[static_cast<std::size_t>(pool)].GetSize(); 57
58 constexpr size_t GetSize(Pool pool) const {
59 constexpr Direction GetSizeDirection = Direction::FromFront;
60 size_t total = 0;
61 for (auto* manager = this->GetFirstManager(pool, GetSizeDirection); manager != nullptr;
62 manager = this->GetNextManager(manager, GetSizeDirection)) {
63 total += manager->GetSize();
64 }
65 return total;
53 } 66 }
54 67
55 void InitializeManager(Pool pool, u64 start_address, u64 end_address); 68 PAddr AllocateAndOpenContinuous(size_t num_pages, size_t align_pages, u32 option);
69 ResultCode AllocateAndOpen(KPageLinkedList* out, size_t num_pages, u32 option);
70 ResultCode AllocateAndOpenForProcess(KPageLinkedList* out, size_t num_pages, u32 option,
71 u64 process_id, u8 fill_pattern);
72
73 static constexpr size_t MaxManagerCount = 10;
56 74
57 VAddr AllocateAndOpenContinuous(size_t num_pages, size_t align_pages, u32 option); 75 void Close(PAddr address, size_t num_pages);
58 ResultCode Allocate(KPageLinkedList& page_list, std::size_t num_pages, Pool pool, Direction dir, 76 void Close(const KPageLinkedList& pg);
59 u32 heap_fill_value = 0);
60 ResultCode Free(KPageLinkedList& page_list, std::size_t num_pages, Pool pool, Direction dir,
61 u32 heap_fill_value = 0);
62 77
63 static constexpr std::size_t MaxManagerCount = 10; 78 void Open(PAddr address, size_t num_pages);
79 void Open(const KPageLinkedList& pg);
64 80
65public: 81public:
66 static std::size_t CalculateManagementOverheadSize(std::size_t region_size) { 82 static size_t CalculateManagementOverheadSize(size_t region_size) {
67 return Impl::CalculateManagementOverheadSize(region_size); 83 return Impl::CalculateManagementOverheadSize(region_size);
68 } 84 }
69 85
@@ -88,38 +104,34 @@ public:
88 } 104 }
89 105
90private: 106private:
91 class Impl final : NonCopyable { 107 class Impl final {
92 private:
93 using RefCount = u16;
94
95 private:
96 KPageHeap heap;
97 Pool pool{};
98
99 public: 108 public:
100 static std::size_t CalculateManagementOverheadSize(std::size_t region_size); 109 YUZU_NON_COPYABLE(Impl);
101 110 YUZU_NON_MOVEABLE(Impl);
102 static constexpr std::size_t CalculateOptimizedProcessOverheadSize(
103 std::size_t region_size) {
104 return (Common::AlignUp((region_size / PageSize), Common::BitSize<u64>()) /
105 Common::BitSize<u64>()) *
106 sizeof(u64);
107 }
108 111
109 public:
110 Impl() = default; 112 Impl() = default;
113 ~Impl() = default;
111 114
112 std::size_t Initialize(Pool new_pool, u64 start_address, u64 end_address); 115 size_t Initialize(PAddr address, size_t size, VAddr management, VAddr management_end,
116 Pool p);
113 117
114 VAddr AllocateBlock(s32 index, bool random) { 118 VAddr AllocateBlock(s32 index, bool random) {
115 return heap.AllocateBlock(index, random); 119 return heap.AllocateBlock(index, random);
116 } 120 }
117 121
118 void Free(VAddr addr, std::size_t num_pages) { 122 void Free(VAddr addr, size_t num_pages) {
119 heap.Free(addr, num_pages); 123 heap.Free(addr, num_pages);
120 } 124 }
121 125
122 constexpr std::size_t GetSize() const { 126 void SetInitialUsedHeapSize(size_t reserved_size) {
127 heap.SetInitialUsedSize(reserved_size);
128 }
129
130 constexpr Pool GetPool() const {
131 return pool;
132 }
133
134 constexpr size_t GetSize() const {
123 return heap.GetSize(); 135 return heap.GetSize();
124 } 136 }
125 137
@@ -130,12 +142,137 @@ private:
130 constexpr VAddr GetEndAddress() const { 142 constexpr VAddr GetEndAddress() const {
131 return heap.GetEndAddress(); 143 return heap.GetEndAddress();
132 } 144 }
145
146 constexpr size_t GetPageOffset(PAddr address) const {
147 return heap.GetPageOffset(address);
148 }
149
150 constexpr size_t GetPageOffsetToEnd(PAddr address) const {
151 return heap.GetPageOffsetToEnd(address);
152 }
153
154 constexpr void SetNext(Impl* n) {
155 next = n;
156 }
157
158 constexpr void SetPrev(Impl* n) {
159 prev = n;
160 }
161
162 constexpr Impl* GetNext() const {
163 return next;
164 }
165
166 constexpr Impl* GetPrev() const {
167 return prev;
168 }
169
170 void OpenFirst(PAddr address, size_t num_pages) {
171 size_t index = this->GetPageOffset(address);
172 const size_t end = index + num_pages;
173 while (index < end) {
174 const RefCount ref_count = (++page_reference_counts[index]);
175 ASSERT(ref_count == 1);
176
177 index++;
178 }
179 }
180
181 void Open(PAddr address, size_t num_pages) {
182 size_t index = this->GetPageOffset(address);
183 const size_t end = index + num_pages;
184 while (index < end) {
185 const RefCount ref_count = (++page_reference_counts[index]);
186 ASSERT(ref_count > 1);
187
188 index++;
189 }
190 }
191
192 void Close(PAddr address, size_t num_pages) {
193 size_t index = this->GetPageOffset(address);
194 const size_t end = index + num_pages;
195
196 size_t free_start = 0;
197 size_t free_count = 0;
198 while (index < end) {
199 ASSERT(page_reference_counts[index] > 0);
200 const RefCount ref_count = (--page_reference_counts[index]);
201
202 // Keep track of how many zero refcounts we see in a row, to minimize calls to free.
203 if (ref_count == 0) {
204 if (free_count > 0) {
205 free_count++;
206 } else {
207 free_start = index;
208 free_count = 1;
209 }
210 } else {
211 if (free_count > 0) {
212 this->Free(heap.GetAddress() + free_start * PageSize, free_count);
213 free_count = 0;
214 }
215 }
216
217 index++;
218 }
219
220 if (free_count > 0) {
221 this->Free(heap.GetAddress() + free_start * PageSize, free_count);
222 }
223 }
224
225 static size_t CalculateManagementOverheadSize(size_t region_size);
226
227 static constexpr size_t CalculateOptimizedProcessOverheadSize(size_t region_size) {
228 return (Common::AlignUp((region_size / PageSize), Common::BitSize<u64>()) /
229 Common::BitSize<u64>()) *
230 sizeof(u64);
231 }
232
233 private:
234 using RefCount = u16;
235
236 KPageHeap heap;
237 std::vector<RefCount> page_reference_counts;
238 VAddr management_region{};
239 Pool pool{};
240 Impl* next{};
241 Impl* prev{};
133 }; 242 };
134 243
135private: 244private:
245 Impl& GetManager(const KMemoryLayout& memory_layout, PAddr address) {
246 return managers[memory_layout.GetPhysicalLinearRegion(address).GetAttributes()];
247 }
248
249 const Impl& GetManager(const KMemoryLayout& memory_layout, PAddr address) const {
250 return managers[memory_layout.GetPhysicalLinearRegion(address).GetAttributes()];
251 }
252
253 constexpr Impl* GetFirstManager(Pool pool, Direction dir) const {
254 return dir == Direction::FromBack ? pool_managers_tail[static_cast<size_t>(pool)]
255 : pool_managers_head[static_cast<size_t>(pool)];
256 }
257
258 constexpr Impl* GetNextManager(Impl* cur, Direction dir) const {
259 if (dir == Direction::FromBack) {
260 return cur->GetPrev();
261 } else {
262 return cur->GetNext();
263 }
264 }
265
266 ResultCode AllocatePageGroupImpl(KPageLinkedList* out, size_t num_pages, Pool pool,
267 Direction dir, bool random);
268
269private:
136 Core::System& system; 270 Core::System& system;
137 std::array<std::mutex, static_cast<std::size_t>(Pool::Count)> pool_locks; 271 std::array<KLightLock, static_cast<size_t>(Pool::Count)> pool_locks;
272 std::array<Impl*, MaxManagerCount> pool_managers_head{};
273 std::array<Impl*, MaxManagerCount> pool_managers_tail{};
138 std::array<Impl, MaxManagerCount> managers; 274 std::array<Impl, MaxManagerCount> managers;
275 size_t num_managers{};
139}; 276};
140 277
141} // namespace Kernel 278} // namespace Kernel
diff --git a/src/core/hle/kernel/k_memory_region.h b/src/core/hle/kernel/k_memory_region.h
index 90ab8fd62..e9bdf4e59 100644
--- a/src/core/hle/kernel/k_memory_region.h
+++ b/src/core/hle/kernel/k_memory_region.h
@@ -5,6 +5,7 @@
5#pragma once 5#pragma once
6 6
7#include "common/assert.h" 7#include "common/assert.h"
8#include "common/common_funcs.h"
8#include "common/common_types.h" 9#include "common/common_types.h"
9#include "common/intrusive_red_black_tree.h" 10#include "common/intrusive_red_black_tree.h"
10#include "core/hle/kernel/k_memory_region_type.h" 11#include "core/hle/kernel/k_memory_region_type.h"
@@ -13,11 +14,13 @@ namespace Kernel {
13 14
14class KMemoryRegionAllocator; 15class KMemoryRegionAllocator;
15 16
16class KMemoryRegion final : public Common::IntrusiveRedBlackTreeBaseNode<KMemoryRegion>, 17class KMemoryRegion final : public Common::IntrusiveRedBlackTreeBaseNode<KMemoryRegion> {
17 NonCopyable {
18 friend class KMemoryRegionTree; 18 friend class KMemoryRegionTree;
19 19
20public: 20public:
21 YUZU_NON_COPYABLE(KMemoryRegion);
22 YUZU_NON_MOVEABLE(KMemoryRegion);
23
21 constexpr KMemoryRegion() = default; 24 constexpr KMemoryRegion() = default;
22 constexpr KMemoryRegion(u64 address_, u64 last_address_) 25 constexpr KMemoryRegion(u64 address_, u64 last_address_)
23 : address{address_}, last_address{last_address_} {} 26 : address{address_}, last_address{last_address_} {}
@@ -29,6 +32,8 @@ public:
29 : KMemoryRegion(address_, last_address_, std::numeric_limits<u64>::max(), attributes_, 32 : KMemoryRegion(address_, last_address_, std::numeric_limits<u64>::max(), attributes_,
30 type_id_) {} 33 type_id_) {}
31 34
35 ~KMemoryRegion() = default;
36
32 static constexpr int Compare(const KMemoryRegion& lhs, const KMemoryRegion& rhs) { 37 static constexpr int Compare(const KMemoryRegion& lhs, const KMemoryRegion& rhs) {
33 if (lhs.GetAddress() < rhs.GetAddress()) { 38 if (lhs.GetAddress() < rhs.GetAddress()) {
34 return -1; 39 return -1;
@@ -39,16 +44,6 @@ public:
39 } 44 }
40 } 45 }
41 46
42private:
43 constexpr void Reset(u64 a, u64 la, u64 p, u32 r, u32 t) {
44 address = a;
45 pair_address = p;
46 last_address = la;
47 attributes = r;
48 type_id = t;
49 }
50
51public:
52 constexpr u64 GetAddress() const { 47 constexpr u64 GetAddress() const {
53 return address; 48 return address;
54 } 49 }
@@ -108,6 +103,14 @@ public:
108 } 103 }
109 104
110private: 105private:
106 constexpr void Reset(u64 a, u64 la, u64 p, u32 r, u32 t) {
107 address = a;
108 pair_address = p;
109 last_address = la;
110 attributes = r;
111 type_id = t;
112 }
113
111 u64 address{}; 114 u64 address{};
112 u64 last_address{}; 115 u64 last_address{};
113 u64 pair_address{}; 116 u64 pair_address{};
@@ -115,8 +118,25 @@ private:
115 u32 type_id{}; 118 u32 type_id{};
116}; 119};
117 120
118class KMemoryRegionTree final : NonCopyable { 121class KMemoryRegionTree final {
122private:
123 using TreeType =
124 Common::IntrusiveRedBlackTreeBaseTraits<KMemoryRegion>::TreeType<KMemoryRegion>;
125
119public: 126public:
127 YUZU_NON_COPYABLE(KMemoryRegionTree);
128 YUZU_NON_MOVEABLE(KMemoryRegionTree);
129
130 using value_type = TreeType::value_type;
131 using size_type = TreeType::size_type;
132 using difference_type = TreeType::difference_type;
133 using pointer = TreeType::pointer;
134 using const_pointer = TreeType::const_pointer;
135 using reference = TreeType::reference;
136 using const_reference = TreeType::const_reference;
137 using iterator = TreeType::iterator;
138 using const_iterator = TreeType::const_iterator;
139
120 struct DerivedRegionExtents { 140 struct DerivedRegionExtents {
121 const KMemoryRegion* first_region{}; 141 const KMemoryRegion* first_region{};
122 const KMemoryRegion* last_region{}; 142 const KMemoryRegion* last_region{};
@@ -140,29 +160,9 @@ public:
140 } 160 }
141 }; 161 };
142 162
143private:
144 using TreeType =
145 Common::IntrusiveRedBlackTreeBaseTraits<KMemoryRegion>::TreeType<KMemoryRegion>;
146
147public:
148 using value_type = TreeType::value_type;
149 using size_type = TreeType::size_type;
150 using difference_type = TreeType::difference_type;
151 using pointer = TreeType::pointer;
152 using const_pointer = TreeType::const_pointer;
153 using reference = TreeType::reference;
154 using const_reference = TreeType::const_reference;
155 using iterator = TreeType::iterator;
156 using const_iterator = TreeType::const_iterator;
157
158private:
159 TreeType m_tree{};
160 KMemoryRegionAllocator& memory_region_allocator;
161
162public:
163 explicit KMemoryRegionTree(KMemoryRegionAllocator& memory_region_allocator_); 163 explicit KMemoryRegionTree(KMemoryRegionAllocator& memory_region_allocator_);
164 ~KMemoryRegionTree() = default;
164 165
165public:
166 KMemoryRegion* FindModifiable(u64 address) { 166 KMemoryRegion* FindModifiable(u64 address) {
167 if (auto it = this->find(KMemoryRegion(address, address, 0, 0)); it != this->end()) { 167 if (auto it = this->find(KMemoryRegion(address, address, 0, 0)); it != this->end()) {
168 return std::addressof(*it); 168 return std::addressof(*it);
@@ -241,7 +241,6 @@ public:
241 return GetDerivedRegionExtents(static_cast<KMemoryRegionType>(type_id)); 241 return GetDerivedRegionExtents(static_cast<KMemoryRegionType>(type_id));
242 } 242 }
243 243
244public:
245 void InsertDirectly(u64 address, u64 last_address, u32 attr = 0, u32 type_id = 0); 244 void InsertDirectly(u64 address, u64 last_address, u32 attr = 0, u32 type_id = 0);
246 bool Insert(u64 address, size_t size, u32 type_id, u32 new_attr = 0, u32 old_attr = 0); 245 bool Insert(u64 address, size_t size, u32 type_id, u32 new_attr = 0, u32 old_attr = 0);
247 246
@@ -252,7 +251,6 @@ public:
252 return this->GetRandomAlignedRegion(size + 2 * guard_size, alignment, type_id) + guard_size; 251 return this->GetRandomAlignedRegion(size + 2 * guard_size, alignment, type_id) + guard_size;
253 } 252 }
254 253
255public:
256 // Iterator accessors. 254 // Iterator accessors.
257 iterator begin() { 255 iterator begin() {
258 return m_tree.begin(); 256 return m_tree.begin();
@@ -322,13 +320,21 @@ public:
322 iterator nfind(const_reference ref) const { 320 iterator nfind(const_reference ref) const {
323 return m_tree.nfind(ref); 321 return m_tree.nfind(ref);
324 } 322 }
323
324private:
325 TreeType m_tree{};
326 KMemoryRegionAllocator& memory_region_allocator;
325}; 327};
326 328
327class KMemoryRegionAllocator final : NonCopyable { 329class KMemoryRegionAllocator final {
328public: 330public:
331 YUZU_NON_COPYABLE(KMemoryRegionAllocator);
332 YUZU_NON_MOVEABLE(KMemoryRegionAllocator);
333
329 static constexpr size_t MaxMemoryRegions = 200; 334 static constexpr size_t MaxMemoryRegions = 200;
330 335
331 constexpr KMemoryRegionAllocator() = default; 336 constexpr KMemoryRegionAllocator() = default;
337 constexpr ~KMemoryRegionAllocator() = default;
332 338
333 template <typename... Args> 339 template <typename... Args>
334 KMemoryRegion* Allocate(Args&&... args) { 340 KMemoryRegion* Allocate(Args&&... args) {
diff --git a/src/core/hle/kernel/k_memory_region_type.h b/src/core/hle/kernel/k_memory_region_type.h
index a05e66677..0baeddf51 100644
--- a/src/core/hle/kernel/k_memory_region_type.h
+++ b/src/core/hle/kernel/k_memory_region_type.h
@@ -14,7 +14,8 @@
14namespace Kernel { 14namespace Kernel {
15 15
16enum KMemoryRegionType : u32 { 16enum KMemoryRegionType : u32 {
17 KMemoryRegionAttr_CarveoutProtected = 0x04000000, 17 KMemoryRegionAttr_CarveoutProtected = 0x02000000,
18 KMemoryRegionAttr_Uncached = 0x04000000,
18 KMemoryRegionAttr_DidKernelMap = 0x08000000, 19 KMemoryRegionAttr_DidKernelMap = 0x08000000,
19 KMemoryRegionAttr_ShouldKernelMap = 0x10000000, 20 KMemoryRegionAttr_ShouldKernelMap = 0x10000000,
20 KMemoryRegionAttr_UserReadOnly = 0x20000000, 21 KMemoryRegionAttr_UserReadOnly = 0x20000000,
@@ -239,6 +240,11 @@ static_assert(KMemoryRegionType_VirtualDramHeapBase.GetValue() == 0x1A);
239static_assert(KMemoryRegionType_VirtualDramKernelPtHeap.GetValue() == 0x2A); 240static_assert(KMemoryRegionType_VirtualDramKernelPtHeap.GetValue() == 0x2A);
240static_assert(KMemoryRegionType_VirtualDramKernelTraceBuffer.GetValue() == 0x4A); 241static_assert(KMemoryRegionType_VirtualDramKernelTraceBuffer.GetValue() == 0x4A);
241 242
243// UNUSED: .DeriveSparse(2, 2, 0);
244constexpr auto KMemoryRegionType_VirtualDramUnknownDebug =
245 KMemoryRegionType_Dram.DeriveSparse(2, 2, 1);
246static_assert(KMemoryRegionType_VirtualDramUnknownDebug.GetValue() == (0x52));
247
242constexpr auto KMemoryRegionType_VirtualDramKernelInitPt = 248constexpr auto KMemoryRegionType_VirtualDramKernelInitPt =
243 KMemoryRegionType_VirtualDramHeapBase.Derive(3, 0); 249 KMemoryRegionType_VirtualDramHeapBase.Derive(3, 0);
244constexpr auto KMemoryRegionType_VirtualDramPoolManagement = 250constexpr auto KMemoryRegionType_VirtualDramPoolManagement =
@@ -330,6 +336,8 @@ constexpr KMemoryRegionType GetTypeForVirtualLinearMapping(u32 type_id) {
330 return KMemoryRegionType_VirtualDramKernelTraceBuffer; 336 return KMemoryRegionType_VirtualDramKernelTraceBuffer;
331 } else if (KMemoryRegionType_DramKernelPtHeap.IsAncestorOf(type_id)) { 337 } else if (KMemoryRegionType_DramKernelPtHeap.IsAncestorOf(type_id)) {
332 return KMemoryRegionType_VirtualDramKernelPtHeap; 338 return KMemoryRegionType_VirtualDramKernelPtHeap;
339 } else if ((type_id | KMemoryRegionAttr_ShouldKernelMap) == type_id) {
340 return KMemoryRegionType_VirtualDramUnknownDebug;
333 } else { 341 } else {
334 return KMemoryRegionType_Dram; 342 return KMemoryRegionType_Dram;
335 } 343 }
diff --git a/src/core/hle/kernel/k_page_buffer.cpp b/src/core/hle/kernel/k_page_buffer.cpp
new file mode 100644
index 000000000..f7df4a9a8
--- /dev/null
+++ b/src/core/hle/kernel/k_page_buffer.cpp
@@ -0,0 +1,19 @@
1// Copyright 2022 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include "common/alignment.h"
6#include "common/assert.h"
7#include "core/core.h"
8#include "core/device_memory.h"
9#include "core/hle/kernel/k_page_buffer.h"
10#include "core/hle/kernel/memory_types.h"
11
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_heap.cpp b/src/core/hle/kernel/k_page_heap.cpp
index 29d996d62..97a5890a0 100644
--- a/src/core/hle/kernel/k_page_heap.cpp
+++ b/src/core/hle/kernel/k_page_heap.cpp
@@ -7,35 +7,51 @@
7 7
8namespace Kernel { 8namespace Kernel {
9 9
10void KPageHeap::Initialize(VAddr address, std::size_t size, std::size_t metadata_size) { 10void KPageHeap::Initialize(PAddr address, size_t size, VAddr management_address,
11 // Check our assumptions 11 size_t management_size, const size_t* block_shifts,
12 ASSERT(Common::IsAligned((address), PageSize)); 12 size_t num_block_shifts) {
13 // Check our assumptions.
14 ASSERT(Common::IsAligned(address, PageSize));
13 ASSERT(Common::IsAligned(size, PageSize)); 15 ASSERT(Common::IsAligned(size, PageSize));
16 ASSERT(0 < num_block_shifts && num_block_shifts <= NumMemoryBlockPageShifts);
17 const VAddr management_end = management_address + management_size;
14 18
15 // Set our members 19 // Set our members.
16 heap_address = address; 20 m_heap_address = address;
17 heap_size = size; 21 m_heap_size = size;
18 22 m_num_blocks = num_block_shifts;
19 // Setup bitmaps 23
20 metadata.resize(metadata_size / sizeof(u64)); 24 // Setup bitmaps.
21 u64* cur_bitmap_storage{metadata.data()}; 25 m_management_data.resize(management_size / sizeof(u64));
22 for (std::size_t i = 0; i < MemoryBlockPageShifts.size(); i++) { 26 u64* cur_bitmap_storage{m_management_data.data()};
23 const std::size_t cur_block_shift{MemoryBlockPageShifts[i]}; 27 for (size_t i = 0; i < num_block_shifts; i++) {
24 const std::size_t next_block_shift{ 28 const size_t cur_block_shift = block_shifts[i];
25 (i != MemoryBlockPageShifts.size() - 1) ? MemoryBlockPageShifts[i + 1] : 0}; 29 const size_t next_block_shift = (i != num_block_shifts - 1) ? block_shifts[i + 1] : 0;
26 cur_bitmap_storage = blocks[i].Initialize(heap_address, heap_size, cur_block_shift, 30 cur_bitmap_storage = m_blocks[i].Initialize(m_heap_address, m_heap_size, cur_block_shift,
27 next_block_shift, cur_bitmap_storage); 31 next_block_shift, cur_bitmap_storage);
28 } 32 }
33
34 // Ensure we didn't overextend our bounds.
35 ASSERT(VAddr(cur_bitmap_storage) <= management_end);
36}
37
38size_t KPageHeap::GetNumFreePages() const {
39 size_t num_free = 0;
40
41 for (size_t i = 0; i < m_num_blocks; i++) {
42 num_free += m_blocks[i].GetNumFreePages();
43 }
44
45 return num_free;
29} 46}
30 47
31VAddr KPageHeap::AllocateBlock(s32 index, bool random) { 48PAddr KPageHeap::AllocateBlock(s32 index, bool random) {
32 const std::size_t needed_size{blocks[index].GetSize()}; 49 const size_t needed_size = m_blocks[index].GetSize();
33 50
34 for (s32 i{index}; i < static_cast<s32>(MemoryBlockPageShifts.size()); i++) { 51 for (s32 i = index; i < static_cast<s32>(m_num_blocks); i++) {
35 if (const VAddr addr{blocks[i].PopBlock(random)}; addr) { 52 if (const PAddr addr = m_blocks[i].PopBlock(random); addr != 0) {
36 if (const std::size_t allocated_size{blocks[i].GetSize()}; 53 if (const size_t allocated_size = m_blocks[i].GetSize(); allocated_size > needed_size) {
37 allocated_size > needed_size) { 54 this->Free(addr + needed_size, (allocated_size - needed_size) / PageSize);
38 Free(addr + needed_size, (allocated_size - needed_size) / PageSize);
39 } 55 }
40 return addr; 56 return addr;
41 } 57 }
@@ -44,34 +60,34 @@ VAddr KPageHeap::AllocateBlock(s32 index, bool random) {
44 return 0; 60 return 0;
45} 61}
46 62
47void KPageHeap::FreeBlock(VAddr block, s32 index) { 63void KPageHeap::FreeBlock(PAddr block, s32 index) {
48 do { 64 do {
49 block = blocks[index++].PushBlock(block); 65 block = m_blocks[index++].PushBlock(block);
50 } while (block != 0); 66 } while (block != 0);
51} 67}
52 68
53void KPageHeap::Free(VAddr addr, std::size_t num_pages) { 69void KPageHeap::Free(PAddr addr, size_t num_pages) {
54 // Freeing no pages is a no-op 70 // Freeing no pages is a no-op.
55 if (num_pages == 0) { 71 if (num_pages == 0) {
56 return; 72 return;
57 } 73 }
58 74
59 // Find the largest block size that we can free, and free as many as possible 75 // Find the largest block size that we can free, and free as many as possible.
60 s32 big_index{static_cast<s32>(MemoryBlockPageShifts.size()) - 1}; 76 s32 big_index = static_cast<s32>(m_num_blocks) - 1;
61 const VAddr start{addr}; 77 const PAddr start = addr;
62 const VAddr end{(num_pages * PageSize) + addr}; 78 const PAddr end = addr + num_pages * PageSize;
63 VAddr before_start{start}; 79 PAddr before_start = start;
64 VAddr before_end{start}; 80 PAddr before_end = start;
65 VAddr after_start{end}; 81 PAddr after_start = end;
66 VAddr after_end{end}; 82 PAddr after_end = end;
67 while (big_index >= 0) { 83 while (big_index >= 0) {
68 const std::size_t block_size{blocks[big_index].GetSize()}; 84 const size_t block_size = m_blocks[big_index].GetSize();
69 const VAddr big_start{Common::AlignUp((start), block_size)}; 85 const PAddr big_start = Common::AlignUp(start, block_size);
70 const VAddr big_end{Common::AlignDown((end), block_size)}; 86 const PAddr big_end = Common::AlignDown(end, block_size);
71 if (big_start < big_end) { 87 if (big_start < big_end) {
72 // Free as many big blocks as we can 88 // Free as many big blocks as we can.
73 for (auto block{big_start}; block < big_end; block += block_size) { 89 for (auto block = big_start; block < big_end; block += block_size) {
74 FreeBlock(block, big_index); 90 this->FreeBlock(block, big_index);
75 } 91 }
76 before_end = big_start; 92 before_end = big_start;
77 after_start = big_end; 93 after_start = big_end;
@@ -81,31 +97,31 @@ void KPageHeap::Free(VAddr addr, std::size_t num_pages) {
81 } 97 }
82 ASSERT(big_index >= 0); 98 ASSERT(big_index >= 0);
83 99
84 // Free space before the big blocks 100 // Free space before the big blocks.
85 for (s32 i{big_index - 1}; i >= 0; i--) { 101 for (s32 i = big_index - 1; i >= 0; i--) {
86 const std::size_t block_size{blocks[i].GetSize()}; 102 const size_t block_size = m_blocks[i].GetSize();
87 while (before_start + block_size <= before_end) { 103 while (before_start + block_size <= before_end) {
88 before_end -= block_size; 104 before_end -= block_size;
89 FreeBlock(before_end, i); 105 this->FreeBlock(before_end, i);
90 } 106 }
91 } 107 }
92 108
93 // Free space after the big blocks 109 // Free space after the big blocks.
94 for (s32 i{big_index - 1}; i >= 0; i--) { 110 for (s32 i = big_index - 1; i >= 0; i--) {
95 const std::size_t block_size{blocks[i].GetSize()}; 111 const size_t block_size = m_blocks[i].GetSize();
96 while (after_start + block_size <= after_end) { 112 while (after_start + block_size <= after_end) {
97 FreeBlock(after_start, i); 113 this->FreeBlock(after_start, i);
98 after_start += block_size; 114 after_start += block_size;
99 } 115 }
100 } 116 }
101} 117}
102 118
103std::size_t KPageHeap::CalculateManagementOverheadSize(std::size_t region_size) { 119size_t KPageHeap::CalculateManagementOverheadSize(size_t region_size, const size_t* block_shifts,
104 std::size_t overhead_size = 0; 120 size_t num_block_shifts) {
105 for (std::size_t i = 0; i < MemoryBlockPageShifts.size(); i++) { 121 size_t overhead_size = 0;
106 const std::size_t cur_block_shift{MemoryBlockPageShifts[i]}; 122 for (size_t i = 0; i < num_block_shifts; i++) {
107 const std::size_t next_block_shift{ 123 const size_t cur_block_shift = block_shifts[i];
108 (i != MemoryBlockPageShifts.size() - 1) ? MemoryBlockPageShifts[i + 1] : 0}; 124 const size_t next_block_shift = (i != num_block_shifts - 1) ? block_shifts[i + 1] : 0;
109 overhead_size += KPageHeap::Block::CalculateManagementOverheadSize( 125 overhead_size += KPageHeap::Block::CalculateManagementOverheadSize(
110 region_size, cur_block_shift, next_block_shift); 126 region_size, cur_block_shift, next_block_shift);
111 } 127 }
diff --git a/src/core/hle/kernel/k_page_heap.h b/src/core/hle/kernel/k_page_heap.h
index 8d9f30523..60fff766b 100644
--- a/src/core/hle/kernel/k_page_heap.h
+++ b/src/core/hle/kernel/k_page_heap.h
@@ -8,183 +8,210 @@
8#include <vector> 8#include <vector>
9 9
10#include "common/alignment.h" 10#include "common/alignment.h"
11#include "common/common_funcs.h"
11#include "common/common_types.h" 12#include "common/common_types.h"
12#include "core/hle/kernel/k_page_bitmap.h" 13#include "core/hle/kernel/k_page_bitmap.h"
13#include "core/hle/kernel/memory_types.h" 14#include "core/hle/kernel/memory_types.h"
14 15
15namespace Kernel { 16namespace Kernel {
16 17
17class KPageHeap final : NonCopyable { 18class KPageHeap final {
18public: 19public:
19 static constexpr s32 GetAlignedBlockIndex(std::size_t num_pages, std::size_t align_pages) { 20 YUZU_NON_COPYABLE(KPageHeap);
20 const auto target_pages{std::max(num_pages, align_pages)}; 21 YUZU_NON_MOVEABLE(KPageHeap);
21 for (std::size_t i = 0; i < NumMemoryBlockPageShifts; i++) { 22
22 if (target_pages <= 23 KPageHeap() = default;
23 (static_cast<std::size_t>(1) << MemoryBlockPageShifts[i]) / PageSize) { 24 ~KPageHeap() = default;
25
26 constexpr PAddr GetAddress() const {
27 return m_heap_address;
28 }
29 constexpr size_t GetSize() const {
30 return m_heap_size;
31 }
32 constexpr PAddr GetEndAddress() const {
33 return this->GetAddress() + this->GetSize();
34 }
35 constexpr size_t GetPageOffset(PAddr block) const {
36 return (block - this->GetAddress()) / PageSize;
37 }
38 constexpr size_t GetPageOffsetToEnd(PAddr block) const {
39 return (this->GetEndAddress() - block) / PageSize;
40 }
41
42 void Initialize(PAddr heap_address, size_t heap_size, VAddr management_address,
43 size_t management_size) {
44 return this->Initialize(heap_address, heap_size, management_address, management_size,
45 MemoryBlockPageShifts.data(), NumMemoryBlockPageShifts);
46 }
47
48 size_t GetFreeSize() const {
49 return this->GetNumFreePages() * PageSize;
50 }
51
52 void SetInitialUsedSize(size_t reserved_size) {
53 // Check that the reserved size is valid.
54 const size_t free_size = this->GetNumFreePages() * PageSize;
55 ASSERT(m_heap_size >= free_size + reserved_size);
56
57 // Set the initial used size.
58 m_initial_used_size = m_heap_size - free_size - reserved_size;
59 }
60
61 PAddr AllocateBlock(s32 index, bool random);
62 void Free(PAddr addr, size_t num_pages);
63
64 static size_t CalculateManagementOverheadSize(size_t region_size) {
65 return CalculateManagementOverheadSize(region_size, MemoryBlockPageShifts.data(),
66 NumMemoryBlockPageShifts);
67 }
68
69 static constexpr s32 GetAlignedBlockIndex(size_t num_pages, size_t align_pages) {
70 const size_t target_pages = std::max(num_pages, align_pages);
71 for (size_t i = 0; i < NumMemoryBlockPageShifts; i++) {
72 if (target_pages <= (size_t(1) << MemoryBlockPageShifts[i]) / PageSize) {
24 return static_cast<s32>(i); 73 return static_cast<s32>(i);
25 } 74 }
26 } 75 }
27 return -1; 76 return -1;
28 } 77 }
29 78
30 static constexpr s32 GetBlockIndex(std::size_t num_pages) { 79 static constexpr s32 GetBlockIndex(size_t num_pages) {
31 for (s32 i{static_cast<s32>(NumMemoryBlockPageShifts) - 1}; i >= 0; i--) { 80 for (s32 i = static_cast<s32>(NumMemoryBlockPageShifts) - 1; i >= 0; i--) {
32 if (num_pages >= (static_cast<std::size_t>(1) << MemoryBlockPageShifts[i]) / PageSize) { 81 if (num_pages >= (size_t(1) << MemoryBlockPageShifts[i]) / PageSize) {
33 return i; 82 return i;
34 } 83 }
35 } 84 }
36 return -1; 85 return -1;
37 } 86 }
38 87
39 static constexpr std::size_t GetBlockSize(std::size_t index) { 88 static constexpr size_t GetBlockSize(size_t index) {
40 return static_cast<std::size_t>(1) << MemoryBlockPageShifts[index]; 89 return size_t(1) << MemoryBlockPageShifts[index];
41 } 90 }
42 91
43 static constexpr std::size_t GetBlockNumPages(std::size_t index) { 92 static constexpr size_t GetBlockNumPages(size_t index) {
44 return GetBlockSize(index) / PageSize; 93 return GetBlockSize(index) / PageSize;
45 } 94 }
46 95
47private: 96private:
48 static constexpr std::size_t NumMemoryBlockPageShifts{7}; 97 class Block final {
49 static constexpr std::array<std::size_t, NumMemoryBlockPageShifts> MemoryBlockPageShifts{
50 0xC, 0x10, 0x15, 0x16, 0x19, 0x1D, 0x1E,
51 };
52
53 class Block final : NonCopyable {
54 private:
55 KPageBitmap bitmap;
56 VAddr heap_address{};
57 uintptr_t end_offset{};
58 std::size_t block_shift{};
59 std::size_t next_block_shift{};
60
61 public: 98 public:
99 YUZU_NON_COPYABLE(Block);
100 YUZU_NON_MOVEABLE(Block);
101
62 Block() = default; 102 Block() = default;
103 ~Block() = default;
63 104
64 constexpr std::size_t GetShift() const { 105 constexpr size_t GetShift() const {
65 return block_shift; 106 return m_block_shift;
66 } 107 }
67 constexpr std::size_t GetNextShift() const { 108 constexpr size_t GetNextShift() const {
68 return next_block_shift; 109 return m_next_block_shift;
69 } 110 }
70 constexpr std::size_t GetSize() const { 111 constexpr size_t GetSize() const {
71 return static_cast<std::size_t>(1) << GetShift(); 112 return u64(1) << this->GetShift();
72 } 113 }
73 constexpr std::size_t GetNumPages() const { 114 constexpr size_t GetNumPages() const {
74 return GetSize() / PageSize; 115 return this->GetSize() / PageSize;
75 } 116 }
76 constexpr std::size_t GetNumFreeBlocks() const { 117 constexpr size_t GetNumFreeBlocks() const {
77 return bitmap.GetNumBits(); 118 return m_bitmap.GetNumBits();
78 } 119 }
79 constexpr std::size_t GetNumFreePages() const { 120 constexpr size_t GetNumFreePages() const {
80 return GetNumFreeBlocks() * GetNumPages(); 121 return this->GetNumFreeBlocks() * this->GetNumPages();
81 } 122 }
82 123
83 u64* Initialize(VAddr addr, std::size_t size, std::size_t bs, std::size_t nbs, 124 u64* Initialize(PAddr addr, size_t size, size_t bs, size_t nbs, u64* bit_storage) {
84 u64* bit_storage) { 125 // Set shifts.
85 // Set shifts 126 m_block_shift = bs;
86 block_shift = bs; 127 m_next_block_shift = nbs;
87 next_block_shift = nbs; 128
88 129 // Align up the address.
89 // Align up the address 130 PAddr end = addr + size;
90 VAddr end{addr + size}; 131 const size_t align = (m_next_block_shift != 0) ? (u64(1) << m_next_block_shift)
91 const auto align{(next_block_shift != 0) ? (1ULL << next_block_shift) 132 : (u64(1) << m_block_shift);
92 : (1ULL << block_shift)}; 133 addr = Common::AlignDown(addr, align);
93 addr = Common::AlignDown((addr), align); 134 end = Common::AlignUp(end, align);
94 end = Common::AlignUp((end), align); 135
95 136 m_heap_address = addr;
96 heap_address = addr; 137 m_end_offset = (end - addr) / (u64(1) << m_block_shift);
97 end_offset = (end - addr) / (1ULL << block_shift); 138 return m_bitmap.Initialize(bit_storage, m_end_offset);
98 return bitmap.Initialize(bit_storage, end_offset);
99 } 139 }
100 140
101 VAddr PushBlock(VAddr address) { 141 PAddr PushBlock(PAddr address) {
102 // Set the bit for the free block 142 // Set the bit for the free block.
103 std::size_t offset{(address - heap_address) >> GetShift()}; 143 size_t offset = (address - m_heap_address) >> this->GetShift();
104 bitmap.SetBit(offset); 144 m_bitmap.SetBit(offset);
105 145
106 // If we have a next shift, try to clear the blocks below and return the address 146 // If we have a next shift, try to clear the blocks below this one and return the new
107 if (GetNextShift()) { 147 // address.
108 const auto diff{1ULL << (GetNextShift() - GetShift())}; 148 if (this->GetNextShift()) {
149 const size_t diff = u64(1) << (this->GetNextShift() - this->GetShift());
109 offset = Common::AlignDown(offset, diff); 150 offset = Common::AlignDown(offset, diff);
110 if (bitmap.ClearRange(offset, diff)) { 151 if (m_bitmap.ClearRange(offset, diff)) {
111 return heap_address + (offset << GetShift()); 152 return m_heap_address + (offset << this->GetShift());
112 } 153 }
113 } 154 }
114 155
115 // We couldn't coalesce, or we're already as big as possible 156 // We couldn't coalesce, or we're already as big as possible.
116 return 0; 157 return {};
117 } 158 }
118 159
119 VAddr PopBlock(bool random) { 160 PAddr PopBlock(bool random) {
120 // Find a free block 161 // Find a free block.
121 const s64 soffset{bitmap.FindFreeBlock(random)}; 162 s64 soffset = m_bitmap.FindFreeBlock(random);
122 if (soffset < 0) { 163 if (soffset < 0) {
123 return 0; 164 return {};
124 } 165 }
125 const auto offset{static_cast<std::size_t>(soffset)}; 166 const size_t offset = static_cast<size_t>(soffset);
126 167
127 // Update our tracking and return it 168 // Update our tracking and return it.
128 bitmap.ClearBit(offset); 169 m_bitmap.ClearBit(offset);
129 return heap_address + (offset << GetShift()); 170 return m_heap_address + (offset << this->GetShift());
130 } 171 }
131 172
132 public: 173 public:
133 static constexpr std::size_t CalculateManagementOverheadSize(std::size_t region_size, 174 static constexpr size_t CalculateManagementOverheadSize(size_t region_size,
134 std::size_t cur_block_shift, 175 size_t cur_block_shift,
135 std::size_t next_block_shift) { 176 size_t next_block_shift) {
136 const auto cur_block_size{(1ULL << cur_block_shift)}; 177 const size_t cur_block_size = (u64(1) << cur_block_shift);
137 const auto next_block_size{(1ULL << next_block_shift)}; 178 const size_t next_block_size = (u64(1) << next_block_shift);
138 const auto align{(next_block_shift != 0) ? next_block_size : cur_block_size}; 179 const size_t align = (next_block_shift != 0) ? next_block_size : cur_block_size;
139 return KPageBitmap::CalculateManagementOverheadSize( 180 return KPageBitmap::CalculateManagementOverheadSize(
140 (align * 2 + Common::AlignUp(region_size, align)) / cur_block_size); 181 (align * 2 + Common::AlignUp(region_size, align)) / cur_block_size);
141 } 182 }
142 };
143
144public:
145 KPageHeap() = default;
146
147 constexpr VAddr GetAddress() const {
148 return heap_address;
149 }
150 constexpr std::size_t GetSize() const {
151 return heap_size;
152 }
153 constexpr VAddr GetEndAddress() const {
154 return GetAddress() + GetSize();
155 }
156 constexpr std::size_t GetPageOffset(VAddr block) const {
157 return (block - GetAddress()) / PageSize;
158 }
159 183
160 void Initialize(VAddr heap_address, std::size_t heap_size, std::size_t metadata_size); 184 private:
161 VAddr AllocateBlock(s32 index, bool random); 185 KPageBitmap m_bitmap;
162 void Free(VAddr addr, std::size_t num_pages); 186 PAddr m_heap_address{};
163 187 uintptr_t m_end_offset{};
164 void UpdateUsedSize() { 188 size_t m_block_shift{};
165 used_size = heap_size - (GetNumFreePages() * PageSize); 189 size_t m_next_block_shift{};
166 } 190 };
167
168 static std::size_t CalculateManagementOverheadSize(std::size_t region_size);
169 191
170private: 192private:
171 constexpr std::size_t GetNumFreePages() const { 193 void Initialize(PAddr heap_address, size_t heap_size, VAddr management_address,
172 std::size_t num_free{}; 194 size_t management_size, const size_t* block_shifts, size_t num_block_shifts);
195 size_t GetNumFreePages() const;
173 196
174 for (const auto& block : blocks) { 197 void FreeBlock(PAddr block, s32 index);
175 num_free += block.GetNumFreePages();
176 }
177 198
178 return num_free; 199 static constexpr size_t NumMemoryBlockPageShifts{7};
179 } 200 static constexpr std::array<size_t, NumMemoryBlockPageShifts> MemoryBlockPageShifts{
201 0xC, 0x10, 0x15, 0x16, 0x19, 0x1D, 0x1E,
202 };
180 203
181 void FreeBlock(VAddr block, s32 index); 204private:
205 static size_t CalculateManagementOverheadSize(size_t region_size, const size_t* block_shifts,
206 size_t num_block_shifts);
182 207
183 VAddr heap_address{}; 208private:
184 std::size_t heap_size{}; 209 PAddr m_heap_address{};
185 std::size_t used_size{}; 210 size_t m_heap_size{};
186 std::array<Block, NumMemoryBlockPageShifts> blocks{}; 211 size_t m_initial_used_size{};
187 std::vector<u64> metadata; 212 size_t m_num_blocks{};
213 std::array<Block, NumMemoryBlockPageShifts> m_blocks{};
214 std::vector<u64> m_management_data;
188}; 215};
189 216
190} // namespace Kernel 217} // namespace Kernel
diff --git a/src/core/hle/kernel/k_page_table.cpp b/src/core/hle/kernel/k_page_table.cpp
index 2ebbc0819..02d93b12e 100644
--- a/src/core/hle/kernel/k_page_table.cpp
+++ b/src/core/hle/kernel/k_page_table.cpp
@@ -41,27 +41,12 @@ constexpr std::size_t GetAddressSpaceWidthFromType(FileSys::ProgramAddressSpaceT
41 } 41 }
42} 42}
43 43
44constexpr u64 GetAddressInRange(const KMemoryInfo& info, VAddr addr) {
45 if (info.GetAddress() < addr) {
46 return addr;
47 }
48 return info.GetAddress();
49}
50
51constexpr std::size_t GetSizeInRange(const KMemoryInfo& info, VAddr start, VAddr end) {
52 std::size_t size{info.GetSize()};
53 if (info.GetAddress() < start) {
54 size -= start - info.GetAddress();
55 }
56 if (info.GetEndAddress() > end) {
57 size -= info.GetEndAddress() - end;
58 }
59 return size;
60}
61
62} // namespace 44} // namespace
63 45
64KPageTable::KPageTable(Core::System& system_) : system{system_} {} 46KPageTable::KPageTable(Core::System& system_)
47 : general_lock{system_.Kernel()}, map_physical_memory_lock{system_.Kernel()}, system{system_} {}
48
49KPageTable::~KPageTable() = default;
65 50
66ResultCode KPageTable::InitializeForProcess(FileSys::ProgramAddressSpaceType as_type, 51ResultCode KPageTable::InitializeForProcess(FileSys::ProgramAddressSpaceType as_type,
67 bool enable_aslr, VAddr code_addr, 52 bool enable_aslr, VAddr code_addr,
@@ -282,96 +267,228 @@ ResultCode KPageTable::MapProcessCode(VAddr addr, std::size_t num_pages, KMemory
282 R_UNLESS(this->CanContain(addr, size, state), ResultInvalidCurrentMemory); 267 R_UNLESS(this->CanContain(addr, size, state), ResultInvalidCurrentMemory);
283 268
284 // Lock the table. 269 // Lock the table.
285 std::lock_guard lock{page_table_lock}; 270 KScopedLightLock lk(general_lock);
286 271
287 // Verify that the destination memory is unmapped. 272 // Verify that the destination memory is unmapped.
288 R_TRY(this->CheckMemoryState(addr, size, KMemoryState::All, KMemoryState::Free, 273 R_TRY(this->CheckMemoryState(addr, size, KMemoryState::All, KMemoryState::Free,
289 KMemoryPermission::None, KMemoryPermission::None, 274 KMemoryPermission::None, KMemoryPermission::None,
290 KMemoryAttribute::None, KMemoryAttribute::None)); 275 KMemoryAttribute::None, KMemoryAttribute::None));
276 KPageLinkedList pg;
277 R_TRY(system.Kernel().MemoryManager().AllocateAndOpen(
278 &pg, num_pages,
279 KMemoryManager::EncodeOption(KMemoryManager::Pool::Application, allocation_option)));
291 280
292 KPageLinkedList page_linked_list; 281 R_TRY(Operate(addr, num_pages, pg, OperationType::MapGroup));
293 R_TRY(system.Kernel().MemoryManager().Allocate(page_linked_list, num_pages, memory_pool,
294 allocation_option));
295 R_TRY(Operate(addr, num_pages, page_linked_list, OperationType::MapGroup));
296 282
297 block_manager->Update(addr, num_pages, state, perm); 283 block_manager->Update(addr, num_pages, state, perm);
298 284
299 return ResultSuccess; 285 return ResultSuccess;
300} 286}
301 287
302ResultCode KPageTable::MapCodeMemory(VAddr dst_addr, VAddr src_addr, std::size_t size) { 288ResultCode KPageTable::MapCodeMemory(VAddr dst_address, VAddr src_address, std::size_t size) {
303 std::lock_guard lock{page_table_lock}; 289 // Validate the mapping request.
304 290 R_UNLESS(this->CanContain(dst_address, size, KMemoryState::AliasCode),
305 const std::size_t num_pages{size / PageSize}; 291 ResultInvalidMemoryRegion);
306 292
307 KMemoryState state{}; 293 // Lock the table.
308 KMemoryPermission perm{}; 294 KScopedLightLock lk(general_lock);
309 CASCADE_CODE(CheckMemoryState(&state, &perm, nullptr, nullptr, src_addr, size,
310 KMemoryState::All, KMemoryState::Normal, KMemoryPermission::All,
311 KMemoryPermission::UserReadWrite, KMemoryAttribute::Mask,
312 KMemoryAttribute::None, KMemoryAttribute::IpcAndDeviceMapped));
313 295
314 if (IsRegionMapped(dst_addr, size)) { 296 // Verify that the source memory is normal heap.
315 return ResultInvalidCurrentMemory; 297 KMemoryState src_state{};
316 } 298 KMemoryPermission src_perm{};
299 std::size_t num_src_allocator_blocks{};
300 R_TRY(this->CheckMemoryState(&src_state, &src_perm, nullptr, &num_src_allocator_blocks,
301 src_address, size, KMemoryState::All, KMemoryState::Normal,
302 KMemoryPermission::All, KMemoryPermission::UserReadWrite,
303 KMemoryAttribute::All, KMemoryAttribute::None));
317 304
318 KPageLinkedList page_linked_list; 305 // Verify that the destination memory is unmapped.
319 AddRegionToPages(src_addr, num_pages, page_linked_list); 306 std::size_t num_dst_allocator_blocks{};
307 R_TRY(this->CheckMemoryState(&num_dst_allocator_blocks, dst_address, size, KMemoryState::All,
308 KMemoryState::Free, KMemoryPermission::None,
309 KMemoryPermission::None, KMemoryAttribute::None,
310 KMemoryAttribute::None));
320 311
312 // Map the code memory.
321 { 313 {
322 auto block_guard = detail::ScopeExit( 314 // Determine the number of pages being operated on.
323 [&] { Operate(src_addr, num_pages, perm, OperationType::ChangePermissions); }); 315 const std::size_t num_pages = size / PageSize;
324 316
325 CASCADE_CODE(Operate(src_addr, num_pages, KMemoryPermission::None, 317 // Create page groups for the memory being mapped.
326 OperationType::ChangePermissions)); 318 KPageLinkedList pg;
327 CASCADE_CODE(MapPages(dst_addr, page_linked_list, KMemoryPermission::None)); 319 AddRegionToPages(src_address, num_pages, pg);
328 320
329 block_guard.Cancel(); 321 // Reprotect the source as kernel-read/not mapped.
330 } 322 const auto new_perm = static_cast<KMemoryPermission>(KMemoryPermission::KernelRead |
323 KMemoryPermission::NotMapped);
324 R_TRY(Operate(src_address, num_pages, new_perm, OperationType::ChangePermissions));
331 325
332 block_manager->Update(src_addr, num_pages, state, KMemoryPermission::None, 326 // Ensure that we unprotect the source pages on failure.
333 KMemoryAttribute::Locked); 327 auto unprot_guard = SCOPE_GUARD({
334 block_manager->Update(dst_addr, num_pages, KMemoryState::AliasCode); 328 ASSERT(this->Operate(src_address, num_pages, src_perm, OperationType::ChangePermissions)
329 .IsSuccess());
330 });
331
332 // Map the alias pages.
333 R_TRY(MapPages(dst_address, pg, new_perm));
334
335 // We successfully mapped the alias pages, so we don't need to unprotect the src pages on
336 // failure.
337 unprot_guard.Cancel();
338
339 // Apply the memory block updates.
340 block_manager->Update(src_address, num_pages, src_state, new_perm,
341 KMemoryAttribute::Locked);
342 block_manager->Update(dst_address, num_pages, KMemoryState::AliasCode, new_perm,
343 KMemoryAttribute::None);
344 }
335 345
336 return ResultSuccess; 346 return ResultSuccess;
337} 347}
338 348
339ResultCode KPageTable::UnmapCodeMemory(VAddr dst_addr, VAddr src_addr, std::size_t size) { 349ResultCode KPageTable::UnmapCodeMemory(VAddr dst_address, VAddr src_address, std::size_t size) {
340 std::lock_guard lock{page_table_lock}; 350 // Validate the mapping request.
351 R_UNLESS(this->CanContain(dst_address, size, KMemoryState::AliasCode),
352 ResultInvalidMemoryRegion);
353
354 // Lock the table.
355 KScopedLightLock lk(general_lock);
341 356
342 if (!size) { 357 // Verify that the source memory is locked normal heap.
343 return ResultSuccess; 358 std::size_t num_src_allocator_blocks{};
359 R_TRY(this->CheckMemoryState(std::addressof(num_src_allocator_blocks), src_address, size,
360 KMemoryState::All, KMemoryState::Normal, KMemoryPermission::None,
361 KMemoryPermission::None, KMemoryAttribute::All,
362 KMemoryAttribute::Locked));
363
364 // Verify that the destination memory is aliasable code.
365 std::size_t num_dst_allocator_blocks{};
366 R_TRY(this->CheckMemoryStateContiguous(
367 std::addressof(num_dst_allocator_blocks), dst_address, size, KMemoryState::FlagCanCodeAlias,
368 KMemoryState::FlagCanCodeAlias, KMemoryPermission::None, KMemoryPermission::None,
369 KMemoryAttribute::All, KMemoryAttribute::None));
370
371 // Determine whether any pages being unmapped are code.
372 bool any_code_pages = false;
373 {
374 KMemoryBlockManager::const_iterator it = block_manager->FindIterator(dst_address);
375 while (true) {
376 // Get the memory info.
377 const KMemoryInfo info = it->GetMemoryInfo();
378
379 // Check if the memory has code flag.
380 if ((info.GetState() & KMemoryState::FlagCode) != KMemoryState::None) {
381 any_code_pages = true;
382 break;
383 }
384
385 // Check if we're done.
386 if (dst_address + size - 1 <= info.GetLastAddress()) {
387 break;
388 }
389
390 // Advance.
391 ++it;
392 }
344 } 393 }
345 394
346 const std::size_t num_pages{size / PageSize}; 395 // Ensure that we maintain the instruction cache.
396 bool reprotected_pages = false;
397 SCOPE_EXIT({
398 if (reprotected_pages && any_code_pages) {
399 system.InvalidateCpuInstructionCacheRange(dst_address, size);
400 }
401 });
347 402
348 CASCADE_CODE(CheckMemoryState(nullptr, nullptr, nullptr, nullptr, src_addr, size, 403 // Unmap.
349 KMemoryState::All, KMemoryState::Normal, KMemoryPermission::None, 404 {
350 KMemoryPermission::None, KMemoryAttribute::Mask, 405 // Determine the number of pages being operated on.
351 KMemoryAttribute::Locked, KMemoryAttribute::IpcAndDeviceMapped)); 406 const std::size_t num_pages = size / PageSize;
352 407
353 KMemoryState state{}; 408 // Unmap the aliased copy of the pages.
354 CASCADE_CODE(CheckMemoryState( 409 R_TRY(Operate(dst_address, num_pages, KMemoryPermission::None, OperationType::Unmap));
355 &state, nullptr, nullptr, nullptr, dst_addr, PageSize, KMemoryState::FlagCanCodeAlias,
356 KMemoryState::FlagCanCodeAlias, KMemoryPermission::None, KMemoryPermission::None,
357 KMemoryAttribute::Mask, KMemoryAttribute::None, KMemoryAttribute::IpcAndDeviceMapped));
358 CASCADE_CODE(CheckMemoryState(dst_addr, size, KMemoryState::All, state, KMemoryPermission::None,
359 KMemoryPermission::None, KMemoryAttribute::Mask,
360 KMemoryAttribute::None));
361 CASCADE_CODE(Operate(dst_addr, num_pages, KMemoryPermission::None, OperationType::Unmap));
362 410
363 block_manager->Update(dst_addr, num_pages, KMemoryState::Free); 411 // Try to set the permissions for the source pages back to what they should be.
364 block_manager->Update(src_addr, num_pages, KMemoryState::Normal, 412 R_TRY(Operate(src_address, num_pages, KMemoryPermission::UserReadWrite,
365 KMemoryPermission::UserReadWrite); 413 OperationType::ChangePermissions));
366 414
367 system.InvalidateCpuInstructionCacheRange(dst_addr, size); 415 // Apply the memory block updates.
416 block_manager->Update(dst_address, num_pages, KMemoryState::None);
417 block_manager->Update(src_address, num_pages, KMemoryState::Normal,
418 KMemoryPermission::UserReadWrite);
419
420 // Note that we reprotected pages.
421 reprotected_pages = true;
422 }
368 423
369 return ResultSuccess; 424 return ResultSuccess;
370} 425}
371 426
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
372ResultCode KPageTable::UnmapProcessMemory(VAddr dst_addr, std::size_t size, 489ResultCode KPageTable::UnmapProcessMemory(VAddr dst_addr, std::size_t size,
373 KPageTable& src_page_table, VAddr src_addr) { 490 KPageTable& src_page_table, VAddr src_addr) {
374 std::lock_guard lock{page_table_lock}; 491 KScopedLightLock lk(general_lock);
375 492
376 const std::size_t num_pages{size / PageSize}; 493 const std::size_t num_pages{size / PageSize};
377 494
@@ -397,150 +514,482 @@ ResultCode KPageTable::UnmapProcessMemory(VAddr dst_addr, std::size_t size,
397 return ResultSuccess; 514 return ResultSuccess;
398} 515}
399 516
400ResultCode KPageTable::MapPhysicalMemory(VAddr addr, std::size_t size) { 517ResultCode KPageTable::MapPhysicalMemory(VAddr address, std::size_t size) {
401 // Lock the physical memory lock. 518 // Lock the physical memory lock.
402 std::lock_guard phys_lk(map_physical_memory_lock); 519 KScopedLightLock map_phys_mem_lk(map_physical_memory_lock);
403 520
404 // Lock the table. 521 // Calculate the last address for convenience.
405 std::lock_guard lock{page_table_lock}; 522 const VAddr last_address = address + size - 1;
406 523
407 std::size_t mapped_size{}; 524 // Define iteration variables.
408 const VAddr end_addr{addr + size}; 525 VAddr cur_address;
526 std::size_t mapped_size;
409 527
410 block_manager->IterateForRange(addr, end_addr, [&](const KMemoryInfo& info) { 528 // The entire mapping process can be retried.
411 if (info.state != KMemoryState::Free) { 529 while (true) {
412 mapped_size += GetSizeInRange(info, addr, end_addr); 530 // Check if the memory is already mapped.
413 } 531 {
414 }); 532 // Lock the table.
415 533 KScopedLightLock lk(general_lock);
416 if (mapped_size == size) { 534
417 return ResultSuccess; 535 // Iterate over the memory.
418 } 536 cur_address = address;
537 mapped_size = 0;
538
539 auto it = block_manager->FindIterator(cur_address);
540 while (true) {
541 // Check that the iterator is valid.
542 ASSERT(it != block_manager->end());
543
544 // Get the memory info.
545 const KMemoryInfo info = it->GetMemoryInfo();
546
547 // Check if we're done.
548 if (last_address <= info.GetLastAddress()) {
549 if (info.GetState() != KMemoryState::Free) {
550 mapped_size += (last_address + 1 - cur_address);
551 }
552 break;
553 }
554
555 // Track the memory if it's mapped.
556 if (info.GetState() != KMemoryState::Free) {
557 mapped_size += VAddr(info.GetEndAddress()) - cur_address;
558 }
559
560 // Advance.
561 cur_address = info.GetEndAddress();
562 ++it;
563 }
419 564
420 const std::size_t remaining_size{size - mapped_size}; 565 // If the size mapped is the size requested, we've nothing to do.
421 const std::size_t remaining_pages{remaining_size / PageSize}; 566 R_SUCCEED_IF(size == mapped_size);
567 }
422 568
423 // Reserve the memory from the process resource limit. 569 // Allocate and map the memory.
424 KScopedResourceReservation memory_reservation( 570 {
425 system.Kernel().CurrentProcess()->GetResourceLimit(), LimitableResource::PhysicalMemory, 571 // Reserve the memory from the process resource limit.
426 remaining_size); 572 KScopedResourceReservation memory_reservation(
427 if (!memory_reservation.Succeeded()) { 573 system.Kernel().CurrentProcess()->GetResourceLimit(),
428 LOG_ERROR(Kernel, "Could not reserve remaining {:X} bytes", remaining_size); 574 LimitableResource::PhysicalMemory, size - mapped_size);
429 return ResultLimitReached; 575 R_UNLESS(memory_reservation.Succeeded(), ResultLimitReached);
576
577 // Allocate pages for the new memory.
578 KPageLinkedList pg;
579 R_TRY(system.Kernel().MemoryManager().AllocateAndOpenForProcess(
580 &pg, (size - mapped_size) / PageSize,
581 KMemoryManager::EncodeOption(memory_pool, allocation_option), 0, 0));
582
583 // Map the memory.
584 {
585 // Lock the table.
586 KScopedLightLock lk(general_lock);
587
588 size_t num_allocator_blocks = 0;
589
590 // Verify that nobody has mapped memory since we first checked.
591 {
592 // Iterate over the memory.
593 size_t checked_mapped_size = 0;
594 cur_address = address;
595
596 auto it = block_manager->FindIterator(cur_address);
597 while (true) {
598 // Check that the iterator is valid.
599 ASSERT(it != block_manager->end());
600
601 // Get the memory info.
602 const KMemoryInfo info = it->GetMemoryInfo();
603
604 const bool is_free = info.GetState() == KMemoryState::Free;
605 if (is_free) {
606 if (info.GetAddress() < address) {
607 ++num_allocator_blocks;
608 }
609 if (last_address < info.GetLastAddress()) {
610 ++num_allocator_blocks;
611 }
612 }
613
614 // Check if we're done.
615 if (last_address <= info.GetLastAddress()) {
616 if (!is_free) {
617 checked_mapped_size += (last_address + 1 - cur_address);
618 }
619 break;
620 }
621
622 // Track the memory if it's mapped.
623 if (!is_free) {
624 checked_mapped_size += VAddr(info.GetEndAddress()) - cur_address;
625 }
626
627 // Advance.
628 cur_address = info.GetEndAddress();
629 ++it;
630 }
631
632 // If the size now isn't what it was before, somebody mapped or unmapped
633 // concurrently. If this happened, retry.
634 if (mapped_size != checked_mapped_size) {
635 continue;
636 }
637 }
638
639 // Reset the current tracking address, and make sure we clean up on failure.
640 cur_address = address;
641 auto unmap_guard = detail::ScopeExit([&] {
642 if (cur_address > address) {
643 const VAddr last_unmap_address = cur_address - 1;
644
645 // Iterate, unmapping the pages.
646 cur_address = address;
647
648 auto it = block_manager->FindIterator(cur_address);
649 while (true) {
650 // Check that the iterator is valid.
651 ASSERT(it != block_manager->end());
652
653 // Get the memory info.
654 const KMemoryInfo info = it->GetMemoryInfo();
655
656 // If the memory state is free, we mapped it and need to unmap it.
657 if (info.GetState() == KMemoryState::Free) {
658 // Determine the range to unmap.
659 const size_t cur_pages =
660 std::min(VAddr(info.GetEndAddress()) - cur_address,
661 last_unmap_address + 1 - cur_address) /
662 PageSize;
663
664 // Unmap.
665 ASSERT(Operate(cur_address, cur_pages, KMemoryPermission::None,
666 OperationType::Unmap)
667 .IsSuccess());
668 }
669
670 // Check if we're done.
671 if (last_unmap_address <= info.GetLastAddress()) {
672 break;
673 }
674
675 // Advance.
676 cur_address = info.GetEndAddress();
677 ++it;
678 }
679 }
680 });
681
682 // Iterate over the memory.
683 auto pg_it = pg.Nodes().begin();
684 PAddr pg_phys_addr = pg_it->GetAddress();
685 size_t pg_pages = pg_it->GetNumPages();
686
687 auto it = block_manager->FindIterator(cur_address);
688 while (true) {
689 // Check that the iterator is valid.
690 ASSERT(it != block_manager->end());
691
692 // Get the memory info.
693 const KMemoryInfo info = it->GetMemoryInfo();
694
695 // If it's unmapped, we need to map it.
696 if (info.GetState() == KMemoryState::Free) {
697 // Determine the range to map.
698 size_t map_pages = std::min(VAddr(info.GetEndAddress()) - cur_address,
699 last_address + 1 - cur_address) /
700 PageSize;
701
702 // While we have pages to map, map them.
703 while (map_pages > 0) {
704 // Check if we're at the end of the physical block.
705 if (pg_pages == 0) {
706 // Ensure there are more pages to map.
707 ASSERT(pg_it != pg.Nodes().end());
708
709 // Advance our physical block.
710 ++pg_it;
711 pg_phys_addr = pg_it->GetAddress();
712 pg_pages = pg_it->GetNumPages();
713 }
714
715 // Map whatever we can.
716 const size_t cur_pages = std::min(pg_pages, map_pages);
717 R_TRY(Operate(cur_address, cur_pages, KMemoryPermission::UserReadWrite,
718 OperationType::Map, pg_phys_addr));
719
720 // Advance.
721 cur_address += cur_pages * PageSize;
722 map_pages -= cur_pages;
723
724 pg_phys_addr += cur_pages * PageSize;
725 pg_pages -= cur_pages;
726 }
727 }
728
729 // Check if we're done.
730 if (last_address <= info.GetLastAddress()) {
731 break;
732 }
733
734 // Advance.
735 cur_address = info.GetEndAddress();
736 ++it;
737 }
738
739 // We succeeded, so commit the memory reservation.
740 memory_reservation.Commit();
741
742 // Increase our tracked mapped size.
743 mapped_physical_memory_size += (size - mapped_size);
744
745 // Update the relevant memory blocks.
746 block_manager->Update(address, size / PageSize, KMemoryState::Free,
747 KMemoryPermission::None, KMemoryAttribute::None,
748 KMemoryState::Normal, KMemoryPermission::UserReadWrite,
749 KMemoryAttribute::None);
750
751 // Cancel our guard.
752 unmap_guard.Cancel();
753
754 return ResultSuccess;
755 }
756 }
430 } 757 }
758}
431 759
432 KPageLinkedList page_linked_list; 760ResultCode KPageTable::UnmapPhysicalMemory(VAddr address, std::size_t size) {
761 // Lock the physical memory lock.
762 KScopedLightLock map_phys_mem_lk(map_physical_memory_lock);
433 763
434 CASCADE_CODE(system.Kernel().MemoryManager().Allocate(page_linked_list, remaining_pages, 764 // Lock the table.
435 memory_pool, allocation_option)); 765 KScopedLightLock lk(general_lock);
436 766
437 // We succeeded, so commit the memory reservation. 767 // Calculate the last address for convenience.
438 memory_reservation.Commit(); 768 const VAddr last_address = address + size - 1;
439 769
440 // Map the memory. 770 // Define iteration variables.
441 auto node{page_linked_list.Nodes().begin()}; 771 VAddr cur_address = 0;
442 PAddr map_addr{node->GetAddress()}; 772 std::size_t mapped_size = 0;
443 std::size_t src_num_pages{node->GetNumPages()}; 773 std::size_t num_allocator_blocks = 0;
444 block_manager->IterateForRange(addr, end_addr, [&](const KMemoryInfo& info) {
445 if (info.state != KMemoryState::Free) {
446 return;
447 }
448 774
449 std::size_t dst_num_pages{GetSizeInRange(info, addr, end_addr) / PageSize}; 775 // Check if the memory is mapped.
450 VAddr dst_addr{GetAddressInRange(info, addr)}; 776 {
777 // Iterate over the memory.
778 cur_address = address;
779 mapped_size = 0;
780
781 auto it = block_manager->FindIterator(cur_address);
782 while (true) {
783 // Check that the iterator is valid.
784 ASSERT(it != block_manager->end());
785
786 // Get the memory info.
787 const KMemoryInfo info = it->GetMemoryInfo();
788
789 // Verify the memory's state.
790 const bool is_normal = info.GetState() == KMemoryState::Normal &&
791 info.GetAttribute() == KMemoryAttribute::None;
792 const bool is_free = info.GetState() == KMemoryState::Free;
793 R_UNLESS(is_normal || is_free, ResultInvalidCurrentMemory);
794
795 if (is_normal) {
796 R_UNLESS(info.GetAttribute() == KMemoryAttribute::None, ResultInvalidCurrentMemory);
797
798 if (info.GetAddress() < address) {
799 ++num_allocator_blocks;
800 }
801 if (last_address < info.GetLastAddress()) {
802 ++num_allocator_blocks;
803 }
804 }
451 805
452 while (dst_num_pages) { 806 // Check if we're done.
453 if (!src_num_pages) { 807 if (last_address <= info.GetLastAddress()) {
454 node = std::next(node); 808 if (is_normal) {
455 map_addr = node->GetAddress(); 809 mapped_size += (last_address + 1 - cur_address);
456 src_num_pages = node->GetNumPages(); 810 }
811 break;
457 } 812 }
458 813
459 const std::size_t num_pages{std::min(src_num_pages, dst_num_pages)}; 814 // Track the memory if it's mapped.
460 Operate(dst_addr, num_pages, KMemoryPermission::UserReadWrite, OperationType::Map, 815 if (is_normal) {
461 map_addr); 816 mapped_size += VAddr(info.GetEndAddress()) - cur_address;
817 }
462 818
463 dst_addr += num_pages * PageSize; 819 // Advance.
464 map_addr += num_pages * PageSize; 820 cur_address = info.GetEndAddress();
465 src_num_pages -= num_pages; 821 ++it;
466 dst_num_pages -= num_pages;
467 } 822 }
468 });
469
470 mapped_physical_memory_size += remaining_size;
471 823
472 const std::size_t num_pages{size / PageSize}; 824 // If there's nothing mapped, we've nothing to do.
473 block_manager->Update(addr, num_pages, KMemoryState::Free, KMemoryPermission::None, 825 R_SUCCEED_IF(mapped_size == 0);
474 KMemoryAttribute::None, KMemoryState::Normal, 826 }
475 KMemoryPermission::UserReadWrite, KMemoryAttribute::None);
476 827
477 return ResultSuccess; 828 // Make a page group for the unmap region.
478} 829 KPageLinkedList pg;
830 {
831 auto& impl = this->PageTableImpl();
832
833 // Begin traversal.
834 Common::PageTable::TraversalContext context;
835 Common::PageTable::TraversalEntry cur_entry = {.phys_addr = 0, .block_size = 0};
836 bool cur_valid = false;
837 Common::PageTable::TraversalEntry next_entry;
838 bool next_valid = false;
839 size_t tot_size = 0;
840
841 cur_address = address;
842 next_valid = impl.BeginTraversal(next_entry, context, cur_address);
843 next_entry.block_size =
844 (next_entry.block_size - (next_entry.phys_addr & (next_entry.block_size - 1)));
845
846 // Iterate, building the group.
847 while (true) {
848 if ((!next_valid && !cur_valid) ||
849 (next_valid && cur_valid &&
850 next_entry.phys_addr == cur_entry.phys_addr + cur_entry.block_size)) {
851 cur_entry.block_size += next_entry.block_size;
852 } else {
853 if (cur_valid) {
854 // ASSERT(IsHeapPhysicalAddress(cur_entry.phys_addr));
855 R_TRY(pg.AddBlock(cur_entry.phys_addr, cur_entry.block_size / PageSize));
856 }
857
858 // Update tracking variables.
859 tot_size += cur_entry.block_size;
860 cur_entry = next_entry;
861 cur_valid = next_valid;
862 }
479 863
480ResultCode KPageTable::UnmapPhysicalMemory(VAddr addr, std::size_t size) { 864 if (cur_entry.block_size + tot_size >= size) {
481 std::lock_guard lock{page_table_lock}; 865 break;
866 }
482 867
483 const VAddr end_addr{addr + size}; 868 next_valid = impl.ContinueTraversal(next_entry, context);
484 ResultCode result{ResultSuccess}; 869 }
485 std::size_t mapped_size{};
486 870
487 // Verify that the region can be unmapped 871 // Add the last block.
488 block_manager->IterateForRange(addr, end_addr, [&](const KMemoryInfo& info) { 872 if (cur_valid) {
489 if (info.state == KMemoryState::Normal) { 873 // ASSERT(IsHeapPhysicalAddress(cur_entry.phys_addr));
490 if (info.attribute != KMemoryAttribute::None) { 874 R_TRY(pg.AddBlock(cur_entry.phys_addr, (size - tot_size) / PageSize));
491 result = ResultInvalidCurrentMemory; 875 }
492 return; 876 }
877 ASSERT(pg.GetNumPages() == mapped_size / PageSize);
878
879 // Reset the current tracking address, and make sure we clean up on failure.
880 cur_address = address;
881 auto remap_guard = detail::ScopeExit([&] {
882 if (cur_address > address) {
883 const VAddr last_map_address = cur_address - 1;
884 cur_address = address;
885
886 // Iterate over the memory we unmapped.
887 auto it = block_manager->FindIterator(cur_address);
888 auto pg_it = pg.Nodes().begin();
889 PAddr pg_phys_addr = pg_it->GetAddress();
890 size_t pg_pages = pg_it->GetNumPages();
891
892 while (true) {
893 // Get the memory info for the pages we unmapped, convert to property.
894 const KMemoryInfo info = it->GetMemoryInfo();
895
896 // If the memory is normal, we unmapped it and need to re-map it.
897 if (info.GetState() == KMemoryState::Normal) {
898 // Determine the range to map.
899 size_t map_pages = std::min(VAddr(info.GetEndAddress()) - cur_address,
900 last_map_address + 1 - cur_address) /
901 PageSize;
902
903 // While we have pages to map, map them.
904 while (map_pages > 0) {
905 // Check if we're at the end of the physical block.
906 if (pg_pages == 0) {
907 // Ensure there are more pages to map.
908 ASSERT(pg_it != pg.Nodes().end());
909
910 // Advance our physical block.
911 ++pg_it;
912 pg_phys_addr = pg_it->GetAddress();
913 pg_pages = pg_it->GetNumPages();
914 }
915
916 // Map whatever we can.
917 const size_t cur_pages = std::min(pg_pages, map_pages);
918 ASSERT(this->Operate(cur_address, cur_pages, info.GetPermission(),
919 OperationType::Map, pg_phys_addr) == ResultSuccess);
920
921 // Advance.
922 cur_address += cur_pages * PageSize;
923 map_pages -= cur_pages;
924
925 pg_phys_addr += cur_pages * PageSize;
926 pg_pages -= cur_pages;
927 }
928 }
929
930 // Check if we're done.
931 if (last_map_address <= info.GetLastAddress()) {
932 break;
933 }
934
935 // Advance.
936 ++it;
493 } 937 }
494 mapped_size += GetSizeInRange(info, addr, end_addr);
495 } else if (info.state != KMemoryState::Free) {
496 result = ResultInvalidCurrentMemory;
497 } 938 }
498 }); 939 });
499 940
500 if (result.IsError()) { 941 // Iterate over the memory, unmapping as we go.
501 return result; 942 auto it = block_manager->FindIterator(cur_address);
502 } 943 while (true) {
944 // Check that the iterator is valid.
945 ASSERT(it != block_manager->end());
503 946
504 if (!mapped_size) { 947 // Get the memory info.
505 return ResultSuccess; 948 const KMemoryInfo info = it->GetMemoryInfo();
506 }
507 949
508 // Unmap each region within the range 950 // If the memory state is normal, we need to unmap it.
509 KPageLinkedList page_linked_list; 951 if (info.GetState() == KMemoryState::Normal) {
510 block_manager->IterateForRange(addr, end_addr, [&](const KMemoryInfo& info) { 952 // Determine the range to unmap.
511 if (info.state == KMemoryState::Normal) { 953 const size_t cur_pages = std::min(VAddr(info.GetEndAddress()) - cur_address,
512 const std::size_t block_size{GetSizeInRange(info, addr, end_addr)}; 954 last_address + 1 - cur_address) /
513 const std::size_t block_num_pages{block_size / PageSize}; 955 PageSize;
514 const VAddr block_addr{GetAddressInRange(info, addr)}; 956
515 957 // Unmap.
516 AddRegionToPages(block_addr, block_size / PageSize, page_linked_list); 958 R_TRY(Operate(cur_address, cur_pages, KMemoryPermission::None, OperationType::Unmap));
517
518 if (result = Operate(block_addr, block_num_pages, KMemoryPermission::None,
519 OperationType::Unmap);
520 result.IsError()) {
521 return;
522 }
523 } 959 }
524 });
525 if (result.IsError()) {
526 return result;
527 }
528 960
529 const std::size_t num_pages{size / PageSize}; 961 // Check if we're done.
530 system.Kernel().MemoryManager().Free(page_linked_list, num_pages, memory_pool, 962 if (last_address <= info.GetLastAddress()) {
531 allocation_option); 963 break;
964 }
532 965
533 block_manager->Update(addr, num_pages, KMemoryState::Free); 966 // Advance.
967 cur_address = info.GetEndAddress();
968 ++it;
969 }
534 970
971 // Release the memory resource.
972 mapped_physical_memory_size -= mapped_size;
535 auto process{system.Kernel().CurrentProcess()}; 973 auto process{system.Kernel().CurrentProcess()};
536 process->GetResourceLimit()->Release(LimitableResource::PhysicalMemory, mapped_size); 974 process->GetResourceLimit()->Release(LimitableResource::PhysicalMemory, mapped_size);
537 mapped_physical_memory_size -= mapped_size; 975
976 // Update memory blocks.
977 block_manager->Update(address, size / PageSize, KMemoryState::Free, KMemoryPermission::None,
978 KMemoryAttribute::None);
979
980 // TODO(bunnei): This is a workaround until the next set of changes, where we add reference
981 // counting for mapped pages. Until then, we must manually close the reference to the page
982 // group.
983 system.Kernel().MemoryManager().Close(pg);
984
985 // We succeeded.
986 remap_guard.Cancel();
538 987
539 return ResultSuccess; 988 return ResultSuccess;
540} 989}
541 990
542ResultCode KPageTable::MapMemory(VAddr dst_addr, VAddr src_addr, std::size_t size) { 991ResultCode KPageTable::MapMemory(VAddr dst_addr, VAddr src_addr, std::size_t size) {
543 std::lock_guard lock{page_table_lock}; 992 KScopedLightLock lk(general_lock);
544 993
545 KMemoryState src_state{}; 994 KMemoryState src_state{};
546 CASCADE_CODE(CheckMemoryState( 995 CASCADE_CODE(CheckMemoryState(
@@ -579,7 +1028,7 @@ ResultCode KPageTable::MapMemory(VAddr dst_addr, VAddr src_addr, std::size_t siz
579} 1028}
580 1029
581ResultCode KPageTable::UnmapMemory(VAddr dst_addr, VAddr src_addr, std::size_t size) { 1030ResultCode KPageTable::UnmapMemory(VAddr dst_addr, VAddr src_addr, std::size_t size) {
582 std::lock_guard lock{page_table_lock}; 1031 KScopedLightLock lk(general_lock);
583 1032
584 KMemoryState src_state{}; 1033 KMemoryState src_state{};
585 CASCADE_CODE(CheckMemoryState( 1034 CASCADE_CODE(CheckMemoryState(
@@ -622,6 +1071,8 @@ ResultCode KPageTable::UnmapMemory(VAddr dst_addr, VAddr src_addr, std::size_t s
622 1071
623ResultCode KPageTable::MapPages(VAddr addr, const KPageLinkedList& page_linked_list, 1072ResultCode KPageTable::MapPages(VAddr addr, const KPageLinkedList& page_linked_list,
624 KMemoryPermission perm) { 1073 KMemoryPermission perm) {
1074 ASSERT(this->IsLockedByCurrentThread());
1075
625 VAddr cur_addr{addr}; 1076 VAddr cur_addr{addr};
626 1077
627 for (const auto& node : page_linked_list.Nodes()) { 1078 for (const auto& node : page_linked_list.Nodes()) {
@@ -650,7 +1101,7 @@ ResultCode KPageTable::MapPages(VAddr address, KPageLinkedList& page_linked_list
650 R_UNLESS(this->CanContain(address, size, state), ResultInvalidCurrentMemory); 1101 R_UNLESS(this->CanContain(address, size, state), ResultInvalidCurrentMemory);
651 1102
652 // Lock the table. 1103 // Lock the table.
653 std::lock_guard lock{page_table_lock}; 1104 KScopedLightLock lk(general_lock);
654 1105
655 // Check the memory state. 1106 // Check the memory state.
656 R_TRY(this->CheckMemoryState(address, size, KMemoryState::All, KMemoryState::Free, 1107 R_TRY(this->CheckMemoryState(address, size, KMemoryState::All, KMemoryState::Free,
@@ -666,13 +1117,54 @@ ResultCode KPageTable::MapPages(VAddr address, KPageLinkedList& page_linked_list
666 return ResultSuccess; 1117 return ResultSuccess;
667} 1118}
668 1119
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
669ResultCode KPageTable::UnmapPages(VAddr addr, const KPageLinkedList& page_linked_list) { 1160ResultCode KPageTable::UnmapPages(VAddr addr, const KPageLinkedList& page_linked_list) {
1161 ASSERT(this->IsLockedByCurrentThread());
1162
670 VAddr cur_addr{addr}; 1163 VAddr cur_addr{addr};
671 1164
672 for (const auto& node : page_linked_list.Nodes()) { 1165 for (const auto& node : page_linked_list.Nodes()) {
673 const std::size_t num_pages{(addr - cur_addr) / PageSize}; 1166 if (const auto result{Operate(cur_addr, node.GetNumPages(), KMemoryPermission::None,
674 if (const auto result{ 1167 OperationType::Unmap)};
675 Operate(addr, num_pages, KMemoryPermission::None, OperationType::Unmap)};
676 result.IsError()) { 1168 result.IsError()) {
677 return result; 1169 return result;
678 } 1170 }
@@ -691,7 +1183,7 @@ ResultCode KPageTable::UnmapPages(VAddr addr, KPageLinkedList& page_linked_list,
691 R_UNLESS(this->Contains(addr, size), ResultInvalidCurrentMemory); 1183 R_UNLESS(this->Contains(addr, size), ResultInvalidCurrentMemory);
692 1184
693 // Lock the table. 1185 // Lock the table.
694 std::lock_guard lock{page_table_lock}; 1186 KScopedLightLock lk(general_lock);
695 1187
696 // Check the memory state. 1188 // Check the memory state.
697 R_TRY(this->CheckMemoryState(addr, size, KMemoryState::All, state, KMemoryPermission::None, 1189 R_TRY(this->CheckMemoryState(addr, size, KMemoryState::All, state, KMemoryPermission::None,
@@ -707,12 +1199,36 @@ ResultCode KPageTable::UnmapPages(VAddr addr, KPageLinkedList& page_linked_list,
707 return ResultSuccess; 1199 return ResultSuccess;
708} 1200}
709 1201
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
710ResultCode KPageTable::SetProcessMemoryPermission(VAddr addr, std::size_t size, 1226ResultCode KPageTable::SetProcessMemoryPermission(VAddr addr, std::size_t size,
711 Svc::MemoryPermission svc_perm) { 1227 Svc::MemoryPermission svc_perm) {
712 const size_t num_pages = size / PageSize; 1228 const size_t num_pages = size / PageSize;
713 1229
714 // Lock the table. 1230 // Lock the table.
715 std::lock_guard lock{page_table_lock}; 1231 KScopedLightLock lk(general_lock);
716 1232
717 // Verify we can change the memory permission. 1233 // Verify we can change the memory permission.
718 KMemoryState old_state; 1234 KMemoryState old_state;
@@ -766,7 +1282,7 @@ ResultCode KPageTable::SetProcessMemoryPermission(VAddr addr, std::size_t size,
766} 1282}
767 1283
768KMemoryInfo KPageTable::QueryInfoImpl(VAddr addr) { 1284KMemoryInfo KPageTable::QueryInfoImpl(VAddr addr) {
769 std::lock_guard lock{page_table_lock}; 1285 KScopedLightLock lk(general_lock);
770 1286
771 return block_manager->FindBlock(addr).GetMemoryInfo(); 1287 return block_manager->FindBlock(addr).GetMemoryInfo();
772} 1288}
@@ -781,7 +1297,7 @@ KMemoryInfo KPageTable::QueryInfo(VAddr addr) {
781} 1297}
782 1298
783ResultCode KPageTable::ReserveTransferMemory(VAddr addr, std::size_t size, KMemoryPermission perm) { 1299ResultCode KPageTable::ReserveTransferMemory(VAddr addr, std::size_t size, KMemoryPermission perm) {
784 std::lock_guard lock{page_table_lock}; 1300 KScopedLightLock lk(general_lock);
785 1301
786 KMemoryState state{}; 1302 KMemoryState state{};
787 KMemoryAttribute attribute{}; 1303 KMemoryAttribute attribute{};
@@ -799,7 +1315,7 @@ ResultCode KPageTable::ReserveTransferMemory(VAddr addr, std::size_t size, KMemo
799} 1315}
800 1316
801ResultCode KPageTable::ResetTransferMemory(VAddr addr, std::size_t size) { 1317ResultCode KPageTable::ResetTransferMemory(VAddr addr, std::size_t size) {
802 std::lock_guard lock{page_table_lock}; 1318 KScopedLightLock lk(general_lock);
803 1319
804 KMemoryState state{}; 1320 KMemoryState state{};
805 1321
@@ -818,7 +1334,7 @@ ResultCode KPageTable::SetMemoryPermission(VAddr addr, std::size_t size,
818 const size_t num_pages = size / PageSize; 1334 const size_t num_pages = size / PageSize;
819 1335
820 // Lock the table. 1336 // Lock the table.
821 std::lock_guard lock{page_table_lock}; 1337 KScopedLightLock lk(general_lock);
822 1338
823 // Verify we can change the memory permission. 1339 // Verify we can change the memory permission.
824 KMemoryState old_state; 1340 KMemoryState old_state;
@@ -847,7 +1363,7 @@ ResultCode KPageTable::SetMemoryAttribute(VAddr addr, std::size_t size, u32 mask
847 KMemoryAttribute::SetMask); 1363 KMemoryAttribute::SetMask);
848 1364
849 // Lock the table. 1365 // Lock the table.
850 std::lock_guard lock{page_table_lock}; 1366 KScopedLightLock lk(general_lock);
851 1367
852 // Verify we can change the memory attribute. 1368 // Verify we can change the memory attribute.
853 KMemoryState old_state; 1369 KMemoryState old_state;
@@ -878,7 +1394,7 @@ ResultCode KPageTable::SetMemoryAttribute(VAddr addr, std::size_t size, u32 mask
878 1394
879ResultCode KPageTable::SetMaxHeapSize(std::size_t size) { 1395ResultCode KPageTable::SetMaxHeapSize(std::size_t size) {
880 // Lock the table. 1396 // Lock the table.
881 std::lock_guard lock{page_table_lock}; 1397 KScopedLightLock lk(general_lock);
882 1398
883 // Only process page tables are allowed to set heap size. 1399 // Only process page tables are allowed to set heap size.
884 ASSERT(!this->IsKernel()); 1400 ASSERT(!this->IsKernel());
@@ -889,15 +1405,15 @@ ResultCode KPageTable::SetMaxHeapSize(std::size_t size) {
889} 1405}
890 1406
891ResultCode KPageTable::SetHeapSize(VAddr* out, std::size_t size) { 1407ResultCode KPageTable::SetHeapSize(VAddr* out, std::size_t size) {
892 // Lock the physical memory lock. 1408 // Lock the physical memory mutex.
893 std::lock_guard phys_lk(map_physical_memory_lock); 1409 KScopedLightLock map_phys_mem_lk(map_physical_memory_lock);
894 1410
895 // Try to perform a reduction in heap, instead of an extension. 1411 // Try to perform a reduction in heap, instead of an extension.
896 VAddr cur_address{}; 1412 VAddr cur_address{};
897 std::size_t allocation_size{}; 1413 std::size_t allocation_size{};
898 { 1414 {
899 // Lock the table. 1415 // Lock the table.
900 std::lock_guard lk(page_table_lock); 1416 KScopedLightLock lk(general_lock);
901 1417
902 // Validate that setting heap size is possible at all. 1418 // Validate that setting heap size is possible at all.
903 R_UNLESS(!is_kernel, ResultOutOfMemory); 1419 R_UNLESS(!is_kernel, ResultOutOfMemory);
@@ -955,14 +1471,21 @@ ResultCode KPageTable::SetHeapSize(VAddr* out, std::size_t size) {
955 R_UNLESS(memory_reservation.Succeeded(), ResultLimitReached); 1471 R_UNLESS(memory_reservation.Succeeded(), ResultLimitReached);
956 1472
957 // Allocate pages for the heap extension. 1473 // Allocate pages for the heap extension.
958 KPageLinkedList page_linked_list; 1474 KPageLinkedList pg;
959 R_TRY(system.Kernel().MemoryManager().Allocate(page_linked_list, allocation_size / PageSize, 1475 R_TRY(system.Kernel().MemoryManager().AllocateAndOpen(
960 memory_pool, allocation_option)); 1476 &pg, allocation_size / PageSize,
1477 KMemoryManager::EncodeOption(memory_pool, allocation_option)));
1478
1479 // Clear all the newly allocated pages.
1480 for (const auto& it : pg.Nodes()) {
1481 std::memset(system.DeviceMemory().GetPointer(it.GetAddress()), heap_fill_value,
1482 it.GetSize());
1483 }
961 1484
962 // Map the pages. 1485 // Map the pages.
963 { 1486 {
964 // Lock the table. 1487 // Lock the table.
965 std::lock_guard lk(page_table_lock); 1488 KScopedLightLock lk(general_lock);
966 1489
967 // Ensure that the heap hasn't changed since we began executing. 1490 // Ensure that the heap hasn't changed since we began executing.
968 ASSERT(cur_address == current_heap_end); 1491 ASSERT(cur_address == current_heap_end);
@@ -976,7 +1499,7 @@ ResultCode KPageTable::SetHeapSize(VAddr* out, std::size_t size) {
976 1499
977 // Map the pages. 1500 // Map the pages.
978 const auto num_pages = allocation_size / PageSize; 1501 const auto num_pages = allocation_size / PageSize;
979 R_TRY(Operate(current_heap_end, num_pages, page_linked_list, OperationType::MapGroup)); 1502 R_TRY(Operate(current_heap_end, num_pages, pg, OperationType::MapGroup));
980 1503
981 // Clear all the newly allocated pages. 1504 // Clear all the newly allocated pages.
982 for (std::size_t cur_page = 0; cur_page < num_pages; ++cur_page) { 1505 for (std::size_t cur_page = 0; cur_page < num_pages; ++cur_page) {
@@ -1004,7 +1527,7 @@ ResultVal<VAddr> KPageTable::AllocateAndMapMemory(std::size_t needed_num_pages,
1004 bool is_map_only, VAddr region_start, 1527 bool is_map_only, VAddr region_start,
1005 std::size_t region_num_pages, KMemoryState state, 1528 std::size_t region_num_pages, KMemoryState state,
1006 KMemoryPermission perm, PAddr map_addr) { 1529 KMemoryPermission perm, PAddr map_addr) {
1007 std::lock_guard lock{page_table_lock}; 1530 KScopedLightLock lk(general_lock);
1008 1531
1009 if (!CanContain(region_start, region_num_pages * PageSize, state)) { 1532 if (!CanContain(region_start, region_num_pages * PageSize, state)) {
1010 return ResultInvalidCurrentMemory; 1533 return ResultInvalidCurrentMemory;
@@ -1024,8 +1547,9 @@ ResultVal<VAddr> KPageTable::AllocateAndMapMemory(std::size_t needed_num_pages,
1024 R_TRY(Operate(addr, needed_num_pages, perm, OperationType::Map, map_addr)); 1547 R_TRY(Operate(addr, needed_num_pages, perm, OperationType::Map, map_addr));
1025 } else { 1548 } else {
1026 KPageLinkedList page_group; 1549 KPageLinkedList page_group;
1027 R_TRY(system.Kernel().MemoryManager().Allocate(page_group, needed_num_pages, memory_pool, 1550 R_TRY(system.Kernel().MemoryManager().AllocateAndOpenForProcess(
1028 allocation_option)); 1551 &page_group, needed_num_pages,
1552 KMemoryManager::EncodeOption(memory_pool, allocation_option), 0, 0));
1029 R_TRY(Operate(addr, needed_num_pages, page_group, OperationType::MapGroup)); 1553 R_TRY(Operate(addr, needed_num_pages, page_group, OperationType::MapGroup));
1030 } 1554 }
1031 1555
@@ -1035,7 +1559,7 @@ ResultVal<VAddr> KPageTable::AllocateAndMapMemory(std::size_t needed_num_pages,
1035} 1559}
1036 1560
1037ResultCode KPageTable::LockForDeviceAddressSpace(VAddr addr, std::size_t size) { 1561ResultCode KPageTable::LockForDeviceAddressSpace(VAddr addr, std::size_t size) {
1038 std::lock_guard lock{page_table_lock}; 1562 KScopedLightLock lk(general_lock);
1039 1563
1040 KMemoryPermission perm{}; 1564 KMemoryPermission perm{};
1041 if (const ResultCode result{CheckMemoryState( 1565 if (const ResultCode result{CheckMemoryState(
@@ -1058,7 +1582,7 @@ ResultCode KPageTable::LockForDeviceAddressSpace(VAddr addr, std::size_t size) {
1058} 1582}
1059 1583
1060ResultCode KPageTable::UnlockForDeviceAddressSpace(VAddr addr, std::size_t size) { 1584ResultCode KPageTable::UnlockForDeviceAddressSpace(VAddr addr, std::size_t size) {
1061 std::lock_guard lock{page_table_lock}; 1585 KScopedLightLock lk(general_lock);
1062 1586
1063 KMemoryPermission perm{}; 1587 KMemoryPermission perm{};
1064 if (const ResultCode result{CheckMemoryState( 1588 if (const ResultCode result{CheckMemoryState(
@@ -1081,7 +1605,7 @@ ResultCode KPageTable::UnlockForDeviceAddressSpace(VAddr addr, std::size_t size)
1081} 1605}
1082 1606
1083ResultCode KPageTable::LockForCodeMemory(VAddr addr, std::size_t size) { 1607ResultCode KPageTable::LockForCodeMemory(VAddr addr, std::size_t size) {
1084 std::lock_guard lock{page_table_lock}; 1608 KScopedLightLock lk(general_lock);
1085 1609
1086 KMemoryPermission new_perm = KMemoryPermission::NotMapped | KMemoryPermission::KernelReadWrite; 1610 KMemoryPermission new_perm = KMemoryPermission::NotMapped | KMemoryPermission::KernelReadWrite;
1087 1611
@@ -1108,7 +1632,7 @@ ResultCode KPageTable::LockForCodeMemory(VAddr addr, std::size_t size) {
1108} 1632}
1109 1633
1110ResultCode KPageTable::UnlockForCodeMemory(VAddr addr, std::size_t size) { 1634ResultCode KPageTable::UnlockForCodeMemory(VAddr addr, std::size_t size) {
1111 std::lock_guard lock{page_table_lock}; 1635 KScopedLightLock lk(general_lock);
1112 1636
1113 KMemoryPermission new_perm = KMemoryPermission::UserReadWrite; 1637 KMemoryPermission new_perm = KMemoryPermission::UserReadWrite;
1114 1638
@@ -1232,7 +1756,7 @@ ResultCode KPageTable::Operate(VAddr addr, std::size_t num_pages, KMemoryPermiss
1232 return ResultSuccess; 1756 return ResultSuccess;
1233} 1757}
1234 1758
1235constexpr VAddr KPageTable::GetRegionAddress(KMemoryState state) const { 1759VAddr KPageTable::GetRegionAddress(KMemoryState state) const {
1236 switch (state) { 1760 switch (state) {
1237 case KMemoryState::Free: 1761 case KMemoryState::Free:
1238 case KMemoryState::Kernel: 1762 case KMemoryState::Kernel:
@@ -1268,7 +1792,7 @@ constexpr VAddr KPageTable::GetRegionAddress(KMemoryState state) const {
1268 } 1792 }
1269} 1793}
1270 1794
1271constexpr std::size_t KPageTable::GetRegionSize(KMemoryState state) const { 1795std::size_t KPageTable::GetRegionSize(KMemoryState state) const {
1272 switch (state) { 1796 switch (state) {
1273 case KMemoryState::Free: 1797 case KMemoryState::Free:
1274 case KMemoryState::Kernel: 1798 case KMemoryState::Kernel:
diff --git a/src/core/hle/kernel/k_page_table.h b/src/core/hle/kernel/k_page_table.h
index 60ae9b9e8..54c6adf8d 100644
--- a/src/core/hle/kernel/k_page_table.h
+++ b/src/core/hle/kernel/k_page_table.h
@@ -5,11 +5,12 @@
5#pragma once 5#pragma once
6 6
7#include <memory> 7#include <memory>
8#include <mutex>
9 8
9#include "common/common_funcs.h"
10#include "common/common_types.h" 10#include "common/common_types.h"
11#include "common/page_table.h" 11#include "common/page_table.h"
12#include "core/file_sys/program_metadata.h" 12#include "core/file_sys/program_metadata.h"
13#include "core/hle/kernel/k_light_lock.h"
13#include "core/hle/kernel/k_memory_block.h" 14#include "core/hle/kernel/k_memory_block.h"
14#include "core/hle/kernel/k_memory_manager.h" 15#include "core/hle/kernel/k_memory_manager.h"
15#include "core/hle/result.h" 16#include "core/hle/result.h"
@@ -22,17 +23,21 @@ namespace Kernel {
22 23
23class KMemoryBlockManager; 24class KMemoryBlockManager;
24 25
25class KPageTable final : NonCopyable { 26class KPageTable final {
26public: 27public:
28 YUZU_NON_COPYABLE(KPageTable);
29 YUZU_NON_MOVEABLE(KPageTable);
30
27 explicit KPageTable(Core::System& system_); 31 explicit KPageTable(Core::System& system_);
32 ~KPageTable();
28 33
29 ResultCode InitializeForProcess(FileSys::ProgramAddressSpaceType as_type, bool enable_aslr, 34 ResultCode InitializeForProcess(FileSys::ProgramAddressSpaceType as_type, bool enable_aslr,
30 VAddr code_addr, std::size_t code_size, 35 VAddr code_addr, std::size_t code_size,
31 KMemoryManager::Pool pool); 36 KMemoryManager::Pool pool);
32 ResultCode MapProcessCode(VAddr addr, std::size_t pages_count, KMemoryState state, 37 ResultCode MapProcessCode(VAddr addr, std::size_t pages_count, KMemoryState state,
33 KMemoryPermission perm); 38 KMemoryPermission perm);
34 ResultCode MapCodeMemory(VAddr dst_addr, VAddr src_addr, std::size_t size); 39 ResultCode MapCodeMemory(VAddr dst_address, VAddr src_address, std::size_t size);
35 ResultCode UnmapCodeMemory(VAddr dst_addr, VAddr src_addr, std::size_t size); 40 ResultCode UnmapCodeMemory(VAddr dst_address, VAddr src_address, std::size_t size);
36 ResultCode UnmapProcessMemory(VAddr dst_addr, std::size_t size, KPageTable& src_page_table, 41 ResultCode UnmapProcessMemory(VAddr dst_addr, std::size_t size, KPageTable& src_page_table,
37 VAddr src_addr); 42 VAddr src_addr);
38 ResultCode MapPhysicalMemory(VAddr addr, std::size_t size); 43 ResultCode MapPhysicalMemory(VAddr addr, std::size_t size);
@@ -41,7 +46,14 @@ public:
41 ResultCode UnmapMemory(VAddr dst_addr, VAddr src_addr, std::size_t size); 46 ResultCode UnmapMemory(VAddr dst_addr, VAddr src_addr, std::size_t size);
42 ResultCode MapPages(VAddr addr, KPageLinkedList& page_linked_list, KMemoryState state, 47 ResultCode MapPages(VAddr addr, KPageLinkedList& page_linked_list, KMemoryState state,
43 KMemoryPermission perm); 48 KMemoryPermission perm);
49 ResultCode MapPages(VAddr* out_addr, std::size_t num_pages, std::size_t alignment,
50 PAddr phys_addr, KMemoryState state, KMemoryPermission perm) {
51 return this->MapPages(out_addr, num_pages, alignment, phys_addr, true,
52 this->GetRegionAddress(state), this->GetRegionSize(state) / PageSize,
53 state, perm);
54 }
44 ResultCode UnmapPages(VAddr addr, KPageLinkedList& page_linked_list, KMemoryState state); 55 ResultCode UnmapPages(VAddr addr, KPageLinkedList& page_linked_list, KMemoryState state);
56 ResultCode UnmapPages(VAddr address, std::size_t num_pages, KMemoryState state);
45 ResultCode SetProcessMemoryPermission(VAddr addr, std::size_t size, 57 ResultCode SetProcessMemoryPermission(VAddr addr, std::size_t size,
46 Svc::MemoryPermission svc_perm); 58 Svc::MemoryPermission svc_perm);
47 KMemoryInfo QueryInfo(VAddr addr); 59 KMemoryInfo QueryInfo(VAddr addr);
@@ -86,6 +98,9 @@ private:
86 ResultCode InitializeMemoryLayout(VAddr start, VAddr end); 98 ResultCode InitializeMemoryLayout(VAddr start, VAddr end);
87 ResultCode MapPages(VAddr addr, const KPageLinkedList& page_linked_list, 99 ResultCode MapPages(VAddr addr, const KPageLinkedList& page_linked_list,
88 KMemoryPermission perm); 100 KMemoryPermission perm);
101 ResultCode MapPages(VAddr* out_addr, std::size_t num_pages, std::size_t alignment,
102 PAddr phys_addr, bool is_pa_valid, VAddr region_start,
103 std::size_t region_num_pages, KMemoryState state, KMemoryPermission perm);
89 ResultCode UnmapPages(VAddr addr, const KPageLinkedList& page_linked_list); 104 ResultCode UnmapPages(VAddr addr, const KPageLinkedList& page_linked_list);
90 bool IsRegionMapped(VAddr address, u64 size); 105 bool IsRegionMapped(VAddr address, u64 size);
91 bool IsRegionContiguous(VAddr addr, u64 size) const; 106 bool IsRegionContiguous(VAddr addr, u64 size) const;
@@ -97,8 +112,11 @@ private:
97 OperationType operation); 112 OperationType operation);
98 ResultCode Operate(VAddr addr, std::size_t num_pages, KMemoryPermission perm, 113 ResultCode Operate(VAddr addr, std::size_t num_pages, KMemoryPermission perm,
99 OperationType operation, PAddr map_addr = 0); 114 OperationType operation, PAddr map_addr = 0);
100 constexpr VAddr GetRegionAddress(KMemoryState state) const; 115 VAddr GetRegionAddress(KMemoryState state) const;
101 constexpr std::size_t GetRegionSize(KMemoryState state) const; 116 std::size_t GetRegionSize(KMemoryState state) const;
117
118 VAddr FindFreeArea(VAddr region_start, std::size_t region_num_pages, std::size_t num_pages,
119 std::size_t alignment, std::size_t offset, std::size_t guard_pages);
102 120
103 ResultCode CheckMemoryStateContiguous(std::size_t* out_blocks_needed, VAddr addr, 121 ResultCode CheckMemoryStateContiguous(std::size_t* out_blocks_needed, VAddr addr,
104 std::size_t size, KMemoryState state_mask, 122 std::size_t size, KMemoryState state_mask,
@@ -132,7 +150,7 @@ private:
132 return CheckMemoryState(nullptr, nullptr, nullptr, out_blocks_needed, addr, size, 150 return CheckMemoryState(nullptr, nullptr, nullptr, out_blocks_needed, addr, size,
133 state_mask, state, perm_mask, perm, attr_mask, attr, ignore_attr); 151 state_mask, state, perm_mask, perm, attr_mask, attr, ignore_attr);
134 } 152 }
135 ResultCode CheckMemoryState(VAddr addr, size_t size, KMemoryState state_mask, 153 ResultCode CheckMemoryState(VAddr addr, std::size_t size, KMemoryState state_mask,
136 KMemoryState state, KMemoryPermission perm_mask, 154 KMemoryState state, KMemoryPermission perm_mask,
137 KMemoryPermission perm, KMemoryAttribute attr_mask, 155 KMemoryPermission perm, KMemoryAttribute attr_mask,
138 KMemoryAttribute attr, 156 KMemoryAttribute attr,
@@ -142,11 +160,12 @@ private:
142 } 160 }
143 161
144 bool IsLockedByCurrentThread() const { 162 bool IsLockedByCurrentThread() const {
145 return true; 163 return general_lock.IsLockedByCurrentThread();
146 } 164 }
147 165
148 std::recursive_mutex page_table_lock; 166 mutable KLightLock general_lock;
149 std::mutex map_physical_memory_lock; 167 mutable KLightLock map_physical_memory_lock;
168
150 std::unique_ptr<KMemoryBlockManager> block_manager; 169 std::unique_ptr<KMemoryBlockManager> block_manager;
151 170
152public: 171public:
@@ -204,8 +223,8 @@ public:
204 constexpr VAddr GetAliasCodeRegionSize() const { 223 constexpr VAddr GetAliasCodeRegionSize() const {
205 return alias_code_region_end - alias_code_region_start; 224 return alias_code_region_end - alias_code_region_start;
206 } 225 }
207 size_t GetNormalMemorySize() { 226 std::size_t GetNormalMemorySize() {
208 std::lock_guard lk(page_table_lock); 227 KScopedLightLock lk(general_lock);
209 return GetHeapSize() + mapped_physical_memory_size; 228 return GetHeapSize() + mapped_physical_memory_size;
210 } 229 }
211 constexpr std::size_t GetAddressSpaceWidth() const { 230 constexpr std::size_t GetAddressSpaceWidth() const {
@@ -247,7 +266,10 @@ public:
247 constexpr bool IsInsideASLRRegion(VAddr address, std::size_t size) const { 266 constexpr bool IsInsideASLRRegion(VAddr address, std::size_t size) const {
248 return !IsOutsideASLRRegion(address, size); 267 return !IsOutsideASLRRegion(address, size);
249 } 268 }
250 constexpr PAddr GetPhysicalAddr(VAddr addr) { 269 constexpr std::size_t GetNumGuardPages() const {
270 return IsKernel() ? 1 : 4;
271 }
272 PAddr GetPhysicalAddr(VAddr addr) const {
251 const auto backing_addr = page_table_impl.backing_addr[addr >> PageBits]; 273 const auto backing_addr = page_table_impl.backing_addr[addr >> PageBits];
252 ASSERT(backing_addr); 274 ASSERT(backing_addr);
253 return backing_addr + addr; 275 return backing_addr + addr;
@@ -268,10 +290,6 @@ private:
268 return is_aslr_enabled; 290 return is_aslr_enabled;
269 } 291 }
270 292
271 constexpr std::size_t GetNumGuardPages() const {
272 return IsKernel() ? 1 : 4;
273 }
274
275 constexpr bool ContainsPages(VAddr addr, std::size_t num_pages) const { 293 constexpr bool ContainsPages(VAddr addr, std::size_t num_pages) const {
276 return (address_space_start <= addr) && 294 return (address_space_start <= addr) &&
277 (num_pages <= (address_space_end - address_space_start) / PageSize) && 295 (num_pages <= (address_space_end - address_space_start) / PageSize) &&
@@ -303,6 +321,8 @@ private:
303 bool is_kernel{}; 321 bool is_kernel{};
304 bool is_aslr_enabled{}; 322 bool is_aslr_enabled{};
305 323
324 u32 heap_fill_value{};
325
306 KMemoryManager::Pool memory_pool{KMemoryManager::Pool::Application}; 326 KMemoryManager::Pool memory_pool{KMemoryManager::Pool::Application};
307 KMemoryManager::Direction allocation_option{KMemoryManager::Direction::FromFront}; 327 KMemoryManager::Direction allocation_option{KMemoryManager::Direction::FromFront};
308 328
diff --git a/src/core/hle/kernel/k_port.cpp b/src/core/hle/kernel/k_port.cpp
index a8ba09c4a..ceb98709f 100644
--- a/src/core/hle/kernel/k_port.cpp
+++ b/src/core/hle/kernel/k_port.cpp
@@ -57,7 +57,12 @@ ResultCode KPort::EnqueueSession(KServerSession* session) {
57 R_UNLESS(state == State::Normal, ResultPortClosed); 57 R_UNLESS(state == State::Normal, ResultPortClosed);
58 58
59 server.EnqueueSession(session); 59 server.EnqueueSession(session);
60 server.GetSessionRequestHandler()->ClientConnected(server.AcceptSession()); 60
61 if (auto session_ptr = server.GetSessionRequestHandler().lock()) {
62 session_ptr->ClientConnected(server.AcceptSession());
63 } else {
64 UNREACHABLE();
65 }
61 66
62 return ResultSuccess; 67 return ResultSuccess;
63} 68}
diff --git a/src/core/hle/kernel/k_process.cpp b/src/core/hle/kernel/k_process.cpp
index 85c506979..b39405496 100644
--- a/src/core/hle/kernel/k_process.cpp
+++ b/src/core/hle/kernel/k_process.cpp
@@ -70,65 +70,12 @@ void SetupMainThread(Core::System& system, KProcess& owner_process, u32 priority
70} 70}
71} // Anonymous namespace 71} // Anonymous namespace
72 72
73// Represents a page used for thread-local storage.
74//
75// Each TLS page contains slots that may be used by processes and threads.
76// Every process and thread is created with a slot in some arbitrary page
77// (whichever page happens to have an available slot).
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) { 74 ProcessType type, KResourceLimit* res_limit) {
127 auto& kernel = system.Kernel(); 75 auto& kernel = system.Kernel();
128 76
129 process->name = std::move(process_name); 77 process->name = std::move(process_name);
130 78 process->resource_limit = res_limit;
131 process->resource_limit = kernel.GetSystemResourceLimit();
132 process->status = ProcessStatus::Created; 79 process->status = ProcessStatus::Created;
133 process->program_id = 0; 80 process->program_id = 0;
134 process->process_id = type == ProcessType::KernelInternal ? kernel.CreateNewKernelProcessID() 81 process->process_id = type == ProcessType::KernelInternal ? kernel.CreateNewKernelProcessID()
@@ -143,9 +90,6 @@ ResultCode KProcess::Initialize(KProcess* process, Core::System& system, std::st
143 90
144 kernel.AppendNewProcess(process); 91 kernel.AppendNewProcess(process);
145 92
146 // Open a reference to the resource limit.
147 process->resource_limit->Open();
148
149 // Clear remaining fields. 93 // Clear remaining fields.
150 process->num_running_threads = 0; 94 process->num_running_threads = 0;
151 process->is_signaled = false; 95 process->is_signaled = false;
@@ -153,6 +97,9 @@ ResultCode KProcess::Initialize(KProcess* process, Core::System& system, std::st
153 process->is_suspended = false; 97 process->is_suspended = false;
154 process->schedule_count = 0; 98 process->schedule_count = 0;
155 99
100 // Open a reference to the resource limit.
101 process->resource_limit->Open();
102
156 return ResultSuccess; 103 return ResultSuccess;
157} 104}
158 105
@@ -405,7 +352,7 @@ ResultCode KProcess::LoadFromMetadata(const FileSys::ProgramMetadata& metadata,
405 } 352 }
406 353
407 // Create TLS region 354 // Create TLS region
408 tls_region_address = CreateTLSRegion(); 355 R_TRY(this->CreateThreadLocalRegion(std::addressof(tls_region_address)));
409 memory_reservation.Commit(); 356 memory_reservation.Commit();
410 357
411 return handle_table.Initialize(capabilities.GetHandleTableSize()); 358 return handle_table.Initialize(capabilities.GetHandleTableSize());
@@ -445,7 +392,7 @@ void KProcess::PrepareForTermination() {
445 392
446 stop_threads(kernel.System().GlobalSchedulerContext().GetThreadList()); 393 stop_threads(kernel.System().GlobalSchedulerContext().GetThreadList());
447 394
448 FreeTLSRegion(tls_region_address); 395 this->DeleteThreadLocalRegion(tls_region_address);
449 tls_region_address = 0; 396 tls_region_address = 0;
450 397
451 if (resource_limit) { 398 if (resource_limit) {
@@ -457,9 +404,6 @@ void KProcess::PrepareForTermination() {
457} 404}
458 405
459void KProcess::Finalize() { 406void KProcess::Finalize() {
460 // Finalize the handle table and close any open handles.
461 handle_table.Finalize();
462
463 // Free all shared memory infos. 407 // Free all shared memory infos.
464 { 408 {
465 auto it = shared_memory_list.begin(); 409 auto it = shared_memory_list.begin();
@@ -484,67 +428,110 @@ void KProcess::Finalize() {
484 resource_limit = nullptr; 428 resource_limit = nullptr;
485 } 429 }
486 430
431 // Finalize the page table.
432 page_table.reset();
433
487 // Perform inherited finalization. 434 // Perform inherited finalization.
488 KAutoObjectWithSlabHeapAndContainer<KProcess, KWorkerTask>::Finalize(); 435 KAutoObjectWithSlabHeapAndContainer<KProcess, KWorkerTask>::Finalize();
489} 436}
490 437
491/** 438ResultCode KProcess::CreateThreadLocalRegion(VAddr* out) {
492 * Attempts to find a TLS page that contains a free slot for 439 KThreadLocalPage* tlp = nullptr;
493 * use by a thread. 440 VAddr tlr = 0;
494 *
495 * @returns If a page with an available slot is found, then an iterator
496 * pointing to the page is returned. Otherwise the end iterator
497 * is returned instead.
498 */
499static auto FindTLSPageWithAvailableSlots(std::vector<TLSPage>& tls_pages) {
500 return std::find_if(tls_pages.begin(), tls_pages.end(),
501 [](const auto& page) { return page.HasAvailableSlots(); });
502}
503 441
504VAddr KProcess::CreateTLSRegion() { 442 // See if we can get a region from a partially used TLP.
505 KScopedSchedulerLock lock(kernel); 443 {
506 if (auto tls_page_iter{FindTLSPageWithAvailableSlots(tls_pages)}; 444 KScopedSchedulerLock sl{kernel};
507 tls_page_iter != tls_pages.cend()) {
508 return *tls_page_iter->ReserveSlot();
509 }
510 445
511 Page* const tls_page_ptr{kernel.GetUserSlabHeapPages().Allocate()}; 446 if (auto it = partially_used_tlp_tree.begin(); it != partially_used_tlp_tree.end()) {
512 ASSERT(tls_page_ptr); 447 tlr = it->Reserve();
448 ASSERT(tlr != 0);
513 449
514 const VAddr start{page_table->GetKernelMapRegionStart()}; 450 if (it->IsAllUsed()) {
515 const VAddr size{page_table->GetKernelMapRegionEnd() - start}; 451 tlp = std::addressof(*it);
516 const PAddr tls_map_addr{kernel.System().DeviceMemory().GetPhysicalAddr(tls_page_ptr)}; 452 partially_used_tlp_tree.erase(it);
517 const VAddr tls_page_addr{page_table 453 fully_used_tlp_tree.insert(*tlp);
518 ->AllocateAndMapMemory(1, PageSize, true, start, size / PageSize, 454 }
519 KMemoryState::ThreadLocal, 455
520 KMemoryPermission::UserReadWrite, 456 *out = tlr;
521 tls_map_addr) 457 return ResultSuccess;
522 .ValueOr(0)}; 458 }
459 }
523 460
524 ASSERT(tls_page_addr); 461 // Allocate a new page.
462 tlp = KThreadLocalPage::Allocate(kernel);
463 R_UNLESS(tlp != nullptr, ResultOutOfMemory);
464 auto tlp_guard = SCOPE_GUARD({ KThreadLocalPage::Free(kernel, tlp); });
525 465
526 std::memset(tls_page_ptr, 0, PageSize); 466 // Initialize the new page.
527 tls_pages.emplace_back(tls_page_addr); 467 R_TRY(tlp->Initialize(kernel, this));
528 468
529 const auto reserve_result{tls_pages.back().ReserveSlot()}; 469 // Reserve a TLR.
530 ASSERT(reserve_result.has_value()); 470 tlr = tlp->Reserve();
471 ASSERT(tlr != 0);
531 472
532 return *reserve_result; 473 // Insert into our tree.
474 {
475 KScopedSchedulerLock sl{kernel};
476 if (tlp->IsAllUsed()) {
477 fully_used_tlp_tree.insert(*tlp);
478 } else {
479 partially_used_tlp_tree.insert(*tlp);
480 }
481 }
482
483 // We succeeded!
484 tlp_guard.Cancel();
485 *out = tlr;
486 return ResultSuccess;
533} 487}
534 488
535void KProcess::FreeTLSRegion(VAddr tls_address) { 489ResultCode KProcess::DeleteThreadLocalRegion(VAddr addr) {
536 KScopedSchedulerLock lock(kernel); 490 KThreadLocalPage* page_to_free = nullptr;
537 const VAddr aligned_address = Common::AlignDown(tls_address, Core::Memory::PAGE_SIZE); 491
538 auto iter = 492 // Release the region.
539 std::find_if(tls_pages.begin(), tls_pages.end(), [aligned_address](const auto& page) { 493 {
540 return page.GetBaseAddress() == aligned_address; 494 KScopedSchedulerLock sl{kernel};
541 }); 495
496 // Try to find the page in the partially used list.
497 auto it = partially_used_tlp_tree.find_key(Common::AlignDown(addr, PageSize));
498 if (it == partially_used_tlp_tree.end()) {
499 // If we don't find it, it has to be in the fully used list.
500 it = fully_used_tlp_tree.find_key(Common::AlignDown(addr, PageSize));
501 R_UNLESS(it != fully_used_tlp_tree.end(), ResultInvalidAddress);
502
503 // Release the region.
504 it->Release(addr);
505
506 // Move the page out of the fully used list.
507 KThreadLocalPage* tlp = std::addressof(*it);
508 fully_used_tlp_tree.erase(it);
509 if (tlp->IsAllFree()) {
510 page_to_free = tlp;
511 } else {
512 partially_used_tlp_tree.insert(*tlp);
513 }
514 } else {
515 // Release the region.
516 it->Release(addr);
517
518 // Handle the all-free case.
519 KThreadLocalPage* tlp = std::addressof(*it);
520 if (tlp->IsAllFree()) {
521 partially_used_tlp_tree.erase(it);
522 page_to_free = tlp;
523 }
524 }
525 }
526
527 // If we should free the page it was in, do so.
528 if (page_to_free != nullptr) {
529 page_to_free->Finalize();
542 530
543 // Something has gone very wrong if we're freeing a region 531 KThreadLocalPage::Free(kernel, page_to_free);
544 // with no actual page available. 532 }
545 ASSERT(iter != tls_pages.cend());
546 533
547 iter->ReleaseSlot(tls_address); 534 return ResultSuccess;
548} 535}
549 536
550void 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 38b446350..5ed0f2d83 100644
--- a/src/core/hle/kernel/k_process.h
+++ b/src/core/hle/kernel/k_process.h
@@ -15,6 +15,7 @@
15#include "core/hle/kernel/k_condition_variable.h" 15#include "core/hle/kernel/k_condition_variable.h"
16#include "core/hle/kernel/k_handle_table.h" 16#include "core/hle/kernel/k_handle_table.h"
17#include "core/hle/kernel/k_synchronization_object.h" 17#include "core/hle/kernel/k_synchronization_object.h"
18#include "core/hle/kernel/k_thread_local_page.h"
18#include "core/hle/kernel/k_worker_task.h" 19#include "core/hle/kernel/k_worker_task.h"
19#include "core/hle/kernel/process_capability.h" 20#include "core/hle/kernel/process_capability.h"
20#include "core/hle/kernel/slab_helpers.h" 21#include "core/hle/kernel/slab_helpers.h"
@@ -91,7 +92,7 @@ public:
91 static constexpr std::size_t RANDOM_ENTROPY_SIZE = 4; 92 static constexpr std::size_t RANDOM_ENTROPY_SIZE = 4;
92 93
93 static ResultCode Initialize(KProcess* process, Core::System& system, std::string process_name, 94 static ResultCode Initialize(KProcess* process, Core::System& system, std::string process_name,
94 ProcessType type); 95 ProcessType type, KResourceLimit* res_limit);
95 96
96 /// Gets a reference to the process' page table. 97 /// Gets a reference to the process' page table.
97 KPageTable& PageTable() { 98 KPageTable& PageTable() {
@@ -362,10 +363,10 @@ public:
362 // Thread-local storage management 363 // Thread-local storage management
363 364
364 // Marks the next available region as used and returns the address of the slot. 365 // Marks the next available region as used and returns the address of the slot.
365 [[nodiscard]] VAddr CreateTLSRegion(); 366 [[nodiscard]] ResultCode CreateThreadLocalRegion(VAddr* out);
366 367
367 // Frees a used TLS slot identified by the given address 368 // Frees a used TLS slot identified by the given address
368 void FreeTLSRegion(VAddr tls_address); 369 ResultCode DeleteThreadLocalRegion(VAddr addr);
369 370
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_resource_limit.cpp b/src/core/hle/kernel/k_resource_limit.cpp
index 0c4bba66b..a84977c68 100644
--- a/src/core/hle/kernel/k_resource_limit.cpp
+++ b/src/core/hle/kernel/k_resource_limit.cpp
@@ -3,6 +3,7 @@
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include "common/assert.h" 5#include "common/assert.h"
6#include "core/core.h"
6#include "core/core_timing.h" 7#include "core/core_timing.h"
7#include "core/hle/kernel/k_resource_limit.h" 8#include "core/hle/kernel/k_resource_limit.h"
8#include "core/hle/kernel/svc_results.h" 9#include "core/hle/kernel/svc_results.h"
@@ -151,4 +152,22 @@ void KResourceLimit::Release(LimitableResource which, s64 value, s64 hint) {
151 } 152 }
152} 153}
153 154
155KResourceLimit* CreateResourceLimitForProcess(Core::System& system, s64 physical_memory_size) {
156 auto* resource_limit = KResourceLimit::Create(system.Kernel());
157 resource_limit->Initialize(&system.CoreTiming());
158
159 // Initialize default resource limit values.
160 // TODO(bunnei): These values are the system defaults, the limits for service processes are
161 // lower. These should use the correct limit values.
162
163 ASSERT(resource_limit->SetLimitValue(LimitableResource::PhysicalMemory, physical_memory_size)
164 .IsSuccess());
165 ASSERT(resource_limit->SetLimitValue(LimitableResource::Threads, 800).IsSuccess());
166 ASSERT(resource_limit->SetLimitValue(LimitableResource::Events, 900).IsSuccess());
167 ASSERT(resource_limit->SetLimitValue(LimitableResource::TransferMemory, 200).IsSuccess());
168 ASSERT(resource_limit->SetLimitValue(LimitableResource::Sessions, 1133).IsSuccess());
169
170 return resource_limit;
171}
172
154} // namespace Kernel 173} // namespace Kernel
diff --git a/src/core/hle/kernel/k_resource_limit.h b/src/core/hle/kernel/k_resource_limit.h
index fab6005ff..d23d16aa4 100644
--- a/src/core/hle/kernel/k_resource_limit.h
+++ b/src/core/hle/kernel/k_resource_limit.h
@@ -67,4 +67,7 @@ private:
67 KLightConditionVariable cond_var; 67 KLightConditionVariable cond_var;
68 const Core::Timing::CoreTiming* core_timing{}; 68 const Core::Timing::CoreTiming* core_timing{};
69}; 69};
70
71KResourceLimit* CreateResourceLimitForProcess(Core::System& system, s64 physical_memory_size);
72
70} // namespace Kernel 73} // namespace Kernel
diff --git a/src/core/hle/kernel/k_server_port.h b/src/core/hle/kernel/k_server_port.h
index 6302d5e61..2185736be 100644
--- a/src/core/hle/kernel/k_server_port.h
+++ b/src/core/hle/kernel/k_server_port.h
@@ -30,11 +30,11 @@ public:
30 30
31 /// Whether or not this server port has an HLE handler available. 31 /// Whether or not this server port has an HLE handler available.
32 bool HasSessionRequestHandler() const { 32 bool HasSessionRequestHandler() const {
33 return session_handler != nullptr; 33 return !session_handler.expired();
34 } 34 }
35 35
36 /// Gets the HLE handler for this port. 36 /// Gets the HLE handler for this port.
37 SessionRequestHandlerPtr GetSessionRequestHandler() const { 37 SessionRequestHandlerWeakPtr GetSessionRequestHandler() const {
38 return session_handler; 38 return session_handler;
39 } 39 }
40 40
@@ -42,7 +42,7 @@ public:
42 * Sets the HLE handler template for the port. ServerSessions crated by connecting to this port 42 * Sets the HLE handler template for the port. ServerSessions crated by connecting to this port
43 * will inherit a reference to this handler. 43 * will inherit a reference to this handler.
44 */ 44 */
45 void SetSessionHandler(SessionRequestHandlerPtr&& handler) { 45 void SetSessionHandler(SessionRequestHandlerWeakPtr&& handler) {
46 session_handler = std::move(handler); 46 session_handler = std::move(handler);
47 } 47 }
48 48
@@ -66,7 +66,7 @@ private:
66 void CleanupSessions(); 66 void CleanupSessions();
67 67
68 SessionList session_list; 68 SessionList session_list;
69 SessionRequestHandlerPtr session_handler; 69 SessionRequestHandlerWeakPtr session_handler;
70 KPort* parent{}; 70 KPort* parent{};
71}; 71};
72 72
diff --git a/src/core/hle/kernel/k_server_session.cpp b/src/core/hle/kernel/k_server_session.cpp
index 4d94eb9cf..30c56ff29 100644
--- a/src/core/hle/kernel/k_server_session.cpp
+++ b/src/core/hle/kernel/k_server_session.cpp
@@ -27,10 +27,7 @@ namespace Kernel {
27 27
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 0ad74b0a0..5690cc757 100644
--- a/src/core/hle/kernel/k_slab_heap.h
+++ b/src/core/hle/kernel/k_slab_heap.h
@@ -7,6 +7,7 @@
7#include <atomic> 7#include <atomic>
8 8
9#include "common/assert.h" 9#include "common/assert.h"
10#include "common/common_funcs.h"
10#include "common/common_types.h" 11#include "common/common_types.h"
11 12
12namespace Kernel { 13namespace Kernel {
@@ -15,35 +16,34 @@ class KernelCore;
15 16
16namespace impl { 17namespace impl {
17 18
18class KSlabHeapImpl final : NonCopyable { 19class KSlabHeapImpl {
20 YUZU_NON_COPYABLE(KSlabHeapImpl);
21 YUZU_NON_MOVEABLE(KSlabHeapImpl);
22
19public: 23public:
20 struct Node { 24 struct Node {
21 Node* next{}; 25 Node* next{};
22 }; 26 };
23 27
28public:
24 constexpr KSlabHeapImpl() = default; 29 constexpr KSlabHeapImpl() = default;
25 30
26 void Initialize(std::size_t size) { 31 void Initialize() {
27 ASSERT(head == nullptr); 32 ASSERT(m_head == nullptr);
28 obj_size = size;
29 }
30
31 constexpr std::size_t GetObjectSize() const {
32 return obj_size;
33 } 33 }
34 34
35 Node* GetHead() const { 35 Node* GetHead() const {
36 return head; 36 return m_head;
37 } 37 }
38 38
39 void* Allocate() { 39 void* Allocate() {
40 Node* ret = head.load(); 40 Node* ret = m_head.load();
41 41
42 do { 42 do {
43 if (ret == nullptr) { 43 if (ret == nullptr) {
44 break; 44 break;
45 } 45 }
46 } while (!head.compare_exchange_weak(ret, ret->next)); 46 } while (!m_head.compare_exchange_weak(ret, ret->next));
47 47
48 return ret; 48 return ret;
49 } 49 }
@@ -51,166 +51,157 @@ public:
51 void Free(void* obj) { 51 void Free(void* obj) {
52 Node* node = static_cast<Node*>(obj); 52 Node* node = static_cast<Node*>(obj);
53 53
54 Node* cur_head = head.load(); 54 Node* cur_head = m_head.load();
55 do { 55 do {
56 node->next = cur_head; 56 node->next = cur_head;
57 } while (!head.compare_exchange_weak(cur_head, node)); 57 } while (!m_head.compare_exchange_weak(cur_head, node));
58 } 58 }
59 59
60private: 60private:
61 std::atomic<Node*> head{}; 61 std::atomic<Node*> m_head{};
62 std::size_t obj_size{};
63}; 62};
64 63
65} // namespace impl 64} // namespace impl
66 65
67class KSlabHeapBase : NonCopyable { 66template <bool SupportDynamicExpansion>
67class KSlabHeapBase : protected impl::KSlabHeapImpl {
68 YUZU_NON_COPYABLE(KSlabHeapBase);
69 YUZU_NON_MOVEABLE(KSlabHeapBase);
70
71private:
72 size_t m_obj_size{};
73 uintptr_t m_peak{};
74 uintptr_t m_start{};
75 uintptr_t m_end{};
76
77private:
78 void UpdatePeakImpl(uintptr_t obj) {
79 static_assert(std::atomic_ref<uintptr_t>::is_always_lock_free);
80 std::atomic_ref<uintptr_t> peak_ref(m_peak);
81
82 const uintptr_t alloc_peak = obj + this->GetObjectSize();
83 uintptr_t cur_peak = m_peak;
84 do {
85 if (alloc_peak <= cur_peak) {
86 break;
87 }
88 } while (!peak_ref.compare_exchange_strong(cur_peak, alloc_peak));
89 }
90
68public: 91public:
69 constexpr KSlabHeapBase() = default; 92 constexpr KSlabHeapBase() = default;
70 93
71 constexpr bool Contains(uintptr_t addr) const { 94 bool Contains(uintptr_t address) const {
72 return start <= addr && addr < end; 95 return m_start <= address && address < m_end;
73 } 96 }
74 97
75 constexpr std::size_t GetSlabHeapSize() const { 98 void Initialize(size_t obj_size, void* memory, size_t memory_size) {
76 return (end - start) / GetObjectSize(); 99 // Ensure we don't initialize a slab using null memory.
77 } 100 ASSERT(memory != nullptr);
78 101
79 constexpr std::size_t GetObjectSize() const { 102 // Set our object size.
80 return impl.GetObjectSize(); 103 m_obj_size = obj_size;
81 }
82 104
83 constexpr uintptr_t GetSlabHeapAddress() const { 105 // Initialize the base allocator.
84 return start; 106 KSlabHeapImpl::Initialize();
85 } 107
108 // Set our tracking variables.
109 const size_t num_obj = (memory_size / obj_size);
110 m_start = reinterpret_cast<uintptr_t>(memory);
111 m_end = m_start + num_obj * obj_size;
112 m_peak = m_start;
86 113
87 std::size_t GetObjectIndexImpl(const void* obj) const { 114 // Free the objects.
88 return (reinterpret_cast<uintptr_t>(obj) - start) / GetObjectSize(); 115 u8* cur = reinterpret_cast<u8*>(m_end);
116
117 for (size_t i = 0; i < num_obj; i++) {
118 cur -= obj_size;
119 KSlabHeapImpl::Free(cur);
120 }
89 } 121 }
90 122
91 std::size_t GetPeakIndex() const { 123 size_t GetSlabHeapSize() const {
92 return GetObjectIndexImpl(reinterpret_cast<const void*>(peak)); 124 return (m_end - m_start) / this->GetObjectSize();
93 } 125 }
94 126
95 void* AllocateImpl() { 127 size_t GetObjectSize() const {
96 return impl.Allocate(); 128 return m_obj_size;
97 } 129 }
98 130
99 void FreeImpl(void* obj) { 131 void* Allocate() {
100 // Don't allow freeing an object that wasn't allocated from this heap 132 void* obj = KSlabHeapImpl::Allocate();
101 ASSERT(Contains(reinterpret_cast<uintptr_t>(obj)));
102 133
103 impl.Free(obj); 134 return obj;
104 } 135 }
105 136
106 void InitializeImpl(std::size_t obj_size, void* memory, std::size_t memory_size) { 137 void Free(void* obj) {
107 // Ensure we don't initialize a slab using null memory 138 // Don't allow freeing an object that wasn't allocated from this heap.
108 ASSERT(memory != nullptr); 139 const bool contained = this->Contains(reinterpret_cast<uintptr_t>(obj));
109 140 ASSERT(contained);
110 // Initialize the base allocator 141 KSlabHeapImpl::Free(obj);
111 impl.Initialize(obj_size); 142 }
112 143
113 // Set our tracking variables 144 size_t GetObjectIndex(const void* obj) const {
114 const std::size_t num_obj = (memory_size / obj_size); 145 if constexpr (SupportDynamicExpansion) {
115 start = reinterpret_cast<uintptr_t>(memory); 146 if (!this->Contains(reinterpret_cast<uintptr_t>(obj))) {
116 end = start + num_obj * obj_size; 147 return std::numeric_limits<size_t>::max();
117 peak = start; 148 }
149 }
118 150
119 // Free the objects 151 return (reinterpret_cast<uintptr_t>(obj) - m_start) / this->GetObjectSize();
120 u8* cur = reinterpret_cast<u8*>(end); 152 }
121 153
122 for (std::size_t i{}; i < num_obj; i++) { 154 size_t GetPeakIndex() const {
123 cur -= obj_size; 155 return this->GetObjectIndex(reinterpret_cast<const void*>(m_peak));
124 impl.Free(cur);
125 }
126 } 156 }
127 157
128private: 158 uintptr_t GetSlabHeapAddress() const {
129 using Impl = impl::KSlabHeapImpl; 159 return m_start;
160 }
130 161
131 Impl impl; 162 size_t GetNumRemaining() const {
132 uintptr_t peak{}; 163 // Only calculate the number of remaining objects under debug configuration.
133 uintptr_t start{}; 164 return 0;
134 uintptr_t end{}; 165 }
135}; 166};
136 167
137template <typename T> 168template <typename T>
138class KSlabHeap final : public KSlabHeapBase { 169class KSlabHeap final : public KSlabHeapBase<false> {
139public: 170private:
140 enum class AllocationType { 171 using BaseHeap = KSlabHeapBase<false>;
141 Host,
142 Guest,
143 };
144 172
145 explicit constexpr KSlabHeap(AllocationType allocation_type_ = AllocationType::Host) 173public:
146 : KSlabHeapBase(), allocation_type{allocation_type_} {} 174 constexpr KSlabHeap() = default;
147 175
148 void Initialize(void* memory, std::size_t memory_size) { 176 void Initialize(void* memory, size_t memory_size) {
149 if (allocation_type == AllocationType::Guest) { 177 BaseHeap::Initialize(sizeof(T), memory, memory_size);
150 InitializeImpl(sizeof(T), memory, memory_size);
151 }
152 } 178 }
153 179
154 T* Allocate() { 180 T* Allocate() {
155 switch (allocation_type) { 181 T* obj = static_cast<T*>(BaseHeap::Allocate());
156 case AllocationType::Host:
157 // Fallback for cases where we do not yet support allocating guest memory from the slab
158 // heap, such as for kernel memory regions.
159 return new T;
160
161 case AllocationType::Guest:
162 T* obj = static_cast<T*>(AllocateImpl());
163 if (obj != nullptr) {
164 new (obj) T();
165 }
166 return obj;
167 }
168 182
169 UNREACHABLE_MSG("Invalid AllocationType {}", allocation_type); 183 if (obj != nullptr) [[likely]] {
170 return nullptr; 184 std::construct_at(obj);
185 }
186 return obj;
171 } 187 }
172 188
173 T* AllocateWithKernel(KernelCore& kernel) { 189 T* Allocate(KernelCore& kernel) {
174 switch (allocation_type) { 190 T* obj = static_cast<T*>(BaseHeap::Allocate());
175 case AllocationType::Host:
176 // Fallback for cases where we do not yet support allocating guest memory from the slab
177 // heap, such as for kernel memory regions.
178 return new T(kernel);
179 191
180 case AllocationType::Guest: 192 if (obj != nullptr) [[likely]] {
181 T* obj = static_cast<T*>(AllocateImpl()); 193 std::construct_at(obj, kernel);
182 if (obj != nullptr) {
183 new (obj) T(kernel);
184 }
185 return obj;
186 } 194 }
187 195 return obj;
188 UNREACHABLE_MSG("Invalid AllocationType {}", allocation_type);
189 return nullptr;
190 } 196 }
191 197
192 void Free(T* obj) { 198 void Free(T* obj) {
193 switch (allocation_type) { 199 BaseHeap::Free(obj);
194 case AllocationType::Host:
195 // Fallback for cases where we do not yet support allocating guest memory from the slab
196 // heap, such as for kernel memory regions.
197 delete obj;
198 return;
199
200 case AllocationType::Guest:
201 FreeImpl(obj);
202 return;
203 }
204
205 UNREACHABLE_MSG("Invalid AllocationType {}", allocation_type);
206 } 200 }
207 201
208 constexpr std::size_t GetObjectIndex(const T* obj) const { 202 size_t GetObjectIndex(const T* obj) const {
209 return GetObjectIndexImpl(obj); 203 return BaseHeap::GetObjectIndex(obj);
210 } 204 }
211
212private:
213 const AllocationType allocation_type;
214}; 205};
215 206
216} // namespace Kernel 207} // namespace Kernel
diff --git a/src/core/hle/kernel/k_thread.cpp b/src/core/hle/kernel/k_thread.cpp
index de3ffe0c7..ba7f72c6b 100644
--- a/src/core/hle/kernel/k_thread.cpp
+++ b/src/core/hle/kernel/k_thread.cpp
@@ -210,7 +210,7 @@ ResultCode KThread::Initialize(KThreadFunction func, uintptr_t arg, VAddr user_s
210 if (owner != nullptr) { 210 if (owner != nullptr) {
211 // Setup the TLS, if needed. 211 // Setup the TLS, if needed.
212 if (type == ThreadType::User) { 212 if (type == ThreadType::User) {
213 tls_address = owner->CreateTLSRegion(); 213 R_TRY(owner->CreateThreadLocalRegion(std::addressof(tls_address)));
214 } 214 }
215 215
216 parent = owner; 216 parent = owner;
@@ -305,7 +305,7 @@ void KThread::Finalize() {
305 305
306 // If the thread has a local region, delete it. 306 // If the thread has a local region, delete it.
307 if (tls_address != 0) { 307 if (tls_address != 0) {
308 parent->FreeTLSRegion(tls_address); 308 ASSERT(parent->DeleteThreadLocalRegion(tls_address).IsSuccess());
309 } 309 }
310 310
311 // Release any waiters. 311 // Release any waiters.
@@ -326,6 +326,9 @@ void KThread::Finalize() {
326 } 326 }
327 } 327 }
328 328
329 // Release host emulation members.
330 host_context.reset();
331
329 // Perform inherited finalization. 332 // Perform inherited finalization.
330 KSynchronizationObject::Finalize(); 333 KSynchronizationObject::Finalize();
331} 334}
diff --git a/src/core/hle/kernel/k_thread.h b/src/core/hle/kernel/k_thread.h
index d058db62c..f46db7298 100644
--- a/src/core/hle/kernel/k_thread.h
+++ b/src/core/hle/kernel/k_thread.h
@@ -656,7 +656,7 @@ private:
656 static_assert(sizeof(SyncObjectBuffer::sync_objects) == sizeof(SyncObjectBuffer::handles)); 656 static_assert(sizeof(SyncObjectBuffer::sync_objects) == sizeof(SyncObjectBuffer::handles));
657 657
658 struct ConditionVariableComparator { 658 struct ConditionVariableComparator {
659 struct LightCompareType { 659 struct RedBlackKeyType {
660 u64 cv_key{}; 660 u64 cv_key{};
661 s32 priority{}; 661 s32 priority{};
662 662
@@ -672,8 +672,8 @@ private:
672 template <typename T> 672 template <typename T>
673 requires( 673 requires(
674 std::same_as<T, KThread> || 674 std::same_as<T, KThread> ||
675 std::same_as<T, LightCompareType>) static constexpr int Compare(const T& lhs, 675 std::same_as<T, RedBlackKeyType>) static constexpr int Compare(const T& lhs,
676 const KThread& rhs) { 676 const KThread& rhs) {
677 const u64 l_key = lhs.GetConditionVariableKey(); 677 const u64 l_key = lhs.GetConditionVariableKey();
678 const u64 r_key = rhs.GetConditionVariableKey(); 678 const u64 r_key = rhs.GetConditionVariableKey();
679 679
diff --git a/src/core/hle/kernel/k_thread_local_page.cpp b/src/core/hle/kernel/k_thread_local_page.cpp
new file mode 100644
index 000000000..17b233fca
--- /dev/null
+++ b/src/core/hle/kernel/k_thread_local_page.cpp
@@ -0,0 +1,66 @@
1// Copyright 2022 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include "common/scope_exit.h"
6#include "core/core.h"
7#include "core/hle/kernel/k_memory_block.h"
8#include "core/hle/kernel/k_page_table.h"
9#include "core/hle/kernel/k_process.h"
10#include "core/hle/kernel/k_thread_local_page.h"
11#include "core/hle/kernel/kernel.h"
12
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 49c0714ed..f9828bc43 100644
--- a/src/core/hle/kernel/kernel.cpp
+++ b/src/core/hle/kernel/kernel.cpp
@@ -52,7 +52,7 @@ namespace Kernel {
52 52
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);
@@ -70,14 +71,13 @@ struct KernelCore::Impl {
70 71
71 // Derive the initial memory layout from the emulated board 72 // Derive the initial memory layout from the emulated board
72 Init::InitializeSlabResourceCounts(kernel); 73 Init::InitializeSlabResourceCounts(kernel);
73 KMemoryLayout memory_layout; 74 DeriveInitialMemoryLayout();
74 DeriveInitialMemoryLayout(memory_layout); 75 Init::InitializeSlabHeaps(system, *memory_layout);
75 Init::InitializeSlabHeaps(system, memory_layout);
76 76
77 // Initialize kernel memory and resources. 77 // Initialize kernel memory and resources.
78 InitializeSystemResourceLimit(kernel, system.CoreTiming(), memory_layout); 78 InitializeSystemResourceLimit(kernel, system.CoreTiming());
79 InitializeMemoryLayout(memory_layout); 79 InitializeMemoryLayout();
80 InitializePageSlab(); 80 Init::InitializeKPageBufferSlabHeap(system);
81 InitializeSchedulers(); 81 InitializeSchedulers();
82 InitializeSuspendThreads(); 82 InitializeSuspendThreads();
83 InitializePreemption(kernel); 83 InitializePreemption(kernel);
@@ -108,19 +108,6 @@ struct KernelCore::Impl {
108 for (auto* server_port : server_ports_) { 108 for (auto* server_port : server_ports_) {
109 server_port->Close(); 109 server_port->Close();
110 } 110 }
111 // Close all open server sessions.
112 std::unordered_set<KServerSession*> server_sessions_;
113 {
114 std::lock_guard lk(server_sessions_lock);
115 server_sessions_ = server_sessions;
116 server_sessions.clear();
117 }
118 for (auto* server_session : server_sessions_) {
119 server_session->Close();
120 }
121
122 // Ensure that the object list container is finalized and properly shutdown.
123 object_list_container.Finalize();
124 111
125 // Ensures all service threads gracefully shutdown. 112 // Ensures all service threads gracefully shutdown.
126 ClearServiceThreads(); 113 ClearServiceThreads();
@@ -195,11 +182,15 @@ struct KernelCore::Impl {
195 { 182 {
196 std::lock_guard lk(registered_objects_lock); 183 std::lock_guard lk(registered_objects_lock);
197 if (registered_objects.size()) { 184 if (registered_objects.size()) {
198 LOG_WARNING(Kernel, "{} kernel objects were dangling on shutdown!", 185 LOG_DEBUG(Kernel, "{} kernel objects were dangling on shutdown!",
199 registered_objects.size()); 186 registered_objects.size());
200 registered_objects.clear(); 187 registered_objects.clear();
201 } 188 }
202 } 189 }
190
191 // Ensure that the object list container is finalized and properly shutdown.
192 global_object_list_container->Finalize();
193 global_object_list_container.reset();
203 } 194 }
204 195
205 void InitializePhysicalCores() { 196 void InitializePhysicalCores() {
@@ -219,12 +210,11 @@ struct KernelCore::Impl {
219 210
220 // Creates the default system resource limit 211 // Creates the default system resource limit
221 void InitializeSystemResourceLimit(KernelCore& kernel, 212 void InitializeSystemResourceLimit(KernelCore& kernel,
222 const Core::Timing::CoreTiming& core_timing, 213 const Core::Timing::CoreTiming& core_timing) {
223 const KMemoryLayout& memory_layout) {
224 system_resource_limit = KResourceLimit::Create(system.Kernel()); 214 system_resource_limit = KResourceLimit::Create(system.Kernel());
225 system_resource_limit->Initialize(&core_timing); 215 system_resource_limit->Initialize(&core_timing);
226 216
227 const auto [total_size, kernel_size] = memory_layout.GetTotalAndKernelMemorySizes(); 217 const auto [total_size, kernel_size] = memory_layout->GetTotalAndKernelMemorySizes();
228 218
229 // If setting the default system values fails, then something seriously wrong has occurred. 219 // If setting the default system values fails, then something seriously wrong has occurred.
230 ASSERT(system_resource_limit->SetLimitValue(LimitableResource::PhysicalMemory, total_size) 220 ASSERT(system_resource_limit->SetLimitValue(LimitableResource::PhysicalMemory, total_size)
@@ -240,13 +230,6 @@ struct KernelCore::Impl {
240 constexpr u64 secure_applet_memory_size{4_MiB}; 230 constexpr u64 secure_applet_memory_size{4_MiB};
241 ASSERT(system_resource_limit->Reserve(LimitableResource::PhysicalMemory, 231 ASSERT(system_resource_limit->Reserve(LimitableResource::PhysicalMemory,
242 secure_applet_memory_size)); 232 secure_applet_memory_size));
243
244 // This memory seems to be reserved on hardware, but is not reserved/used by yuzu.
245 // Likely Horizon OS reserved memory
246 // TODO(ameerj): Derive the memory rather than hardcode it.
247 constexpr u64 unknown_reserved_memory{0x2f896000};
248 ASSERT(system_resource_limit->Reserve(LimitableResource::PhysicalMemory,
249 unknown_reserved_memory));
250 } 233 }
251 234
252 void InitializePreemption(KernelCore& kernel) { 235 void InitializePreemption(KernelCore& kernel) {
@@ -300,15 +283,16 @@ struct KernelCore::Impl {
300 283
301 // Gets the dummy KThread for the caller, allocating a new one if this is the first time 284 // Gets the dummy KThread for the caller, allocating a new one if this is the first time
302 KThread* GetHostDummyThread() { 285 KThread* GetHostDummyThread() {
303 auto make_thread = [this]() { 286 auto initialize = [this](KThread* thread) {
304 KThread* thread = KThread::Create(system.Kernel());
305 ASSERT(KThread::InitializeDummyThread(thread).IsSuccess()); 287 ASSERT(KThread::InitializeDummyThread(thread).IsSuccess());
306 thread->SetName(fmt::format("DummyThread:{}", GetHostThreadId())); 288 thread->SetName(fmt::format("DummyThread:{}", GetHostThreadId()));
307 return thread; 289 return thread;
308 }; 290 };
309 291
310 thread_local KThread* saved_thread = make_thread(); 292 thread_local auto raw_thread = KThread(system.Kernel());
311 return saved_thread; 293 thread_local auto thread = initialize(&raw_thread);
294
295 return thread;
312 } 296 }
313 297
314 /// Registers a CPU core thread by allocating a host thread ID for it 298 /// Registers a CPU core thread by allocating a host thread ID for it
@@ -360,16 +344,18 @@ struct KernelCore::Impl {
360 return schedulers[thread_id]->GetCurrentThread(); 344 return schedulers[thread_id]->GetCurrentThread();
361 } 345 }
362 346
363 void DeriveInitialMemoryLayout(KMemoryLayout& memory_layout) { 347 void DeriveInitialMemoryLayout() {
348 memory_layout = std::make_unique<KMemoryLayout>();
349
364 // Insert the root region for the virtual memory tree, from which all other regions will 350 // Insert the root region for the virtual memory tree, from which all other regions will
365 // derive. 351 // derive.
366 memory_layout.GetVirtualMemoryRegionTree().InsertDirectly( 352 memory_layout->GetVirtualMemoryRegionTree().InsertDirectly(
367 KernelVirtualAddressSpaceBase, 353 KernelVirtualAddressSpaceBase,
368 KernelVirtualAddressSpaceBase + KernelVirtualAddressSpaceSize - 1); 354 KernelVirtualAddressSpaceBase + KernelVirtualAddressSpaceSize - 1);
369 355
370 // Insert the root region for the physical memory tree, from which all other regions will 356 // Insert the root region for the physical memory tree, from which all other regions will
371 // derive. 357 // derive.
372 memory_layout.GetPhysicalMemoryRegionTree().InsertDirectly( 358 memory_layout->GetPhysicalMemoryRegionTree().InsertDirectly(
373 KernelPhysicalAddressSpaceBase, 359 KernelPhysicalAddressSpaceBase,
374 KernelPhysicalAddressSpaceBase + KernelPhysicalAddressSpaceSize - 1); 360 KernelPhysicalAddressSpaceBase + KernelPhysicalAddressSpaceSize - 1);
375 361
@@ -386,7 +372,7 @@ struct KernelCore::Impl {
386 if (!(kernel_region_start + KernelRegionSize - 1 <= KernelVirtualAddressSpaceLast)) { 372 if (!(kernel_region_start + KernelRegionSize - 1 <= KernelVirtualAddressSpaceLast)) {
387 kernel_region_size = KernelVirtualAddressSpaceEnd - kernel_region_start; 373 kernel_region_size = KernelVirtualAddressSpaceEnd - kernel_region_start;
388 } 374 }
389 ASSERT(memory_layout.GetVirtualMemoryRegionTree().Insert( 375 ASSERT(memory_layout->GetVirtualMemoryRegionTree().Insert(
390 kernel_region_start, kernel_region_size, KMemoryRegionType_Kernel)); 376 kernel_region_start, kernel_region_size, KMemoryRegionType_Kernel));
391 377
392 // Setup the code region. 378 // Setup the code region.
@@ -395,11 +381,11 @@ struct KernelCore::Impl {
395 Common::AlignDown(code_start_virt_addr, CodeRegionAlign); 381 Common::AlignDown(code_start_virt_addr, CodeRegionAlign);
396 constexpr VAddr code_region_end = Common::AlignUp(code_end_virt_addr, CodeRegionAlign); 382 constexpr VAddr code_region_end = Common::AlignUp(code_end_virt_addr, CodeRegionAlign);
397 constexpr size_t code_region_size = code_region_end - code_region_start; 383 constexpr size_t code_region_size = code_region_end - code_region_start;
398 ASSERT(memory_layout.GetVirtualMemoryRegionTree().Insert( 384 ASSERT(memory_layout->GetVirtualMemoryRegionTree().Insert(
399 code_region_start, code_region_size, KMemoryRegionType_KernelCode)); 385 code_region_start, code_region_size, KMemoryRegionType_KernelCode));
400 386
401 // Setup board-specific device physical regions. 387 // Setup board-specific device physical regions.
402 Init::SetupDevicePhysicalMemoryRegions(memory_layout); 388 Init::SetupDevicePhysicalMemoryRegions(*memory_layout);
403 389
404 // Determine the amount of space needed for the misc region. 390 // Determine the amount of space needed for the misc region.
405 size_t misc_region_needed_size; 391 size_t misc_region_needed_size;
@@ -408,7 +394,7 @@ struct KernelCore::Impl {
408 misc_region_needed_size = Core::Hardware::NUM_CPU_CORES * (3 * (PageSize + PageSize)); 394 misc_region_needed_size = Core::Hardware::NUM_CPU_CORES * (3 * (PageSize + PageSize));
409 395
410 // Account for each auto-map device. 396 // Account for each auto-map device.
411 for (const auto& region : memory_layout.GetPhysicalMemoryRegionTree()) { 397 for (const auto& region : memory_layout->GetPhysicalMemoryRegionTree()) {
412 if (region.HasTypeAttribute(KMemoryRegionAttr_ShouldKernelMap)) { 398 if (region.HasTypeAttribute(KMemoryRegionAttr_ShouldKernelMap)) {
413 // Check that the region is valid. 399 // Check that the region is valid.
414 ASSERT(region.GetEndAddress() != 0); 400 ASSERT(region.GetEndAddress() != 0);
@@ -433,22 +419,22 @@ struct KernelCore::Impl {
433 419
434 // Setup the misc region. 420 // Setup the misc region.
435 const VAddr misc_region_start = 421 const VAddr misc_region_start =
436 memory_layout.GetVirtualMemoryRegionTree().GetRandomAlignedRegion( 422 memory_layout->GetVirtualMemoryRegionTree().GetRandomAlignedRegion(
437 misc_region_size, MiscRegionAlign, KMemoryRegionType_Kernel); 423 misc_region_size, MiscRegionAlign, KMemoryRegionType_Kernel);
438 ASSERT(memory_layout.GetVirtualMemoryRegionTree().Insert( 424 ASSERT(memory_layout->GetVirtualMemoryRegionTree().Insert(
439 misc_region_start, misc_region_size, KMemoryRegionType_KernelMisc)); 425 misc_region_start, misc_region_size, KMemoryRegionType_KernelMisc));
440 426
441 // Setup the stack region. 427 // Setup the stack region.
442 constexpr size_t StackRegionSize = 14_MiB; 428 constexpr size_t StackRegionSize = 14_MiB;
443 constexpr size_t StackRegionAlign = KernelAslrAlignment; 429 constexpr size_t StackRegionAlign = KernelAslrAlignment;
444 const VAddr stack_region_start = 430 const VAddr stack_region_start =
445 memory_layout.GetVirtualMemoryRegionTree().GetRandomAlignedRegion( 431 memory_layout->GetVirtualMemoryRegionTree().GetRandomAlignedRegion(
446 StackRegionSize, StackRegionAlign, KMemoryRegionType_Kernel); 432 StackRegionSize, StackRegionAlign, KMemoryRegionType_Kernel);
447 ASSERT(memory_layout.GetVirtualMemoryRegionTree().Insert( 433 ASSERT(memory_layout->GetVirtualMemoryRegionTree().Insert(
448 stack_region_start, StackRegionSize, KMemoryRegionType_KernelStack)); 434 stack_region_start, StackRegionSize, KMemoryRegionType_KernelStack));
449 435
450 // Determine the size of the resource region. 436 // Determine the size of the resource region.
451 const size_t resource_region_size = memory_layout.GetResourceRegionSizeForInit(); 437 const size_t resource_region_size = memory_layout->GetResourceRegionSizeForInit();
452 438
453 // Determine the size of the slab region. 439 // Determine the size of the slab region.
454 const size_t slab_region_size = 440 const size_t slab_region_size =
@@ -465,23 +451,23 @@ struct KernelCore::Impl {
465 Common::AlignUp(code_end_phys_addr + slab_region_size, SlabRegionAlign) - 451 Common::AlignUp(code_end_phys_addr + slab_region_size, SlabRegionAlign) -
466 Common::AlignDown(code_end_phys_addr, SlabRegionAlign); 452 Common::AlignDown(code_end_phys_addr, SlabRegionAlign);
467 const VAddr slab_region_start = 453 const VAddr slab_region_start =
468 memory_layout.GetVirtualMemoryRegionTree().GetRandomAlignedRegion( 454 memory_layout->GetVirtualMemoryRegionTree().GetRandomAlignedRegion(
469 slab_region_needed_size, SlabRegionAlign, KMemoryRegionType_Kernel) + 455 slab_region_needed_size, SlabRegionAlign, KMemoryRegionType_Kernel) +
470 (code_end_phys_addr % SlabRegionAlign); 456 (code_end_phys_addr % SlabRegionAlign);
471 ASSERT(memory_layout.GetVirtualMemoryRegionTree().Insert( 457 ASSERT(memory_layout->GetVirtualMemoryRegionTree().Insert(
472 slab_region_start, slab_region_size, KMemoryRegionType_KernelSlab)); 458 slab_region_start, slab_region_size, KMemoryRegionType_KernelSlab));
473 459
474 // Setup the temp region. 460 // Setup the temp region.
475 constexpr size_t TempRegionSize = 128_MiB; 461 constexpr size_t TempRegionSize = 128_MiB;
476 constexpr size_t TempRegionAlign = KernelAslrAlignment; 462 constexpr size_t TempRegionAlign = KernelAslrAlignment;
477 const VAddr temp_region_start = 463 const VAddr temp_region_start =
478 memory_layout.GetVirtualMemoryRegionTree().GetRandomAlignedRegion( 464 memory_layout->GetVirtualMemoryRegionTree().GetRandomAlignedRegion(
479 TempRegionSize, TempRegionAlign, KMemoryRegionType_Kernel); 465 TempRegionSize, TempRegionAlign, KMemoryRegionType_Kernel);
480 ASSERT(memory_layout.GetVirtualMemoryRegionTree().Insert(temp_region_start, TempRegionSize, 466 ASSERT(memory_layout->GetVirtualMemoryRegionTree().Insert(temp_region_start, TempRegionSize,
481 KMemoryRegionType_KernelTemp)); 467 KMemoryRegionType_KernelTemp));
482 468
483 // Automatically map in devices that have auto-map attributes. 469 // Automatically map in devices that have auto-map attributes.
484 for (auto& region : memory_layout.GetPhysicalMemoryRegionTree()) { 470 for (auto& region : memory_layout->GetPhysicalMemoryRegionTree()) {
485 // We only care about kernel regions. 471 // We only care about kernel regions.
486 if (!region.IsDerivedFrom(KMemoryRegionType_Kernel)) { 472 if (!region.IsDerivedFrom(KMemoryRegionType_Kernel)) {
487 continue; 473 continue;
@@ -508,21 +494,21 @@ struct KernelCore::Impl {
508 const size_t map_size = 494 const size_t map_size =
509 Common::AlignUp(region.GetEndAddress(), PageSize) - map_phys_addr; 495 Common::AlignUp(region.GetEndAddress(), PageSize) - map_phys_addr;
510 const VAddr map_virt_addr = 496 const VAddr map_virt_addr =
511 memory_layout.GetVirtualMemoryRegionTree().GetRandomAlignedRegionWithGuard( 497 memory_layout->GetVirtualMemoryRegionTree().GetRandomAlignedRegionWithGuard(
512 map_size, PageSize, KMemoryRegionType_KernelMisc, PageSize); 498 map_size, PageSize, KMemoryRegionType_KernelMisc, PageSize);
513 ASSERT(memory_layout.GetVirtualMemoryRegionTree().Insert( 499 ASSERT(memory_layout->GetVirtualMemoryRegionTree().Insert(
514 map_virt_addr, map_size, KMemoryRegionType_KernelMiscMappedDevice)); 500 map_virt_addr, map_size, KMemoryRegionType_KernelMiscMappedDevice));
515 region.SetPairAddress(map_virt_addr + region.GetAddress() - map_phys_addr); 501 region.SetPairAddress(map_virt_addr + region.GetAddress() - map_phys_addr);
516 } 502 }
517 503
518 Init::SetupDramPhysicalMemoryRegions(memory_layout); 504 Init::SetupDramPhysicalMemoryRegions(*memory_layout);
519 505
520 // Insert a physical region for the kernel code region. 506 // Insert a physical region for the kernel code region.
521 ASSERT(memory_layout.GetPhysicalMemoryRegionTree().Insert( 507 ASSERT(memory_layout->GetPhysicalMemoryRegionTree().Insert(
522 code_start_phys_addr, code_region_size, KMemoryRegionType_DramKernelCode)); 508 code_start_phys_addr, code_region_size, KMemoryRegionType_DramKernelCode));
523 509
524 // Insert a physical region for the kernel slab region. 510 // Insert a physical region for the kernel slab region.
525 ASSERT(memory_layout.GetPhysicalMemoryRegionTree().Insert( 511 ASSERT(memory_layout->GetPhysicalMemoryRegionTree().Insert(
526 slab_start_phys_addr, slab_region_size, KMemoryRegionType_DramKernelSlab)); 512 slab_start_phys_addr, slab_region_size, KMemoryRegionType_DramKernelSlab));
527 513
528 // Determine size available for kernel page table heaps, requiring > 8 MB. 514 // Determine size available for kernel page table heaps, requiring > 8 MB.
@@ -531,12 +517,12 @@ struct KernelCore::Impl {
531 ASSERT(page_table_heap_size / 4_MiB > 2); 517 ASSERT(page_table_heap_size / 4_MiB > 2);
532 518
533 // Insert a physical region for the kernel page table heap region 519 // Insert a physical region for the kernel page table heap region
534 ASSERT(memory_layout.GetPhysicalMemoryRegionTree().Insert( 520 ASSERT(memory_layout->GetPhysicalMemoryRegionTree().Insert(
535 slab_end_phys_addr, page_table_heap_size, KMemoryRegionType_DramKernelPtHeap)); 521 slab_end_phys_addr, page_table_heap_size, KMemoryRegionType_DramKernelPtHeap));
536 522
537 // All DRAM regions that we haven't tagged by this point will be mapped under the linear 523 // All DRAM regions that we haven't tagged by this point will be mapped under the linear
538 // mapping. Tag them. 524 // mapping. Tag them.
539 for (auto& region : memory_layout.GetPhysicalMemoryRegionTree()) { 525 for (auto& region : memory_layout->GetPhysicalMemoryRegionTree()) {
540 if (region.GetType() == KMemoryRegionType_Dram) { 526 if (region.GetType() == KMemoryRegionType_Dram) {
541 // Check that the region is valid. 527 // Check that the region is valid.
542 ASSERT(region.GetEndAddress() != 0); 528 ASSERT(region.GetEndAddress() != 0);
@@ -548,7 +534,7 @@ struct KernelCore::Impl {
548 534
549 // Get the linear region extents. 535 // Get the linear region extents.
550 const auto linear_extents = 536 const auto linear_extents =
551 memory_layout.GetPhysicalMemoryRegionTree().GetDerivedRegionExtents( 537 memory_layout->GetPhysicalMemoryRegionTree().GetDerivedRegionExtents(
552 KMemoryRegionAttr_LinearMapped); 538 KMemoryRegionAttr_LinearMapped);
553 ASSERT(linear_extents.GetEndAddress() != 0); 539 ASSERT(linear_extents.GetEndAddress() != 0);
554 540
@@ -560,7 +546,7 @@ struct KernelCore::Impl {
560 Common::AlignUp(linear_extents.GetEndAddress(), LinearRegionAlign) - 546 Common::AlignUp(linear_extents.GetEndAddress(), LinearRegionAlign) -
561 aligned_linear_phys_start; 547 aligned_linear_phys_start;
562 const VAddr linear_region_start = 548 const VAddr linear_region_start =
563 memory_layout.GetVirtualMemoryRegionTree().GetRandomAlignedRegionWithGuard( 549 memory_layout->GetVirtualMemoryRegionTree().GetRandomAlignedRegionWithGuard(
564 linear_region_size, LinearRegionAlign, KMemoryRegionType_None, LinearRegionAlign); 550 linear_region_size, LinearRegionAlign, KMemoryRegionType_None, LinearRegionAlign);
565 551
566 const u64 linear_region_phys_to_virt_diff = linear_region_start - aligned_linear_phys_start; 552 const u64 linear_region_phys_to_virt_diff = linear_region_start - aligned_linear_phys_start;
@@ -569,7 +555,7 @@ struct KernelCore::Impl {
569 { 555 {
570 PAddr cur_phys_addr = 0; 556 PAddr cur_phys_addr = 0;
571 u64 cur_size = 0; 557 u64 cur_size = 0;
572 for (auto& region : memory_layout.GetPhysicalMemoryRegionTree()) { 558 for (auto& region : memory_layout->GetPhysicalMemoryRegionTree()) {
573 if (!region.HasTypeAttribute(KMemoryRegionAttr_LinearMapped)) { 559 if (!region.HasTypeAttribute(KMemoryRegionAttr_LinearMapped)) {
574 continue; 560 continue;
575 } 561 }
@@ -588,55 +574,49 @@ struct KernelCore::Impl {
588 574
589 const VAddr region_virt_addr = 575 const VAddr region_virt_addr =
590 region.GetAddress() + linear_region_phys_to_virt_diff; 576 region.GetAddress() + linear_region_phys_to_virt_diff;
591 ASSERT(memory_layout.GetVirtualMemoryRegionTree().Insert( 577 ASSERT(memory_layout->GetVirtualMemoryRegionTree().Insert(
592 region_virt_addr, region.GetSize(), 578 region_virt_addr, region.GetSize(),
593 GetTypeForVirtualLinearMapping(region.GetType()))); 579 GetTypeForVirtualLinearMapping(region.GetType())));
594 region.SetPairAddress(region_virt_addr); 580 region.SetPairAddress(region_virt_addr);
595 581
596 KMemoryRegion* virt_region = 582 KMemoryRegion* virt_region =
597 memory_layout.GetVirtualMemoryRegionTree().FindModifiable(region_virt_addr); 583 memory_layout->GetVirtualMemoryRegionTree().FindModifiable(region_virt_addr);
598 ASSERT(virt_region != nullptr); 584 ASSERT(virt_region != nullptr);
599 virt_region->SetPairAddress(region.GetAddress()); 585 virt_region->SetPairAddress(region.GetAddress());
600 } 586 }
601 } 587 }
602 588
603 // Insert regions for the initial page table region. 589 // Insert regions for the initial page table region.
604 ASSERT(memory_layout.GetPhysicalMemoryRegionTree().Insert( 590 ASSERT(memory_layout->GetPhysicalMemoryRegionTree().Insert(
605 resource_end_phys_addr, KernelPageTableHeapSize, KMemoryRegionType_DramKernelInitPt)); 591 resource_end_phys_addr, KernelPageTableHeapSize, KMemoryRegionType_DramKernelInitPt));
606 ASSERT(memory_layout.GetVirtualMemoryRegionTree().Insert( 592 ASSERT(memory_layout->GetVirtualMemoryRegionTree().Insert(
607 resource_end_phys_addr + linear_region_phys_to_virt_diff, KernelPageTableHeapSize, 593 resource_end_phys_addr + linear_region_phys_to_virt_diff, KernelPageTableHeapSize,
608 KMemoryRegionType_VirtualDramKernelInitPt)); 594 KMemoryRegionType_VirtualDramKernelInitPt));
609 595
610 // All linear-mapped DRAM regions that we haven't tagged by this point will be allocated to 596 // All linear-mapped DRAM regions that we haven't tagged by this point will be allocated to
611 // some pool partition. Tag them. 597 // some pool partition. Tag them.
612 for (auto& region : memory_layout.GetPhysicalMemoryRegionTree()) { 598 for (auto& region : memory_layout->GetPhysicalMemoryRegionTree()) {
613 if (region.GetType() == (KMemoryRegionType_Dram | KMemoryRegionAttr_LinearMapped)) { 599 if (region.GetType() == (KMemoryRegionType_Dram | KMemoryRegionAttr_LinearMapped)) {
614 region.SetType(KMemoryRegionType_DramPoolPartition); 600 region.SetType(KMemoryRegionType_DramPoolPartition);
615 } 601 }
616 } 602 }
617 603
618 // Setup all other memory regions needed to arrange the pool partitions. 604 // Setup all other memory regions needed to arrange the pool partitions.
619 Init::SetupPoolPartitionMemoryRegions(memory_layout); 605 Init::SetupPoolPartitionMemoryRegions(*memory_layout);
620 606
621 // Cache all linear regions in their own trees for faster access, later. 607 // Cache all linear regions in their own trees for faster access, later.
622 memory_layout.InitializeLinearMemoryRegionTrees(aligned_linear_phys_start, 608 memory_layout->InitializeLinearMemoryRegionTrees(aligned_linear_phys_start,
623 linear_region_start); 609 linear_region_start);
624 } 610 }
625 611
626 void InitializeMemoryLayout(const KMemoryLayout& memory_layout) { 612 void InitializeMemoryLayout() {
627 const auto system_pool = memory_layout.GetKernelSystemPoolRegionPhysicalExtents(); 613 const auto system_pool = memory_layout->GetKernelSystemPoolRegionPhysicalExtents();
628 const auto applet_pool = memory_layout.GetKernelAppletPoolRegionPhysicalExtents();
629 const auto application_pool = memory_layout.GetKernelApplicationPoolRegionPhysicalExtents();
630 614
631 // Initialize memory managers 615 // Initialize the memory manager.
632 memory_manager = std::make_unique<KMemoryManager>(system); 616 memory_manager = std::make_unique<KMemoryManager>(system);
633 memory_manager->InitializeManager(KMemoryManager::Pool::Application, 617 const auto& management_region = memory_layout->GetPoolManagementRegion();
634 application_pool.GetAddress(), 618 ASSERT(management_region.GetEndAddress() != 0);
635 application_pool.GetEndAddress()); 619 memory_manager->Initialize(management_region.GetAddress(), management_region.GetSize());
636 memory_manager->InitializeManager(KMemoryManager::Pool::Applet, applet_pool.GetAddress(),
637 applet_pool.GetEndAddress());
638 memory_manager->InitializeManager(KMemoryManager::Pool::System, system_pool.GetAddress(),
639 system_pool.GetEndAddress());
640 620
641 // Setup memory regions for emulated processes 621 // Setup memory regions for emulated processes
642 // TODO(bunnei): These should not be hardcoded regions initialized within the kernel 622 // TODO(bunnei): These should not be hardcoded regions initialized within the kernel
@@ -673,22 +653,6 @@ struct KernelCore::Impl {
673 time_phys_addr, time_size, "Time:SharedMemory"); 653 time_phys_addr, time_size, "Time:SharedMemory");
674 } 654 }
675 655
676 void InitializePageSlab() {
677 // Allocate slab heaps
678 user_slab_heap_pages =
679 std::make_unique<KSlabHeap<Page>>(KSlabHeap<Page>::AllocationType::Guest);
680
681 // TODO(ameerj): This should be derived, not hardcoded within the kernel
682 constexpr u64 user_slab_heap_size{0x3de000};
683 // Reserve slab heaps
684 ASSERT(
685 system_resource_limit->Reserve(LimitableResource::PhysicalMemory, user_slab_heap_size));
686 // Initialize slab heap
687 user_slab_heap_pages->Initialize(
688 system.DeviceMemory().GetPointer(Core::DramMemoryMap::SlabHeapBase),
689 user_slab_heap_size);
690 }
691
692 KClientPort* CreateNamedServicePort(std::string name) { 656 KClientPort* CreateNamedServicePort(std::string name) {
693 auto search = service_interface_factory.find(name); 657 auto search = service_interface_factory.find(name);
694 if (search == service_interface_factory.end()) { 658 if (search == service_interface_factory.end()) {
@@ -726,7 +690,6 @@ struct KernelCore::Impl {
726 } 690 }
727 691
728 std::mutex server_ports_lock; 692 std::mutex server_ports_lock;
729 std::mutex server_sessions_lock;
730 std::mutex registered_objects_lock; 693 std::mutex registered_objects_lock;
731 std::mutex registered_in_use_objects_lock; 694 std::mutex registered_in_use_objects_lock;
732 695
@@ -750,14 +713,13 @@ struct KernelCore::Impl {
750 // stores all the objects in place. 713 // stores all the objects in place.
751 std::unique_ptr<KHandleTable> global_handle_table; 714 std::unique_ptr<KHandleTable> global_handle_table;
752 715
753 KAutoObjectWithListContainer object_list_container; 716 std::unique_ptr<KAutoObjectWithListContainer> global_object_list_container;
754 717
755 /// Map of named ports managed by the kernel, which can be retrieved using 718 /// Map of named ports managed by the kernel, which can be retrieved using
756 /// the ConnectToPort SVC. 719 /// the ConnectToPort SVC.
757 std::unordered_map<std::string, ServiceInterfaceFactory> service_interface_factory; 720 std::unordered_map<std::string, ServiceInterfaceFactory> service_interface_factory;
758 NamedPortTable named_ports; 721 NamedPortTable named_ports;
759 std::unordered_set<KServerPort*> server_ports; 722 std::unordered_set<KServerPort*> server_ports;
760 std::unordered_set<KServerSession*> server_sessions;
761 std::unordered_set<KAutoObject*> registered_objects; 723 std::unordered_set<KAutoObject*> registered_objects;
762 std::unordered_set<KAutoObject*> registered_in_use_objects; 724 std::unordered_set<KAutoObject*> registered_in_use_objects;
763 725
@@ -769,7 +731,6 @@ struct KernelCore::Impl {
769 731
770 // Kernel memory management 732 // Kernel memory management
771 std::unique_ptr<KMemoryManager> memory_manager; 733 std::unique_ptr<KMemoryManager> memory_manager;
772 std::unique_ptr<KSlabHeap<Page>> user_slab_heap_pages;
773 734
774 // Shared memory for services 735 // Shared memory for services
775 Kernel::KSharedMemory* hid_shared_mem{}; 736 Kernel::KSharedMemory* hid_shared_mem{};
@@ -777,6 +738,9 @@ struct KernelCore::Impl {
777 Kernel::KSharedMemory* irs_shared_mem{}; 738 Kernel::KSharedMemory* irs_shared_mem{};
778 Kernel::KSharedMemory* time_shared_mem{}; 739 Kernel::KSharedMemory* time_shared_mem{};
779 740
741 // Memory layout
742 std::unique_ptr<KMemoryLayout> memory_layout;
743
780 // Threads used for services 744 // Threads used for services
781 std::unordered_set<std::shared_ptr<Kernel::ServiceThread>> service_threads; 745 std::unordered_set<std::shared_ptr<Kernel::ServiceThread>> service_threads;
782 Common::ThreadWorker service_threads_manager; 746 Common::ThreadWorker service_threads_manager;
@@ -925,11 +889,11 @@ const Core::ExclusiveMonitor& KernelCore::GetExclusiveMonitor() const {
925} 889}
926 890
927KAutoObjectWithListContainer& KernelCore::ObjectListContainer() { 891KAutoObjectWithListContainer& KernelCore::ObjectListContainer() {
928 return impl->object_list_container; 892 return *impl->global_object_list_container;
929} 893}
930 894
931const KAutoObjectWithListContainer& KernelCore::ObjectListContainer() const { 895const KAutoObjectWithListContainer& KernelCore::ObjectListContainer() const {
932 return impl->object_list_container; 896 return *impl->global_object_list_container;
933} 897}
934 898
935void KernelCore::InvalidateAllInstructionCaches() { 899void KernelCore::InvalidateAllInstructionCaches() {
@@ -959,16 +923,6 @@ KClientPort* KernelCore::CreateNamedServicePort(std::string name) {
959 return impl->CreateNamedServicePort(std::move(name)); 923 return impl->CreateNamedServicePort(std::move(name));
960} 924}
961 925
962void KernelCore::RegisterServerSession(KServerSession* server_session) {
963 std::lock_guard lk(impl->server_sessions_lock);
964 impl->server_sessions.insert(server_session);
965}
966
967void KernelCore::UnregisterServerSession(KServerSession* server_session) {
968 std::lock_guard lk(impl->server_sessions_lock);
969 impl->server_sessions.erase(server_session);
970}
971
972void KernelCore::RegisterKernelObject(KAutoObject* object) { 926void KernelCore::RegisterKernelObject(KAutoObject* object) {
973 std::lock_guard lk(impl->registered_objects_lock); 927 std::lock_guard lk(impl->registered_objects_lock);
974 impl->registered_objects.insert(object); 928 impl->registered_objects.insert(object);
@@ -1041,14 +995,6 @@ const KMemoryManager& KernelCore::MemoryManager() const {
1041 return *impl->memory_manager; 995 return *impl->memory_manager;
1042} 996}
1043 997
1044KSlabHeap<Page>& KernelCore::GetUserSlabHeapPages() {
1045 return *impl->user_slab_heap_pages;
1046}
1047
1048const KSlabHeap<Page>& KernelCore::GetUserSlabHeapPages() const {
1049 return *impl->user_slab_heap_pages;
1050}
1051
1052Kernel::KSharedMemory& KernelCore::GetHidSharedMem() { 998Kernel::KSharedMemory& KernelCore::GetHidSharedMem() {
1053 return *impl->hid_shared_mem; 999 return *impl->hid_shared_mem;
1054} 1000}
@@ -1142,6 +1088,10 @@ const KWorkerTaskManager& KernelCore::WorkerTaskManager() const {
1142 return impl->worker_task_manager; 1088 return impl->worker_task_manager;
1143} 1089}
1144 1090
1091const KMemoryLayout& KernelCore::MemoryLayout() const {
1092 return *impl->memory_layout;
1093}
1094
1145bool KernelCore::IsPhantomModeForSingleCore() const { 1095bool KernelCore::IsPhantomModeForSingleCore() const {
1146 return impl->IsPhantomModeForSingleCore(); 1096 return impl->IsPhantomModeForSingleCore();
1147} 1097}
diff --git a/src/core/hle/kernel/kernel.h b/src/core/hle/kernel/kernel.h
index 0e04fc3bb..7087bbda6 100644
--- a/src/core/hle/kernel/kernel.h
+++ b/src/core/hle/kernel/kernel.h
@@ -41,7 +41,9 @@ class KClientSession;
41class KEvent; 41class KEvent;
42class KHandleTable; 42class KHandleTable;
43class KLinkedListNode; 43class KLinkedListNode;
44class KMemoryLayout;
44class KMemoryManager; 45class KMemoryManager;
46class KPageBuffer;
45class KPort; 47class KPort;
46class KProcess; 48class KProcess;
47class KResourceLimit; 49class KResourceLimit;
@@ -51,6 +53,7 @@ class KSession;
51class KSharedMemory; 53class KSharedMemory;
52class KSharedMemoryInfo; 54class KSharedMemoryInfo;
53class KThread; 55class KThread;
56class KThreadLocalPage;
54class KTransferMemory; 57class KTransferMemory;
55class KWorkerTaskManager; 58class KWorkerTaskManager;
56class KWritableEvent; 59class KWritableEvent;
@@ -193,14 +196,6 @@ public:
193 /// Opens a port to a service previously registered with RegisterNamedService. 196 /// Opens a port to a service previously registered with RegisterNamedService.
194 KClientPort* CreateNamedServicePort(std::string name); 197 KClientPort* CreateNamedServicePort(std::string name);
195 198
196 /// Registers a server session with the gobal emulation state, to be freed on shutdown. This is
197 /// necessary because we do not emulate processes for HLE sessions.
198 void RegisterServerSession(KServerSession* server_session);
199
200 /// Unregisters a server session previously registered with RegisterServerSession when it was
201 /// destroyed during the current emulation session.
202 void UnregisterServerSession(KServerSession* server_session);
203
204 /// Registers all kernel objects with the global emulation state, this is purely for tracking 199 /// Registers all kernel objects with the global emulation state, this is purely for tracking
205 /// leaks after emulation has been shutdown. 200 /// leaks after emulation has been shutdown.
206 void RegisterKernelObject(KAutoObject* object); 201 void RegisterKernelObject(KAutoObject* object);
@@ -238,12 +233,6 @@ public:
238 /// Gets the virtual memory manager for the kernel. 233 /// Gets the virtual memory manager for the kernel.
239 const KMemoryManager& MemoryManager() const; 234 const KMemoryManager& MemoryManager() const;
240 235
241 /// Gets the slab heap allocated for user space pages.
242 KSlabHeap<Page>& GetUserSlabHeapPages();
243
244 /// Gets the slab heap allocated for user space pages.
245 const KSlabHeap<Page>& GetUserSlabHeapPages() const;
246
247 /// Gets the shared memory object for HID services. 236 /// Gets the shared memory object for HID services.
248 Kernel::KSharedMemory& GetHidSharedMem(); 237 Kernel::KSharedMemory& GetHidSharedMem();
249 238
@@ -335,6 +324,10 @@ public:
335 return slab_heap_container->writeable_event; 324 return slab_heap_container->writeable_event;
336 } else if constexpr (std::is_same_v<T, KCodeMemory>) { 325 } else if constexpr (std::is_same_v<T, KCodeMemory>) {
337 return slab_heap_container->code_memory; 326 return slab_heap_container->code_memory;
327 } else if constexpr (std::is_same_v<T, KPageBuffer>) {
328 return slab_heap_container->page_buffer;
329 } else if constexpr (std::is_same_v<T, KThreadLocalPage>) {
330 return slab_heap_container->thread_local_page;
338 } 331 }
339 } 332 }
340 333
@@ -350,6 +343,9 @@ public:
350 /// Gets the current worker task manager, used for dispatching KThread/KProcess tasks. 343 /// Gets the current worker task manager, used for dispatching KThread/KProcess tasks.
351 const KWorkerTaskManager& WorkerTaskManager() const; 344 const KWorkerTaskManager& WorkerTaskManager() const;
352 345
346 /// Gets the memory layout.
347 const KMemoryLayout& MemoryLayout() const;
348
353private: 349private:
354 friend class KProcess; 350 friend class KProcess;
355 friend class KThread; 351 friend class KThread;
@@ -393,6 +389,8 @@ private:
393 KSlabHeap<KTransferMemory> transfer_memory; 389 KSlabHeap<KTransferMemory> transfer_memory;
394 KSlabHeap<KWritableEvent> writeable_event; 390 KSlabHeap<KWritableEvent> writeable_event;
395 KSlabHeap<KCodeMemory> code_memory; 391 KSlabHeap<KCodeMemory> code_memory;
392 KSlabHeap<KPageBuffer> page_buffer;
393 KSlabHeap<KThreadLocalPage> thread_local_page;
396 }; 394 };
397 395
398 std::unique_ptr<SlabHeapContainer> slab_heap_container; 396 std::unique_ptr<SlabHeapContainer> slab_heap_container;
diff --git a/src/core/hle/kernel/service_thread.cpp b/src/core/hle/kernel/service_thread.cpp
index 4eb3a5988..52d25b837 100644
--- a/src/core/hle/kernel/service_thread.cpp
+++ b/src/core/hle/kernel/service_thread.cpp
@@ -49,12 +49,9 @@ ServiceThread::Impl::Impl(KernelCore& kernel, std::size_t num_threads, const std
49 return; 49 return;
50 } 50 }
51 51
52 // Allocate a dummy guest thread for this host thread.
52 kernel.RegisterHostThread(); 53 kernel.RegisterHostThread();
53 54
54 // Ensure the dummy thread allocated for this host thread is closed on exit.
55 auto* dummy_thread = kernel.GetCurrentEmuThread();
56 SCOPE_EXIT({ dummy_thread->Close(); });
57
58 while (true) { 55 while (true) {
59 std::function<void()> task; 56 std::function<void()> task;
60 57
diff --git a/src/core/hle/kernel/slab_helpers.h b/src/core/hle/kernel/slab_helpers.h
index f1c11256e..dc1e48fc9 100644
--- a/src/core/hle/kernel/slab_helpers.h
+++ b/src/core/hle/kernel/slab_helpers.h
@@ -59,7 +59,7 @@ class KAutoObjectWithSlabHeapAndContainer : public Base {
59 59
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.cpp b/src/core/hle/kernel/svc.cpp
index 4f7aebf3f..839171e85 100644
--- a/src/core/hle/kernel/svc.cpp
+++ b/src/core/hle/kernel/svc.cpp
@@ -396,7 +396,7 @@ static ResultCode GetProcessId(Core::System& system, u64* out_process_id, Handle
396 // Get the process id. 396 // Get the process id.
397 *out_process_id = process->GetId(); 397 *out_process_id = process->GetId();
398 398
399 return ResultInvalidHandle; 399 return ResultSuccess;
400} 400}
401 401
402static ResultCode GetProcessId32(Core::System& system, u32* out_process_id_low, 402static ResultCode GetProcessId32(Core::System& system, u32* out_process_id_low,
@@ -645,6 +645,10 @@ static void OutputDebugString(Core::System& system, VAddr address, u64 len) {
645 LOG_DEBUG(Debug_Emulated, "{}", str); 645 LOG_DEBUG(Debug_Emulated, "{}", str);
646} 646}
647 647
648static void OutputDebugString32(Core::System& system, u32 address, u32 len) {
649 OutputDebugString(system, address, len);
650}
651
648/// Gets system/memory information for the current process 652/// Gets system/memory information for the current process
649static ResultCode GetInfo(Core::System& system, u64* result, u64 info_id, Handle handle, 653static ResultCode GetInfo(Core::System& system, u64* result, u64 info_id, Handle handle,
650 u64 info_sub_id) { 654 u64 info_sub_id) {
@@ -1404,7 +1408,7 @@ static ResultCode UnmapProcessMemory(Core::System& system, VAddr dst_address, Ha
1404} 1408}
1405 1409
1406static ResultCode CreateCodeMemory(Core::System& system, Handle* out, VAddr address, size_t size) { 1410static ResultCode CreateCodeMemory(Core::System& system, Handle* out, VAddr address, size_t size) {
1407 LOG_TRACE(Kernel_SVC, "called, handle_out=0x{:X}, address=0x{:X}, size=0x{:X}", 1411 LOG_TRACE(Kernel_SVC, "called, handle_out={}, address=0x{:X}, size=0x{:X}",
1408 static_cast<void*>(out), address, size); 1412 static_cast<void*>(out), address, size);
1409 // Get kernel instance. 1413 // Get kernel instance.
1410 auto& kernel = system.Kernel(); 1414 auto& kernel = system.Kernel();
@@ -1438,6 +1442,10 @@ static ResultCode CreateCodeMemory(Core::System& system, Handle* out, VAddr addr
1438 return ResultSuccess; 1442 return ResultSuccess;
1439} 1443}
1440 1444
1445static ResultCode CreateCodeMemory32(Core::System& system, Handle* out, u32 address, u32 size) {
1446 return CreateCodeMemory(system, out, address, size);
1447}
1448
1441static ResultCode ControlCodeMemory(Core::System& system, Handle code_memory_handle, u32 operation, 1449static ResultCode ControlCodeMemory(Core::System& system, Handle code_memory_handle, u32 operation,
1442 VAddr address, size_t size, Svc::MemoryPermission perm) { 1450 VAddr address, size_t size, Svc::MemoryPermission perm) {
1443 1451
@@ -1517,6 +1525,12 @@ static ResultCode ControlCodeMemory(Core::System& system, Handle code_memory_han
1517 return ResultSuccess; 1525 return ResultSuccess;
1518} 1526}
1519 1527
1528static ResultCode ControlCodeMemory32(Core::System& system, Handle code_memory_handle,
1529 u32 operation, u64 address, u64 size,
1530 Svc::MemoryPermission perm) {
1531 return ControlCodeMemory(system, code_memory_handle, operation, address, size, perm);
1532}
1533
1520static ResultCode QueryProcessMemory(Core::System& system, VAddr memory_info_address, 1534static ResultCode QueryProcessMemory(Core::System& system, VAddr memory_info_address,
1521 VAddr page_info_address, Handle process_handle, 1535 VAddr page_info_address, Handle process_handle,
1522 VAddr address) { 1536 VAddr address) {
@@ -2318,7 +2332,7 @@ static ResultCode CreateEvent(Core::System& system, Handle* out_write, Handle* o
2318 R_UNLESS(event != nullptr, ResultOutOfResource); 2332 R_UNLESS(event != nullptr, ResultOutOfResource);
2319 2333
2320 // Initialize the event. 2334 // Initialize the event.
2321 event->Initialize("CreateEvent"); 2335 event->Initialize("CreateEvent", kernel.CurrentProcess());
2322 2336
2323 // Commit the thread reservation. 2337 // Commit the thread reservation.
2324 event_reservation.Commit(); 2338 event_reservation.Commit();
@@ -2559,9 +2573,9 @@ struct FunctionDef {
2559} // namespace 2573} // namespace
2560 2574
2561static const FunctionDef SVC_Table_32[] = { 2575static const FunctionDef SVC_Table_32[] = {
2562 {0x00, nullptr, "Unknown"}, 2576 {0x00, nullptr, "Unknown0"},
2563 {0x01, SvcWrap32<SetHeapSize32>, "SetHeapSize32"}, 2577 {0x01, SvcWrap32<SetHeapSize32>, "SetHeapSize32"},
2564 {0x02, nullptr, "Unknown"}, 2578 {0x02, nullptr, "SetMemoryPermission32"},
2565 {0x03, SvcWrap32<SetMemoryAttribute32>, "SetMemoryAttribute32"}, 2579 {0x03, SvcWrap32<SetMemoryAttribute32>, "SetMemoryAttribute32"},
2566 {0x04, SvcWrap32<MapMemory32>, "MapMemory32"}, 2580 {0x04, SvcWrap32<MapMemory32>, "MapMemory32"},
2567 {0x05, SvcWrap32<UnmapMemory32>, "UnmapMemory32"}, 2581 {0x05, SvcWrap32<UnmapMemory32>, "UnmapMemory32"},
@@ -2591,97 +2605,97 @@ static const FunctionDef SVC_Table_32[] = {
2591 {0x1d, SvcWrap32<SignalProcessWideKey32>, "SignalProcessWideKey32"}, 2605 {0x1d, SvcWrap32<SignalProcessWideKey32>, "SignalProcessWideKey32"},
2592 {0x1e, SvcWrap32<GetSystemTick32>, "GetSystemTick32"}, 2606 {0x1e, SvcWrap32<GetSystemTick32>, "GetSystemTick32"},
2593 {0x1f, SvcWrap32<ConnectToNamedPort32>, "ConnectToNamedPort32"}, 2607 {0x1f, SvcWrap32<ConnectToNamedPort32>, "ConnectToNamedPort32"},
2594 {0x20, nullptr, "Unknown"}, 2608 {0x20, nullptr, "SendSyncRequestLight32"},
2595 {0x21, SvcWrap32<SendSyncRequest32>, "SendSyncRequest32"}, 2609 {0x21, SvcWrap32<SendSyncRequest32>, "SendSyncRequest32"},
2596 {0x22, nullptr, "SendSyncRequestWithUserBuffer32"}, 2610 {0x22, nullptr, "SendSyncRequestWithUserBuffer32"},
2597 {0x23, nullptr, "Unknown"}, 2611 {0x23, nullptr, "SendAsyncRequestWithUserBuffer32"},
2598 {0x24, SvcWrap32<GetProcessId32>, "GetProcessId32"}, 2612 {0x24, SvcWrap32<GetProcessId32>, "GetProcessId32"},
2599 {0x25, SvcWrap32<GetThreadId32>, "GetThreadId32"}, 2613 {0x25, SvcWrap32<GetThreadId32>, "GetThreadId32"},
2600 {0x26, SvcWrap32<Break32>, "Break32"}, 2614 {0x26, SvcWrap32<Break32>, "Break32"},
2601 {0x27, nullptr, "OutputDebugString32"}, 2615 {0x27, SvcWrap32<OutputDebugString32>, "OutputDebugString32"},
2602 {0x28, nullptr, "Unknown"}, 2616 {0x28, nullptr, "ReturnFromException32"},
2603 {0x29, SvcWrap32<GetInfo32>, "GetInfo32"}, 2617 {0x29, SvcWrap32<GetInfo32>, "GetInfo32"},
2604 {0x2a, nullptr, "Unknown"}, 2618 {0x2a, nullptr, "FlushEntireDataCache32"},
2605 {0x2b, nullptr, "Unknown"}, 2619 {0x2b, nullptr, "FlushDataCache32"},
2606 {0x2c, SvcWrap32<MapPhysicalMemory32>, "MapPhysicalMemory32"}, 2620 {0x2c, SvcWrap32<MapPhysicalMemory32>, "MapPhysicalMemory32"},
2607 {0x2d, SvcWrap32<UnmapPhysicalMemory32>, "UnmapPhysicalMemory32"}, 2621 {0x2d, SvcWrap32<UnmapPhysicalMemory32>, "UnmapPhysicalMemory32"},
2608 {0x2e, nullptr, "Unknown"}, 2622 {0x2e, nullptr, "GetDebugFutureThreadInfo32"},
2609 {0x2f, nullptr, "Unknown"}, 2623 {0x2f, nullptr, "GetLastThreadInfo32"},
2610 {0x30, nullptr, "Unknown"}, 2624 {0x30, nullptr, "GetResourceLimitLimitValue32"},
2611 {0x31, nullptr, "Unknown"}, 2625 {0x31, nullptr, "GetResourceLimitCurrentValue32"},
2612 {0x32, SvcWrap32<SetThreadActivity32>, "SetThreadActivity32"}, 2626 {0x32, SvcWrap32<SetThreadActivity32>, "SetThreadActivity32"},
2613 {0x33, SvcWrap32<GetThreadContext32>, "GetThreadContext32"}, 2627 {0x33, SvcWrap32<GetThreadContext32>, "GetThreadContext32"},
2614 {0x34, SvcWrap32<WaitForAddress32>, "WaitForAddress32"}, 2628 {0x34, SvcWrap32<WaitForAddress32>, "WaitForAddress32"},
2615 {0x35, SvcWrap32<SignalToAddress32>, "SignalToAddress32"}, 2629 {0x35, SvcWrap32<SignalToAddress32>, "SignalToAddress32"},
2616 {0x36, SvcWrap32<SynchronizePreemptionState>, "SynchronizePreemptionState32"}, 2630 {0x36, SvcWrap32<SynchronizePreemptionState>, "SynchronizePreemptionState32"},
2617 {0x37, nullptr, "Unknown"}, 2631 {0x37, nullptr, "GetResourceLimitPeakValue32"},
2618 {0x38, nullptr, "Unknown"}, 2632 {0x38, nullptr, "Unknown38"},
2619 {0x39, nullptr, "Unknown"}, 2633 {0x39, nullptr, "CreateIoPool32"},
2620 {0x3a, nullptr, "Unknown"}, 2634 {0x3a, nullptr, "CreateIoRegion32"},
2621 {0x3b, nullptr, "Unknown"}, 2635 {0x3b, nullptr, "Unknown3b"},
2622 {0x3c, nullptr, "Unknown"}, 2636 {0x3c, nullptr, "KernelDebug32"},
2623 {0x3d, nullptr, "Unknown"}, 2637 {0x3d, nullptr, "ChangeKernelTraceState32"},
2624 {0x3e, nullptr, "Unknown"}, 2638 {0x3e, nullptr, "Unknown3e"},
2625 {0x3f, nullptr, "Unknown"}, 2639 {0x3f, nullptr, "Unknown3f"},
2626 {0x40, nullptr, "CreateSession32"}, 2640 {0x40, nullptr, "CreateSession32"},
2627 {0x41, nullptr, "AcceptSession32"}, 2641 {0x41, nullptr, "AcceptSession32"},
2628 {0x42, nullptr, "Unknown"}, 2642 {0x42, nullptr, "ReplyAndReceiveLight32"},
2629 {0x43, nullptr, "ReplyAndReceive32"}, 2643 {0x43, nullptr, "ReplyAndReceive32"},
2630 {0x44, nullptr, "Unknown"}, 2644 {0x44, nullptr, "ReplyAndReceiveWithUserBuffer32"},
2631 {0x45, SvcWrap32<CreateEvent32>, "CreateEvent32"}, 2645 {0x45, SvcWrap32<CreateEvent32>, "CreateEvent32"},
2632 {0x46, nullptr, "Unknown"}, 2646 {0x46, nullptr, "MapIoRegion32"},
2633 {0x47, nullptr, "Unknown"}, 2647 {0x47, nullptr, "UnmapIoRegion32"},
2634 {0x48, nullptr, "Unknown"}, 2648 {0x48, nullptr, "MapPhysicalMemoryUnsafe32"},
2635 {0x49, nullptr, "Unknown"}, 2649 {0x49, nullptr, "UnmapPhysicalMemoryUnsafe32"},
2636 {0x4a, nullptr, "Unknown"}, 2650 {0x4a, nullptr, "SetUnsafeLimit32"},
2637 {0x4b, nullptr, "Unknown"}, 2651 {0x4b, SvcWrap32<CreateCodeMemory32>, "CreateCodeMemory32"},
2638 {0x4c, nullptr, "Unknown"}, 2652 {0x4c, SvcWrap32<ControlCodeMemory32>, "ControlCodeMemory32"},
2639 {0x4d, nullptr, "Unknown"}, 2653 {0x4d, nullptr, "SleepSystem32"},
2640 {0x4e, nullptr, "Unknown"}, 2654 {0x4e, nullptr, "ReadWriteRegister32"},
2641 {0x4f, nullptr, "Unknown"}, 2655 {0x4f, nullptr, "SetProcessActivity32"},
2642 {0x50, nullptr, "Unknown"}, 2656 {0x50, nullptr, "CreateSharedMemory32"},
2643 {0x51, nullptr, "Unknown"}, 2657 {0x51, nullptr, "MapTransferMemory32"},
2644 {0x52, nullptr, "Unknown"}, 2658 {0x52, nullptr, "UnmapTransferMemory32"},
2645 {0x53, nullptr, "Unknown"}, 2659 {0x53, nullptr, "CreateInterruptEvent32"},
2646 {0x54, nullptr, "Unknown"}, 2660 {0x54, nullptr, "QueryPhysicalAddress32"},
2647 {0x55, nullptr, "Unknown"}, 2661 {0x55, nullptr, "QueryIoMapping32"},
2648 {0x56, nullptr, "Unknown"}, 2662 {0x56, nullptr, "CreateDeviceAddressSpace32"},
2649 {0x57, nullptr, "Unknown"}, 2663 {0x57, nullptr, "AttachDeviceAddressSpace32"},
2650 {0x58, nullptr, "Unknown"}, 2664 {0x58, nullptr, "DetachDeviceAddressSpace32"},
2651 {0x59, nullptr, "Unknown"}, 2665 {0x59, nullptr, "MapDeviceAddressSpaceByForce32"},
2652 {0x5a, nullptr, "Unknown"}, 2666 {0x5a, nullptr, "MapDeviceAddressSpaceAligned32"},
2653 {0x5b, nullptr, "Unknown"}, 2667 {0x5b, nullptr, "MapDeviceAddressSpace32"},
2654 {0x5c, nullptr, "Unknown"}, 2668 {0x5c, nullptr, "UnmapDeviceAddressSpace32"},
2655 {0x5d, nullptr, "Unknown"}, 2669 {0x5d, nullptr, "InvalidateProcessDataCache32"},
2656 {0x5e, nullptr, "Unknown"}, 2670 {0x5e, nullptr, "StoreProcessDataCache32"},
2657 {0x5F, SvcWrap32<FlushProcessDataCache32>, "FlushProcessDataCache32"}, 2671 {0x5F, SvcWrap32<FlushProcessDataCache32>, "FlushProcessDataCache32"},
2658 {0x60, nullptr, "Unknown"}, 2672 {0x60, nullptr, "StoreProcessDataCache32"},
2659 {0x61, nullptr, "Unknown"}, 2673 {0x61, nullptr, "BreakDebugProcess32"},
2660 {0x62, nullptr, "Unknown"}, 2674 {0x62, nullptr, "TerminateDebugProcess32"},
2661 {0x63, nullptr, "Unknown"}, 2675 {0x63, nullptr, "GetDebugEvent32"},
2662 {0x64, nullptr, "Unknown"}, 2676 {0x64, nullptr, "ContinueDebugEvent32"},
2663 {0x65, nullptr, "GetProcessList32"}, 2677 {0x65, nullptr, "GetProcessList32"},
2664 {0x66, nullptr, "Unknown"}, 2678 {0x66, nullptr, "GetThreadList"},
2665 {0x67, nullptr, "Unknown"}, 2679 {0x67, nullptr, "GetDebugThreadContext32"},
2666 {0x68, nullptr, "Unknown"}, 2680 {0x68, nullptr, "SetDebugThreadContext32"},
2667 {0x69, nullptr, "Unknown"}, 2681 {0x69, nullptr, "QueryDebugProcessMemory32"},
2668 {0x6A, nullptr, "Unknown"}, 2682 {0x6A, nullptr, "ReadDebugProcessMemory32"},
2669 {0x6B, nullptr, "Unknown"}, 2683 {0x6B, nullptr, "WriteDebugProcessMemory32"},
2670 {0x6C, nullptr, "Unknown"}, 2684 {0x6C, nullptr, "SetHardwareBreakPoint32"},
2671 {0x6D, nullptr, "Unknown"}, 2685 {0x6D, nullptr, "GetDebugThreadParam32"},
2672 {0x6E, nullptr, "Unknown"}, 2686 {0x6E, nullptr, "Unknown6E"},
2673 {0x6f, nullptr, "GetSystemInfo32"}, 2687 {0x6f, nullptr, "GetSystemInfo32"},
2674 {0x70, nullptr, "CreatePort32"}, 2688 {0x70, nullptr, "CreatePort32"},
2675 {0x71, nullptr, "ManageNamedPort32"}, 2689 {0x71, nullptr, "ManageNamedPort32"},
2676 {0x72, nullptr, "ConnectToPort32"}, 2690 {0x72, nullptr, "ConnectToPort32"},
2677 {0x73, nullptr, "SetProcessMemoryPermission32"}, 2691 {0x73, nullptr, "SetProcessMemoryPermission32"},
2678 {0x74, nullptr, "Unknown"}, 2692 {0x74, nullptr, "MapProcessMemory32"},
2679 {0x75, nullptr, "Unknown"}, 2693 {0x75, nullptr, "UnmapProcessMemory32"},
2680 {0x76, nullptr, "Unknown"}, 2694 {0x76, nullptr, "QueryProcessMemory32"},
2681 {0x77, nullptr, "MapProcessCodeMemory32"}, 2695 {0x77, nullptr, "MapProcessCodeMemory32"},
2682 {0x78, nullptr, "UnmapProcessCodeMemory32"}, 2696 {0x78, nullptr, "UnmapProcessCodeMemory32"},
2683 {0x79, nullptr, "Unknown"}, 2697 {0x79, nullptr, "CreateProcess32"},
2684 {0x7A, nullptr, "Unknown"}, 2698 {0x7A, nullptr, "StartProcess32"},
2685 {0x7B, nullptr, "TerminateProcess32"}, 2699 {0x7B, nullptr, "TerminateProcess32"},
2686 {0x7C, nullptr, "GetProcessInfo32"}, 2700 {0x7C, nullptr, "GetProcessInfo32"},
2687 {0x7D, nullptr, "CreateResourceLimit32"}, 2701 {0x7D, nullptr, "CreateResourceLimit32"},
@@ -2754,7 +2768,7 @@ static const FunctionDef SVC_Table_32[] = {
2754}; 2768};
2755 2769
2756static const FunctionDef SVC_Table_64[] = { 2770static const FunctionDef SVC_Table_64[] = {
2757 {0x00, nullptr, "Unknown"}, 2771 {0x00, nullptr, "Unknown0"},
2758 {0x01, SvcWrap64<SetHeapSize>, "SetHeapSize"}, 2772 {0x01, SvcWrap64<SetHeapSize>, "SetHeapSize"},
2759 {0x02, SvcWrap64<SetMemoryPermission>, "SetMemoryPermission"}, 2773 {0x02, SvcWrap64<SetMemoryPermission>, "SetMemoryPermission"},
2760 {0x03, SvcWrap64<SetMemoryAttribute>, "SetMemoryAttribute"}, 2774 {0x03, SvcWrap64<SetMemoryAttribute>, "SetMemoryAttribute"},
@@ -2809,23 +2823,23 @@ static const FunctionDef SVC_Table_64[] = {
2809 {0x34, SvcWrap64<WaitForAddress>, "WaitForAddress"}, 2823 {0x34, SvcWrap64<WaitForAddress>, "WaitForAddress"},
2810 {0x35, SvcWrap64<SignalToAddress>, "SignalToAddress"}, 2824 {0x35, SvcWrap64<SignalToAddress>, "SignalToAddress"},
2811 {0x36, SvcWrap64<SynchronizePreemptionState>, "SynchronizePreemptionState"}, 2825 {0x36, SvcWrap64<SynchronizePreemptionState>, "SynchronizePreemptionState"},
2812 {0x37, nullptr, "Unknown"}, 2826 {0x37, nullptr, "GetResourceLimitPeakValue"},
2813 {0x38, nullptr, "Unknown"}, 2827 {0x38, nullptr, "Unknown38"},
2814 {0x39, nullptr, "Unknown"}, 2828 {0x39, nullptr, "CreateIoPool"},
2815 {0x3A, nullptr, "Unknown"}, 2829 {0x3A, nullptr, "CreateIoRegion"},
2816 {0x3B, nullptr, "Unknown"}, 2830 {0x3B, nullptr, "Unknown3B"},
2817 {0x3C, SvcWrap64<KernelDebug>, "KernelDebug"}, 2831 {0x3C, SvcWrap64<KernelDebug>, "KernelDebug"},
2818 {0x3D, SvcWrap64<ChangeKernelTraceState>, "ChangeKernelTraceState"}, 2832 {0x3D, SvcWrap64<ChangeKernelTraceState>, "ChangeKernelTraceState"},
2819 {0x3E, nullptr, "Unknown"}, 2833 {0x3E, nullptr, "Unknown3e"},
2820 {0x3F, nullptr, "Unknown"}, 2834 {0x3F, nullptr, "Unknown3f"},
2821 {0x40, nullptr, "CreateSession"}, 2835 {0x40, nullptr, "CreateSession"},
2822 {0x41, nullptr, "AcceptSession"}, 2836 {0x41, nullptr, "AcceptSession"},
2823 {0x42, nullptr, "ReplyAndReceiveLight"}, 2837 {0x42, nullptr, "ReplyAndReceiveLight"},
2824 {0x43, nullptr, "ReplyAndReceive"}, 2838 {0x43, nullptr, "ReplyAndReceive"},
2825 {0x44, nullptr, "ReplyAndReceiveWithUserBuffer"}, 2839 {0x44, nullptr, "ReplyAndReceiveWithUserBuffer"},
2826 {0x45, SvcWrap64<CreateEvent>, "CreateEvent"}, 2840 {0x45, SvcWrap64<CreateEvent>, "CreateEvent"},
2827 {0x46, nullptr, "Unknown"}, 2841 {0x46, nullptr, "MapIoRegion"},
2828 {0x47, nullptr, "Unknown"}, 2842 {0x47, nullptr, "UnmapIoRegion"},
2829 {0x48, nullptr, "MapPhysicalMemoryUnsafe"}, 2843 {0x48, nullptr, "MapPhysicalMemoryUnsafe"},
2830 {0x49, nullptr, "UnmapPhysicalMemoryUnsafe"}, 2844 {0x49, nullptr, "UnmapPhysicalMemoryUnsafe"},
2831 {0x4A, nullptr, "SetUnsafeLimit"}, 2845 {0x4A, nullptr, "SetUnsafeLimit"},
@@ -2864,7 +2878,7 @@ static const FunctionDef SVC_Table_64[] = {
2864 {0x6B, nullptr, "WriteDebugProcessMemory"}, 2878 {0x6B, nullptr, "WriteDebugProcessMemory"},
2865 {0x6C, nullptr, "SetHardwareBreakPoint"}, 2879 {0x6C, nullptr, "SetHardwareBreakPoint"},
2866 {0x6D, nullptr, "GetDebugThreadParam"}, 2880 {0x6D, nullptr, "GetDebugThreadParam"},
2867 {0x6E, nullptr, "Unknown"}, 2881 {0x6E, nullptr, "Unknown6E"},
2868 {0x6F, nullptr, "GetSystemInfo"}, 2882 {0x6F, nullptr, "GetSystemInfo"},
2869 {0x70, nullptr, "CreatePort"}, 2883 {0x70, nullptr, "CreatePort"},
2870 {0x71, nullptr, "ManageNamedPort"}, 2884 {0x71, nullptr, "ManageNamedPort"},
diff --git a/src/core/hle/kernel/svc_types.h b/src/core/hle/kernel/svc_types.h
index 365e22e4e..b2e9ec092 100644
--- a/src/core/hle/kernel/svc_types.h
+++ b/src/core/hle/kernel/svc_types.h
@@ -96,4 +96,6 @@ constexpr inline s32 IdealCoreNoUpdate = -3;
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/kernel/svc_wrap.h b/src/core/hle/kernel/svc_wrap.h
index a60adfcab..d309f166c 100644
--- a/src/core/hle/kernel/svc_wrap.h
+++ b/src/core/hle/kernel/svc_wrap.h
@@ -669,4 +669,26 @@ void SvcWrap32(Core::System& system) {
669 FuncReturn(system, retval); 669 FuncReturn(system, retval);
670} 670}
671 671
672// Used by CreateCodeMemory32
673template <ResultCode func(Core::System&, Handle*, u32, u32)>
674void SvcWrap32(Core::System& system) {
675 Handle handle = 0;
676
677 const u32 retval = func(system, &handle, Param32(system, 1), Param32(system, 2)).raw;
678
679 system.CurrentArmInterface().SetReg(1, handle);
680 FuncReturn(system, retval);
681}
682
683// Used by ControlCodeMemory32
684template <ResultCode func(Core::System&, Handle, u32, u64, u64, Svc::MemoryPermission)>
685void SvcWrap32(Core::System& system) {
686 const u32 retval =
687 func(system, Param32(system, 0), Param32(system, 1), Param(system, 2), Param(system, 4),
688 static_cast<Svc::MemoryPermission>(Param32(system, 6)))
689 .raw;
690
691 FuncReturn(system, retval);
692}
693
672} // namespace Kernel 694} // namespace Kernel
diff --git a/src/core/hle/service/acc/acc.cpp b/src/core/hle/service/acc/acc.cpp
index 6e63e057e..e34ef5a78 100644
--- a/src/core/hle/service/acc/acc.cpp
+++ b/src/core/hle/service/acc/acc.cpp
@@ -39,9 +39,9 @@ constexpr ResultCode ERR_FAILED_SAVE_DATA{ErrorModule::Account, 100};
39// Thumbnails are hard coded to be at least this size 39// Thumbnails are hard coded to be at least this size
40constexpr std::size_t THUMBNAIL_SIZE = 0x24000; 40constexpr std::size_t THUMBNAIL_SIZE = 0x24000;
41 41
42static std::filesystem::path GetImagePath(Common::UUID uuid) { 42static std::filesystem::path GetImagePath(const Common::UUID& uuid) {
43 return Common::FS::GetYuzuPath(Common::FS::YuzuPath::NANDDir) / 43 return Common::FS::GetYuzuPath(Common::FS::YuzuPath::NANDDir) /
44 fmt::format("system/save/8000000000000010/su/avators/{}.jpg", uuid.FormatSwitch()); 44 fmt::format("system/save/8000000000000010/su/avators/{}.jpg", uuid.FormattedString());
45} 45}
46 46
47static constexpr u32 SanitizeJPEGSize(std::size_t size) { 47static constexpr u32 SanitizeJPEGSize(std::size_t size) {
@@ -290,7 +290,7 @@ public:
290 290
291protected: 291protected:
292 void Get(Kernel::HLERequestContext& ctx) { 292 void Get(Kernel::HLERequestContext& ctx) {
293 LOG_DEBUG(Service_ACC, "called user_id=0x{}", user_id.Format()); 293 LOG_DEBUG(Service_ACC, "called user_id=0x{}", user_id.RawString());
294 ProfileBase profile_base{}; 294 ProfileBase profile_base{};
295 ProfileData data{}; 295 ProfileData data{};
296 if (profile_manager.GetProfileBaseAndData(user_id, profile_base, data)) { 296 if (profile_manager.GetProfileBaseAndData(user_id, profile_base, data)) {
@@ -300,21 +300,21 @@ protected:
300 rb.PushRaw(profile_base); 300 rb.PushRaw(profile_base);
301 } else { 301 } else {
302 LOG_ERROR(Service_ACC, "Failed to get profile base and data for user=0x{}", 302 LOG_ERROR(Service_ACC, "Failed to get profile base and data for user=0x{}",
303 user_id.Format()); 303 user_id.RawString());
304 IPC::ResponseBuilder rb{ctx, 2}; 304 IPC::ResponseBuilder rb{ctx, 2};
305 rb.Push(ResultUnknown); // TODO(ogniK): Get actual error code 305 rb.Push(ResultUnknown); // TODO(ogniK): Get actual error code
306 } 306 }
307 } 307 }
308 308
309 void GetBase(Kernel::HLERequestContext& ctx) { 309 void GetBase(Kernel::HLERequestContext& ctx) {
310 LOG_DEBUG(Service_ACC, "called user_id=0x{}", user_id.Format()); 310 LOG_DEBUG(Service_ACC, "called user_id=0x{}", user_id.RawString());
311 ProfileBase profile_base{}; 311 ProfileBase profile_base{};
312 if (profile_manager.GetProfileBase(user_id, profile_base)) { 312 if (profile_manager.GetProfileBase(user_id, profile_base)) {
313 IPC::ResponseBuilder rb{ctx, 16}; 313 IPC::ResponseBuilder rb{ctx, 16};
314 rb.Push(ResultSuccess); 314 rb.Push(ResultSuccess);
315 rb.PushRaw(profile_base); 315 rb.PushRaw(profile_base);
316 } else { 316 } else {
317 LOG_ERROR(Service_ACC, "Failed to get profile base for user=0x{}", user_id.Format()); 317 LOG_ERROR(Service_ACC, "Failed to get profile base for user=0x{}", user_id.RawString());
318 IPC::ResponseBuilder rb{ctx, 2}; 318 IPC::ResponseBuilder rb{ctx, 2};
319 rb.Push(ResultUnknown); // TODO(ogniK): Get actual error code 319 rb.Push(ResultUnknown); // TODO(ogniK): Get actual error code
320 } 320 }
@@ -373,7 +373,7 @@ protected:
373 LOG_DEBUG(Service_ACC, "called, username='{}', timestamp={:016X}, uuid=0x{}", 373 LOG_DEBUG(Service_ACC, "called, username='{}', timestamp={:016X}, uuid=0x{}",
374 Common::StringFromFixedZeroTerminatedBuffer( 374 Common::StringFromFixedZeroTerminatedBuffer(
375 reinterpret_cast<const char*>(base.username.data()), base.username.size()), 375 reinterpret_cast<const char*>(base.username.data()), base.username.size()),
376 base.timestamp, base.user_uuid.Format()); 376 base.timestamp, base.user_uuid.RawString());
377 377
378 if (user_data.size() < sizeof(ProfileData)) { 378 if (user_data.size() < sizeof(ProfileData)) {
379 LOG_ERROR(Service_ACC, "ProfileData buffer too small!"); 379 LOG_ERROR(Service_ACC, "ProfileData buffer too small!");
@@ -406,7 +406,7 @@ protected:
406 LOG_DEBUG(Service_ACC, "called, username='{}', timestamp={:016X}, uuid=0x{}", 406 LOG_DEBUG(Service_ACC, "called, username='{}', timestamp={:016X}, uuid=0x{}",
407 Common::StringFromFixedZeroTerminatedBuffer( 407 Common::StringFromFixedZeroTerminatedBuffer(
408 reinterpret_cast<const char*>(base.username.data()), base.username.size()), 408 reinterpret_cast<const char*>(base.username.data()), base.username.size()),
409 base.timestamp, base.user_uuid.Format()); 409 base.timestamp, base.user_uuid.RawString());
410 410
411 if (user_data.size() < sizeof(ProfileData)) { 411 if (user_data.size() < sizeof(ProfileData)) {
412 LOG_ERROR(Service_ACC, "ProfileData buffer too small!"); 412 LOG_ERROR(Service_ACC, "ProfileData buffer too small!");
@@ -435,7 +435,7 @@ protected:
435 } 435 }
436 436
437 ProfileManager& profile_manager; 437 ProfileManager& profile_manager;
438 Common::UUID user_id{Common::INVALID_UUID}; ///< The user id this profile refers to. 438 Common::UUID user_id{}; ///< The user id this profile refers to.
439}; 439};
440 440
441class IProfile final : public IProfileCommon { 441class IProfile final : public IProfileCommon {
@@ -547,7 +547,7 @@ private:
547 547
548 IPC::ResponseBuilder rb{ctx, 4}; 548 IPC::ResponseBuilder rb{ctx, 4};
549 rb.Push(ResultSuccess); 549 rb.Push(ResultSuccess);
550 rb.PushRaw<u64>(user_id.GetNintendoID()); 550 rb.PushRaw<u64>(user_id.Hash());
551 } 551 }
552 552
553 void EnsureIdTokenCacheAsync(Kernel::HLERequestContext& ctx) { 553 void EnsureIdTokenCacheAsync(Kernel::HLERequestContext& ctx) {
@@ -577,7 +577,7 @@ private:
577 577
578 IPC::ResponseBuilder rb{ctx, 4}; 578 IPC::ResponseBuilder rb{ctx, 4};
579 rb.Push(ResultSuccess); 579 rb.Push(ResultSuccess);
580 rb.PushRaw<u64>(user_id.GetNintendoID()); 580 rb.PushRaw<u64>(user_id.Hash());
581 } 581 }
582 582
583 void StoreOpenContext(Kernel::HLERequestContext& ctx) { 583 void StoreOpenContext(Kernel::HLERequestContext& ctx) {
@@ -587,7 +587,7 @@ private:
587 } 587 }
588 588
589 std::shared_ptr<EnsureTokenIdCacheAsyncInterface> ensure_token_id{}; 589 std::shared_ptr<EnsureTokenIdCacheAsyncInterface> ensure_token_id{};
590 Common::UUID user_id{Common::INVALID_UUID}; 590 Common::UUID user_id{};
591}; 591};
592 592
593// 6.0.0+ 593// 6.0.0+
@@ -687,7 +687,7 @@ void Module::Interface::GetUserCount(Kernel::HLERequestContext& ctx) {
687void Module::Interface::GetUserExistence(Kernel::HLERequestContext& ctx) { 687void Module::Interface::GetUserExistence(Kernel::HLERequestContext& ctx) {
688 IPC::RequestParser rp{ctx}; 688 IPC::RequestParser rp{ctx};
689 Common::UUID user_id = rp.PopRaw<Common::UUID>(); 689 Common::UUID user_id = rp.PopRaw<Common::UUID>();
690 LOG_DEBUG(Service_ACC, "called user_id=0x{}", user_id.Format()); 690 LOG_DEBUG(Service_ACC, "called user_id=0x{}", user_id.RawString());
691 691
692 IPC::ResponseBuilder rb{ctx, 3}; 692 IPC::ResponseBuilder rb{ctx, 3};
693 rb.Push(ResultSuccess); 693 rb.Push(ResultSuccess);
@@ -718,7 +718,7 @@ void Module::Interface::GetLastOpenedUser(Kernel::HLERequestContext& ctx) {
718void Module::Interface::GetProfile(Kernel::HLERequestContext& ctx) { 718void Module::Interface::GetProfile(Kernel::HLERequestContext& ctx) {
719 IPC::RequestParser rp{ctx}; 719 IPC::RequestParser rp{ctx};
720 Common::UUID user_id = rp.PopRaw<Common::UUID>(); 720 Common::UUID user_id = rp.PopRaw<Common::UUID>();
721 LOG_DEBUG(Service_ACC, "called user_id=0x{}", user_id.Format()); 721 LOG_DEBUG(Service_ACC, "called user_id=0x{}", user_id.RawString());
722 722
723 IPC::ResponseBuilder rb{ctx, 2, 0, 1}; 723 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
724 rb.Push(ResultSuccess); 724 rb.Push(ResultSuccess);
@@ -833,7 +833,7 @@ void Module::Interface::GetProfileEditor(Kernel::HLERequestContext& ctx) {
833 IPC::RequestParser rp{ctx}; 833 IPC::RequestParser rp{ctx};
834 Common::UUID user_id = rp.PopRaw<Common::UUID>(); 834 Common::UUID user_id = rp.PopRaw<Common::UUID>();
835 835
836 LOG_DEBUG(Service_ACC, "called, user_id=0x{}", user_id.Format()); 836 LOG_DEBUG(Service_ACC, "called, user_id=0x{}", user_id.RawString());
837 837
838 IPC::ResponseBuilder rb{ctx, 2, 0, 1}; 838 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
839 rb.Push(ResultSuccess); 839 rb.Push(ResultSuccess);
@@ -875,7 +875,7 @@ void Module::Interface::StoreSaveDataThumbnailApplication(Kernel::HLERequestCont
875 IPC::RequestParser rp{ctx}; 875 IPC::RequestParser rp{ctx};
876 const auto uuid = rp.PopRaw<Common::UUID>(); 876 const auto uuid = rp.PopRaw<Common::UUID>();
877 877
878 LOG_WARNING(Service_ACC, "(STUBBED) called, uuid=0x{}", uuid.Format()); 878 LOG_WARNING(Service_ACC, "(STUBBED) called, uuid=0x{}", uuid.RawString());
879 879
880 // TODO(ogniK): Check if application ID is zero on acc initialize. As we don't have a reliable 880 // TODO(ogniK): Check if application ID is zero on acc initialize. As we don't have a reliable
881 // way of confirming things like the TID, we're going to assume a non zero value for the time 881 // way of confirming things like the TID, we're going to assume a non zero value for the time
@@ -889,7 +889,7 @@ void Module::Interface::StoreSaveDataThumbnailSystem(Kernel::HLERequestContext&
889 const auto uuid = rp.PopRaw<Common::UUID>(); 889 const auto uuid = rp.PopRaw<Common::UUID>();
890 const auto tid = rp.Pop<u64_le>(); 890 const auto tid = rp.Pop<u64_le>();
891 891
892 LOG_WARNING(Service_ACC, "(STUBBED) called, uuid=0x{}, tid={:016X}", uuid.Format(), tid); 892 LOG_WARNING(Service_ACC, "(STUBBED) called, uuid=0x{}, tid={:016X}", uuid.RawString(), tid);
893 StoreSaveDataThumbnail(ctx, uuid, tid); 893 StoreSaveDataThumbnail(ctx, uuid, tid);
894} 894}
895 895
@@ -903,7 +903,7 @@ void Module::Interface::StoreSaveDataThumbnail(Kernel::HLERequestContext& ctx,
903 return; 903 return;
904 } 904 }
905 905
906 if (!uuid) { 906 if (uuid.IsInvalid()) {
907 LOG_ERROR(Service_ACC, "User ID is not valid!"); 907 LOG_ERROR(Service_ACC, "User ID is not valid!");
908 rb.Push(ERR_INVALID_USER_ID); 908 rb.Push(ERR_INVALID_USER_ID);
909 return; 909 return;
@@ -927,20 +927,20 @@ void Module::Interface::TrySelectUserWithoutInteraction(Kernel::HLERequestContex
927 IPC::ResponseBuilder rb{ctx, 6}; 927 IPC::ResponseBuilder rb{ctx, 6};
928 if (profile_manager->GetUserCount() != 1) { 928 if (profile_manager->GetUserCount() != 1) {
929 rb.Push(ResultSuccess); 929 rb.Push(ResultSuccess);
930 rb.PushRaw<u128>(Common::INVALID_UUID); 930 rb.PushRaw(Common::InvalidUUID);
931 return; 931 return;
932 } 932 }
933 933
934 const auto user_list = profile_manager->GetAllUsers(); 934 const auto user_list = profile_manager->GetAllUsers();
935 if (std::ranges::all_of(user_list, [](const auto& user) { return user.IsInvalid(); })) { 935 if (std::ranges::all_of(user_list, [](const auto& user) { return user.IsInvalid(); })) {
936 rb.Push(ResultUnknown); // TODO(ogniK): Find the correct error code 936 rb.Push(ResultUnknown); // TODO(ogniK): Find the correct error code
937 rb.PushRaw<u128>(Common::INVALID_UUID); 937 rb.PushRaw(Common::InvalidUUID);
938 return; 938 return;
939 } 939 }
940 940
941 // Select the first user we have 941 // Select the first user we have
942 rb.Push(ResultSuccess); 942 rb.Push(ResultSuccess);
943 rb.PushRaw<u128>(profile_manager->GetUser(0)->uuid); 943 rb.PushRaw(profile_manager->GetUser(0)->uuid);
944} 944}
945 945
946Module::Interface::Interface(std::shared_ptr<Module> module_, 946Module::Interface::Interface(std::shared_ptr<Module> module_,
diff --git a/src/core/hle/service/acc/profile_manager.cpp b/src/core/hle/service/acc/profile_manager.cpp
index 568303ced..fba847142 100644
--- a/src/core/hle/service/acc/profile_manager.cpp
+++ b/src/core/hle/service/acc/profile_manager.cpp
@@ -19,8 +19,8 @@ namespace FS = Common::FS;
19using Common::UUID; 19using Common::UUID;
20 20
21struct UserRaw { 21struct UserRaw {
22 UUID uuid{Common::INVALID_UUID}; 22 UUID uuid{};
23 UUID uuid2{Common::INVALID_UUID}; 23 UUID uuid2{};
24 u64 timestamp{}; 24 u64 timestamp{};
25 ProfileUsername username{}; 25 ProfileUsername username{};
26 ProfileData extra_data{}; 26 ProfileData extra_data{};
@@ -45,7 +45,7 @@ ProfileManager::ProfileManager() {
45 45
46 // Create an user if none are present 46 // Create an user if none are present
47 if (user_count == 0) { 47 if (user_count == 0) {
48 CreateNewUser(UUID::Generate(), "yuzu"); 48 CreateNewUser(UUID::MakeRandom(), "yuzu");
49 } 49 }
50 50
51 auto current = 51 auto current =
@@ -101,7 +101,7 @@ ResultCode ProfileManager::CreateNewUser(UUID uuid, const ProfileUsername& usern
101 if (user_count == MAX_USERS) { 101 if (user_count == MAX_USERS) {
102 return ERROR_TOO_MANY_USERS; 102 return ERROR_TOO_MANY_USERS;
103 } 103 }
104 if (!uuid) { 104 if (uuid.IsInvalid()) {
105 return ERROR_ARGUMENT_IS_NULL; 105 return ERROR_ARGUMENT_IS_NULL;
106 } 106 }
107 if (username[0] == 0x0) { 107 if (username[0] == 0x0) {
@@ -145,7 +145,7 @@ std::optional<UUID> ProfileManager::GetUser(std::size_t index) const {
145 145
146/// Returns a users profile index based on their user id. 146/// Returns a users profile index based on their user id.
147std::optional<std::size_t> ProfileManager::GetUserIndex(const UUID& uuid) const { 147std::optional<std::size_t> ProfileManager::GetUserIndex(const UUID& uuid) const {
148 if (!uuid) { 148 if (uuid.IsInvalid()) {
149 return std::nullopt; 149 return std::nullopt;
150 } 150 }
151 151
@@ -250,9 +250,10 @@ UserIDArray ProfileManager::GetOpenUsers() const {
250 std::ranges::transform(profiles, output.begin(), [](const ProfileInfo& p) { 250 std::ranges::transform(profiles, output.begin(), [](const ProfileInfo& p) {
251 if (p.is_open) 251 if (p.is_open)
252 return p.user_uuid; 252 return p.user_uuid;
253 return UUID{Common::INVALID_UUID}; 253 return Common::InvalidUUID;
254 }); 254 });
255 std::stable_partition(output.begin(), output.end(), [](const UUID& uuid) { return uuid; }); 255 std::stable_partition(output.begin(), output.end(),
256 [](const UUID& uuid) { return uuid.IsValid(); });
256 return output; 257 return output;
257} 258}
258 259
@@ -299,7 +300,7 @@ bool ProfileManager::RemoveUser(UUID uuid) {
299 300
300 profiles[*index] = ProfileInfo{}; 301 profiles[*index] = ProfileInfo{};
301 std::stable_partition(profiles.begin(), profiles.end(), 302 std::stable_partition(profiles.begin(), profiles.end(),
302 [](const ProfileInfo& profile) { return profile.user_uuid; }); 303 [](const ProfileInfo& profile) { return profile.user_uuid.IsValid(); });
303 return true; 304 return true;
304} 305}
305 306
@@ -361,7 +362,7 @@ void ProfileManager::ParseUserSaveFile() {
361 } 362 }
362 363
363 std::stable_partition(profiles.begin(), profiles.end(), 364 std::stable_partition(profiles.begin(), profiles.end(),
364 [](const ProfileInfo& profile) { return profile.user_uuid; }); 365 [](const ProfileInfo& profile) { return profile.user_uuid.IsValid(); });
365} 366}
366 367
367void ProfileManager::WriteUserSaveFile() { 368void ProfileManager::WriteUserSaveFile() {
diff --git a/src/core/hle/service/acc/profile_manager.h b/src/core/hle/service/acc/profile_manager.h
index 71b9d5518..17347f7ef 100644
--- a/src/core/hle/service/acc/profile_manager.h
+++ b/src/core/hle/service/acc/profile_manager.h
@@ -35,7 +35,7 @@ static_assert(sizeof(ProfileData) == 0x80, "ProfileData structure has incorrect
35/// This holds general information about a users profile. This is where we store all the information 35/// This holds general information about a users profile. This is where we store all the information
36/// based on a specific user 36/// based on a specific user
37struct ProfileInfo { 37struct ProfileInfo {
38 Common::UUID user_uuid{Common::INVALID_UUID}; 38 Common::UUID user_uuid{};
39 ProfileUsername username{}; 39 ProfileUsername username{};
40 u64 creation_time{}; 40 u64 creation_time{};
41 ProfileData data{}; // TODO(ognik): Work out what this is 41 ProfileData data{}; // TODO(ognik): Work out what this is
@@ -49,7 +49,7 @@ struct ProfileBase {
49 49
50 // Zero out all the fields to make the profile slot considered "Empty" 50 // Zero out all the fields to make the profile slot considered "Empty"
51 void Invalidate() { 51 void Invalidate() {
52 user_uuid.Invalidate(); 52 user_uuid = {};
53 timestamp = 0; 53 timestamp = 0;
54 username.fill(0); 54 username.fill(0);
55 } 55 }
@@ -103,7 +103,7 @@ private:
103 103
104 std::array<ProfileInfo, MAX_USERS> profiles{}; 104 std::array<ProfileInfo, MAX_USERS> profiles{};
105 std::size_t user_count{}; 105 std::size_t user_count{};
106 Common::UUID last_opened_user{Common::INVALID_UUID}; 106 Common::UUID last_opened_user{};
107}; 107};
108 108
109}; // namespace Service::Account 109}; // namespace Service::Account
diff --git a/src/core/hle/service/am/am.cpp b/src/core/hle/service/am/am.cpp
index e60661fe1..420de3c54 100644
--- a/src/core/hle/service/am/am.cpp
+++ b/src/core/hle/service/am/am.cpp
@@ -55,7 +55,7 @@ constexpr u32 LAUNCH_PARAMETER_ACCOUNT_PRESELECTED_USER_MAGIC = 0xC79497CA;
55struct LaunchParameterAccountPreselectedUser { 55struct LaunchParameterAccountPreselectedUser {
56 u32_le magic; 56 u32_le magic;
57 u32_le is_account_selected; 57 u32_le is_account_selected;
58 u128 current_user; 58 Common::UUID current_user;
59 INSERT_PADDING_BYTES(0x70); 59 INSERT_PADDING_BYTES(0x70);
60}; 60};
61static_assert(sizeof(LaunchParameterAccountPreselectedUser) == 0x88); 61static_assert(sizeof(LaunchParameterAccountPreselectedUser) == 0x88);
@@ -618,7 +618,7 @@ void AppletMessageQueue::PushMessage(AppletMessage msg) {
618AppletMessageQueue::AppletMessage AppletMessageQueue::PopMessage() { 618AppletMessageQueue::AppletMessage AppletMessageQueue::PopMessage() {
619 if (messages.empty()) { 619 if (messages.empty()) {
620 on_new_message->GetWritableEvent().Clear(); 620 on_new_message->GetWritableEvent().Clear();
621 return AppletMessage::NoMessage; 621 return AppletMessage::None;
622 } 622 }
623 auto msg = messages.front(); 623 auto msg = messages.front();
624 messages.pop(); 624 messages.pop();
@@ -633,7 +633,7 @@ std::size_t AppletMessageQueue::GetMessageCount() const {
633} 633}
634 634
635void AppletMessageQueue::RequestExit() { 635void AppletMessageQueue::RequestExit() {
636 PushMessage(AppletMessage::ExitRequested); 636 PushMessage(AppletMessage::Exit);
637} 637}
638 638
639void AppletMessageQueue::FocusStateChanged() { 639void AppletMessageQueue::FocusStateChanged() {
@@ -732,7 +732,7 @@ void ICommonStateGetter::ReceiveMessage(Kernel::HLERequestContext& ctx) {
732 const auto message = msg_queue->PopMessage(); 732 const auto message = msg_queue->PopMessage();
733 IPC::ResponseBuilder rb{ctx, 3}; 733 IPC::ResponseBuilder rb{ctx, 3};
734 734
735 if (message == AppletMessageQueue::AppletMessage::NoMessage) { 735 if (message == AppletMessageQueue::AppletMessage::None) {
736 LOG_ERROR(Service_AM, "Message queue is empty"); 736 LOG_ERROR(Service_AM, "Message queue is empty");
737 rb.Push(ERR_NO_MESSAGES); 737 rb.Push(ERR_NO_MESSAGES);
738 rb.PushEnum<AppletMessageQueue::AppletMessage>(message); 738 rb.PushEnum<AppletMessageQueue::AppletMessage>(message);
@@ -980,7 +980,7 @@ private:
980 LOG_DEBUG(Service_AM, "called"); 980 LOG_DEBUG(Service_AM, "called");
981 981
982 IPC::RequestParser rp{ctx}; 982 IPC::RequestParser rp{ctx};
983 applet->GetBroker().PushNormalDataFromGame(rp.PopIpcInterface<IStorage>()); 983 applet->GetBroker().PushNormalDataFromGame(rp.PopIpcInterface<IStorage>().lock());
984 984
985 IPC::ResponseBuilder rb{ctx, 2}; 985 IPC::ResponseBuilder rb{ctx, 2};
986 rb.Push(ResultSuccess); 986 rb.Push(ResultSuccess);
@@ -1007,7 +1007,7 @@ private:
1007 LOG_DEBUG(Service_AM, "called"); 1007 LOG_DEBUG(Service_AM, "called");
1008 1008
1009 IPC::RequestParser rp{ctx}; 1009 IPC::RequestParser rp{ctx};
1010 applet->GetBroker().PushInteractiveDataFromGame(rp.PopIpcInterface<IStorage>()); 1010 applet->GetBroker().PushInteractiveDataFromGame(rp.PopIpcInterface<IStorage>().lock());
1011 1011
1012 ASSERT(applet->IsInitialized()); 1012 ASSERT(applet->IsInitialized());
1013 applet->ExecuteInteractive(); 1013 applet->ExecuteInteractive();
@@ -1453,8 +1453,8 @@ void IApplicationFunctions::PopLaunchParameter(Kernel::HLERequestContext& ctx) {
1453 1453
1454 Account::ProfileManager profile_manager{}; 1454 Account::ProfileManager profile_manager{};
1455 const auto uuid = profile_manager.GetUser(static_cast<s32>(Settings::values.current_user)); 1455 const auto uuid = profile_manager.GetUser(static_cast<s32>(Settings::values.current_user));
1456 ASSERT(uuid); 1456 ASSERT(uuid.has_value() && uuid->IsValid());
1457 params.current_user = uuid->uuid; 1457 params.current_user = *uuid;
1458 1458
1459 IPC::ResponseBuilder rb{ctx, 2, 0, 1}; 1459 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
1460 1460
diff --git a/src/core/hle/service/am/am.h b/src/core/hle/service/am/am.h
index 2a578aea5..fdd937b82 100644
--- a/src/core/hle/service/am/am.h
+++ b/src/core/hle/service/am/am.h
@@ -22,6 +22,7 @@ class NVFlinger;
22 22
23namespace Service::AM { 23namespace Service::AM {
24 24
25// This is nn::settings::Language
25enum SystemLanguage { 26enum SystemLanguage {
26 Japanese = 0, 27 Japanese = 0,
27 English = 1, // en-US 28 English = 1, // en-US
@@ -41,16 +42,44 @@ enum SystemLanguage {
41 // 4.0.0+ 42 // 4.0.0+
42 SimplifiedChinese = 15, 43 SimplifiedChinese = 15,
43 TraditionalChinese = 16, 44 TraditionalChinese = 16,
45 // 10.1.0+
46 BrazilianPortuguese = 17,
44}; 47};
45 48
46class AppletMessageQueue { 49class AppletMessageQueue {
47public: 50public:
51 // This is nn::am::AppletMessage
48 enum class AppletMessage : u32 { 52 enum class AppletMessage : u32 {
49 NoMessage = 0, 53 None = 0,
50 ExitRequested = 4, 54 ChangeIntoForeground = 1,
55 ChangeIntoBackground = 2,
56 Exit = 4,
57 ApplicationExited = 6,
51 FocusStateChanged = 15, 58 FocusStateChanged = 15,
59 Resume = 16,
60 DetectShortPressingHomeButton = 20,
61 DetectLongPressingHomeButton = 21,
62 DetectShortPressingPowerButton = 22,
63 DetectMiddlePressingPowerButton = 23,
64 DetectLongPressingPowerButton = 24,
65 RequestToPrepareSleep = 25,
66 FinishedSleepSequence = 26,
67 SleepRequiredByHighTemperature = 27,
68 SleepRequiredByLowBattery = 28,
69 AutoPowerDown = 29,
52 OperationModeChanged = 30, 70 OperationModeChanged = 30,
53 PerformanceModeChanged = 31, 71 PerformanceModeChanged = 31,
72 DetectReceivingCecSystemStandby = 32,
73 SdCardRemoved = 33,
74 LaunchApplicationRequested = 50,
75 RequestToDisplay = 51,
76 ShowApplicationLogo = 55,
77 HideApplicationLogo = 56,
78 ForceHideApplicationLogo = 57,
79 FloatingApplicationDetected = 60,
80 DetectShortPressingCaptureButton = 90,
81 AlbumScreenShotTaken = 92,
82 AlbumRecordingSaved = 93,
54 }; 83 };
55 84
56 explicit AppletMessageQueue(Core::System& system); 85 explicit AppletMessageQueue(Core::System& system);
@@ -179,11 +208,14 @@ public:
179 ~ICommonStateGetter() override; 208 ~ICommonStateGetter() override;
180 209
181private: 210private:
211 // This is nn::oe::FocusState
182 enum class FocusState : u8 { 212 enum class FocusState : u8 {
183 InFocus = 1, 213 InFocus = 1,
184 NotInFocus = 2, 214 NotInFocus = 2,
215 Background = 3,
185 }; 216 };
186 217
218 // This is nn::oe::OperationMode
187 enum class OperationMode : u8 { 219 enum class OperationMode : u8 {
188 Handheld = 0, 220 Handheld = 0,
189 Docked = 1, 221 Docked = 1,
diff --git a/src/core/hle/service/am/applets/applet_mii.cpp b/src/core/hle/service/am/applets/applet_mii.cpp
new file mode 100644
index 000000000..8c4173737
--- /dev/null
+++ b/src/core/hle/service/am/applets/applet_mii.cpp
@@ -0,0 +1,101 @@
1// Copyright 2022 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include "common/assert.h"
6#include "common/logging/log.h"
7#include "core/core.h"
8#include "core/frontend/applets/mii.h"
9#include "core/hle/service/am/am.h"
10#include "core/hle/service/am/applets/applet_mii.h"
11#include "core/reporter.h"
12
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/applet_profile_select.cpp b/src/core/hle/service/am/applets/applet_profile_select.cpp
index a6e891944..82500e121 100644
--- a/src/core/hle/service/am/applets/applet_profile_select.cpp
+++ b/src/core/hle/service/am/applets/applet_profile_select.cpp
@@ -62,11 +62,11 @@ void ProfileSelect::SelectionComplete(std::optional<Common::UUID> uuid) {
62 62
63 if (uuid.has_value() && uuid->IsValid()) { 63 if (uuid.has_value() && uuid->IsValid()) {
64 output.result = 0; 64 output.result = 0;
65 output.uuid_selected = uuid->uuid; 65 output.uuid_selected = *uuid;
66 } else { 66 } else {
67 status = ERR_USER_CANCELLED_SELECTION; 67 status = ERR_USER_CANCELLED_SELECTION;
68 output.result = ERR_USER_CANCELLED_SELECTION.raw; 68 output.result = ERR_USER_CANCELLED_SELECTION.raw;
69 output.uuid_selected = Common::INVALID_UUID; 69 output.uuid_selected = Common::InvalidUUID;
70 } 70 }
71 71
72 final_data = std::vector<u8>(sizeof(UserSelectionOutput)); 72 final_data = std::vector<u8>(sizeof(UserSelectionOutput));
diff --git a/src/core/hle/service/am/applets/applet_profile_select.h b/src/core/hle/service/am/applets/applet_profile_select.h
index 8fb76e6c4..852e1e0c0 100644
--- a/src/core/hle/service/am/applets/applet_profile_select.h
+++ b/src/core/hle/service/am/applets/applet_profile_select.h
@@ -27,7 +27,7 @@ static_assert(sizeof(UserSelectionConfig) == 0xA0, "UserSelectionConfig has inco
27 27
28struct UserSelectionOutput { 28struct UserSelectionOutput {
29 u64 result; 29 u64 result;
30 u128 uuid_selected; 30 Common::UUID uuid_selected;
31}; 31};
32static_assert(sizeof(UserSelectionOutput) == 0x18, "UserSelectionOutput has incorrect size."); 32static_assert(sizeof(UserSelectionOutput) == 0x18, "UserSelectionOutput has incorrect size.");
33 33
diff --git a/src/core/hle/service/am/applets/applets.cpp b/src/core/hle/service/am/applets/applets.cpp
index 134ac1ee2..79e62679d 100644
--- a/src/core/hle/service/am/applets/applets.cpp
+++ b/src/core/hle/service/am/applets/applets.cpp
@@ -9,6 +9,7 @@
9#include "core/frontend/applets/controller.h" 9#include "core/frontend/applets/controller.h"
10#include "core/frontend/applets/error.h" 10#include "core/frontend/applets/error.h"
11#include "core/frontend/applets/general_frontend.h" 11#include "core/frontend/applets/general_frontend.h"
12#include "core/frontend/applets/mii.h"
12#include "core/frontend/applets/profile_select.h" 13#include "core/frontend/applets/profile_select.h"
13#include "core/frontend/applets/software_keyboard.h" 14#include "core/frontend/applets/software_keyboard.h"
14#include "core/frontend/applets/web_browser.h" 15#include "core/frontend/applets/web_browser.h"
@@ -19,6 +20,7 @@
19#include "core/hle/service/am/applets/applet_controller.h" 20#include "core/hle/service/am/applets/applet_controller.h"
20#include "core/hle/service/am/applets/applet_error.h" 21#include "core/hle/service/am/applets/applet_error.h"
21#include "core/hle/service/am/applets/applet_general_backend.h" 22#include "core/hle/service/am/applets/applet_general_backend.h"
23#include "core/hle/service/am/applets/applet_mii.h"
22#include "core/hle/service/am/applets/applet_profile_select.h" 24#include "core/hle/service/am/applets/applet_profile_select.h"
23#include "core/hle/service/am/applets/applet_software_keyboard.h" 25#include "core/hle/service/am/applets/applet_software_keyboard.h"
24#include "core/hle/service/am/applets/applet_web_browser.h" 26#include "core/hle/service/am/applets/applet_web_browser.h"
@@ -172,10 +174,11 @@ AppletFrontendSet::AppletFrontendSet() = default;
172 174
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/apm/apm_controller.cpp b/src/core/hle/service/apm/apm_controller.cpp
index 98839fe97..187fef2ad 100644
--- a/src/core/hle/service/apm/apm_controller.cpp
+++ b/src/core/hle/service/apm/apm_controller.cpp
@@ -17,8 +17,8 @@ constexpr auto DEFAULT_PERFORMANCE_CONFIGURATION = PerformanceConfiguration::Con
17 17
18Controller::Controller(Core::Timing::CoreTiming& core_timing_) 18Controller::Controller(Core::Timing::CoreTiming& core_timing_)
19 : core_timing{core_timing_}, configs{ 19 : core_timing{core_timing_}, configs{
20 {PerformanceMode::Handheld, DEFAULT_PERFORMANCE_CONFIGURATION}, 20 {PerformanceMode::Normal, DEFAULT_PERFORMANCE_CONFIGURATION},
21 {PerformanceMode::Docked, DEFAULT_PERFORMANCE_CONFIGURATION}, 21 {PerformanceMode::Boost, DEFAULT_PERFORMANCE_CONFIGURATION},
22 } {} 22 } {}
23 23
24Controller::~Controller() = default; 24Controller::~Controller() = default;
@@ -63,13 +63,13 @@ void Controller::SetFromCpuBoostMode(CpuBoostMode mode) {
63 PerformanceConfiguration::Config15, 63 PerformanceConfiguration::Config15,
64 }}; 64 }};
65 65
66 SetPerformanceConfiguration(PerformanceMode::Docked, 66 SetPerformanceConfiguration(PerformanceMode::Boost,
67 BOOST_MODE_TO_CONFIG_MAP.at(static_cast<u32>(mode))); 67 BOOST_MODE_TO_CONFIG_MAP.at(static_cast<u32>(mode)));
68} 68}
69 69
70PerformanceMode Controller::GetCurrentPerformanceMode() const { 70PerformanceMode Controller::GetCurrentPerformanceMode() const {
71 return Settings::values.use_docked_mode.GetValue() ? PerformanceMode::Docked 71 return Settings::values.use_docked_mode.GetValue() ? PerformanceMode::Boost
72 : PerformanceMode::Handheld; 72 : PerformanceMode::Normal;
73} 73}
74 74
75PerformanceConfiguration Controller::GetCurrentPerformanceConfiguration(PerformanceMode mode) { 75PerformanceConfiguration Controller::GetCurrentPerformanceConfiguration(PerformanceMode mode) {
diff --git a/src/core/hle/service/apm/apm_controller.h b/src/core/hle/service/apm/apm_controller.h
index 8d48e0104..d6fbd2c0c 100644
--- a/src/core/hle/service/apm/apm_controller.h
+++ b/src/core/hle/service/apm/apm_controller.h
@@ -32,15 +32,18 @@ enum class PerformanceConfiguration : u32 {
32 Config16 = 0x9222000C, 32 Config16 = 0x9222000C,
33}; 33};
34 34
35// This is nn::oe::CpuBoostMode
35enum class CpuBoostMode : u32 { 36enum class CpuBoostMode : u32 {
36 Disabled = 0, 37 Normal = 0, // Boost mode disabled
37 Full = 1, // CPU + GPU -> Config 13, 14, 15, or 16 38 FastLoad = 1, // CPU + GPU -> Config 13, 14, 15, or 16
38 Partial = 2, // GPU Only -> Config 15 or 16 39 Partial = 2, // GPU Only -> Config 15 or 16
39}; 40};
40 41
41enum class PerformanceMode : u8 { 42// This is nn::oe::PerformanceMode
42 Handheld = 0, 43enum class PerformanceMode : s32 {
43 Docked = 1, 44 Invalid = -1,
45 Normal = 0,
46 Boost = 1,
44}; 47};
45 48
46// Class to manage the state and change of the emulated system performance. 49// Class to manage the state and change of the emulated system performance.
diff --git a/src/core/hle/service/friend/friend.cpp b/src/core/hle/service/friend/friend.cpp
index 9f9cea1e0..79cd3acbb 100644
--- a/src/core/hle/service/friend/friend.cpp
+++ b/src/core/hle/service/friend/friend.cpp
@@ -173,7 +173,7 @@ private:
173 const auto uuid = rp.PopRaw<Common::UUID>(); 173 const auto uuid = rp.PopRaw<Common::UUID>();
174 174
175 LOG_WARNING(Service_Friend, "(STUBBED) called, local_play={}, uuid=0x{}", local_play, 175 LOG_WARNING(Service_Friend, "(STUBBED) called, local_play={}, uuid=0x{}", local_play,
176 uuid.Format()); 176 uuid.RawString());
177 177
178 IPC::ResponseBuilder rb{ctx, 2}; 178 IPC::ResponseBuilder rb{ctx, 2};
179 rb.Push(ResultSuccess); 179 rb.Push(ResultSuccess);
@@ -186,7 +186,7 @@ private:
186 [[maybe_unused]] const auto filter = rp.PopRaw<SizedFriendFilter>(); 186 [[maybe_unused]] const auto filter = rp.PopRaw<SizedFriendFilter>();
187 const auto pid = rp.Pop<u64>(); 187 const auto pid = rp.Pop<u64>();
188 LOG_WARNING(Service_Friend, "(STUBBED) called, offset={}, uuid=0x{}, pid={}", friend_offset, 188 LOG_WARNING(Service_Friend, "(STUBBED) called, offset={}, uuid=0x{}, pid={}", friend_offset,
189 uuid.Format(), pid); 189 uuid.RawString(), pid);
190 190
191 IPC::ResponseBuilder rb{ctx, 3}; 191 IPC::ResponseBuilder rb{ctx, 3};
192 rb.Push(ResultSuccess); 192 rb.Push(ResultSuccess);
@@ -312,7 +312,7 @@ void Module::Interface::CreateNotificationService(Kernel::HLERequestContext& ctx
312 IPC::RequestParser rp{ctx}; 312 IPC::RequestParser rp{ctx};
313 auto uuid = rp.PopRaw<Common::UUID>(); 313 auto uuid = rp.PopRaw<Common::UUID>();
314 314
315 LOG_DEBUG(Service_Friend, "called, uuid=0x{}", uuid.Format()); 315 LOG_DEBUG(Service_Friend, "called, uuid=0x{}", uuid.RawString());
316 316
317 IPC::ResponseBuilder rb{ctx, 2, 0, 1}; 317 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
318 rb.Push(ResultSuccess); 318 rb.Push(ResultSuccess);
diff --git a/src/core/hle/service/hid/hid.cpp b/src/core/hle/service/hid/hid.cpp
index a2bf7defb..d9202ea6c 100644
--- a/src/core/hle/service/hid/hid.cpp
+++ b/src/core/hle/service/hid/hid.cpp
@@ -320,7 +320,7 @@ Hid::Hid(Core::System& system_)
320 {308, nullptr, "SetSevenSixAxisSensorFusionStrength"}, 320 {308, nullptr, "SetSevenSixAxisSensorFusionStrength"},
321 {309, nullptr, "GetSevenSixAxisSensorFusionStrength"}, 321 {309, nullptr, "GetSevenSixAxisSensorFusionStrength"},
322 {310, &Hid::ResetSevenSixAxisSensorTimestamp, "ResetSevenSixAxisSensorTimestamp"}, 322 {310, &Hid::ResetSevenSixAxisSensorTimestamp, "ResetSevenSixAxisSensorTimestamp"},
323 {400, nullptr, "IsUsbFullKeyControllerEnabled"}, 323 {400, &Hid::IsUsbFullKeyControllerEnabled, "IsUsbFullKeyControllerEnabled"},
324 {401, nullptr, "EnableUsbFullKeyController"}, 324 {401, nullptr, "EnableUsbFullKeyController"},
325 {402, nullptr, "IsUsbFullKeyControllerConnected"}, 325 {402, nullptr, "IsUsbFullKeyControllerConnected"},
326 {403, nullptr, "HasBattery"}, 326 {403, nullptr, "HasBattery"},
@@ -1673,6 +1673,16 @@ void Hid::ResetSevenSixAxisSensorTimestamp(Kernel::HLERequestContext& ctx) {
1673 rb.Push(ResultSuccess); 1673 rb.Push(ResultSuccess);
1674} 1674}
1675 1675
1676void Hid::IsUsbFullKeyControllerEnabled(Kernel::HLERequestContext& ctx) {
1677 IPC::RequestParser rp{ctx};
1678
1679 LOG_WARNING(Service_HID, "(STUBBED) called");
1680
1681 IPC::ResponseBuilder rb{ctx, 3};
1682 rb.Push(ResultSuccess);
1683 rb.Push(false);
1684}
1685
1676void Hid::SetIsPalmaAllConnectable(Kernel::HLERequestContext& ctx) { 1686void Hid::SetIsPalmaAllConnectable(Kernel::HLERequestContext& ctx) {
1677 IPC::RequestParser rp{ctx}; 1687 IPC::RequestParser rp{ctx};
1678 const auto applet_resource_user_id{rp.Pop<u64>()}; 1688 const auto applet_resource_user_id{rp.Pop<u64>()};
diff --git a/src/core/hle/service/hid/hid.h b/src/core/hle/service/hid/hid.h
index d290df161..c281081a7 100644
--- a/src/core/hle/service/hid/hid.h
+++ b/src/core/hle/service/hid/hid.h
@@ -159,6 +159,7 @@ private:
159 void InitializeSevenSixAxisSensor(Kernel::HLERequestContext& ctx); 159 void InitializeSevenSixAxisSensor(Kernel::HLERequestContext& ctx);
160 void FinalizeSevenSixAxisSensor(Kernel::HLERequestContext& ctx); 160 void FinalizeSevenSixAxisSensor(Kernel::HLERequestContext& ctx);
161 void ResetSevenSixAxisSensorTimestamp(Kernel::HLERequestContext& ctx); 161 void ResetSevenSixAxisSensorTimestamp(Kernel::HLERequestContext& ctx);
162 void IsUsbFullKeyControllerEnabled(Kernel::HLERequestContext& ctx);
162 void SetIsPalmaAllConnectable(Kernel::HLERequestContext& ctx); 163 void SetIsPalmaAllConnectable(Kernel::HLERequestContext& ctx);
163 void SetPalmaBoostMode(Kernel::HLERequestContext& ctx); 164 void SetPalmaBoostMode(Kernel::HLERequestContext& ctx);
164 void SetNpadCommunicationMode(Kernel::HLERequestContext& ctx); 165 void SetNpadCommunicationMode(Kernel::HLERequestContext& ctx);
diff --git a/src/core/hle/service/kernel_helpers.cpp b/src/core/hle/service/kernel_helpers.cpp
index 62f4cdfb2..ff0bbb788 100644
--- a/src/core/hle/service/kernel_helpers.cpp
+++ b/src/core/hle/service/kernel_helpers.cpp
@@ -3,7 +3,9 @@
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include "core/core.h" 5#include "core/core.h"
6#include "core/core_timing.h"
6#include "core/hle/kernel/k_event.h" 7#include "core/hle/kernel/k_event.h"
8#include "core/hle/kernel/k_memory_manager.h"
7#include "core/hle/kernel/k_process.h" 9#include "core/hle/kernel/k_process.h"
8#include "core/hle/kernel/k_readable_event.h" 10#include "core/hle/kernel/k_readable_event.h"
9#include "core/hle/kernel/k_resource_limit.h" 11#include "core/hle/kernel/k_resource_limit.h"
@@ -15,9 +17,11 @@ namespace Service::KernelHelpers {
15 17
16ServiceContext::ServiceContext(Core::System& system_, std::string name_) 18ServiceContext::ServiceContext(Core::System& system_, std::string name_)
17 : kernel(system_.Kernel()) { 19 : kernel(system_.Kernel()) {
20 // Create the process.
18 process = Kernel::KProcess::Create(kernel); 21 process = Kernel::KProcess::Create(kernel);
19 ASSERT(Kernel::KProcess::Initialize(process, system_, std::move(name_), 22 ASSERT(Kernel::KProcess::Initialize(process, system_, std::move(name_),
20 Kernel::KProcess::ProcessType::Userland) 23 Kernel::KProcess::ProcessType::KernelInternal,
24 kernel.GetSystemResourceLimit())
21 .IsSuccess()); 25 .IsSuccess());
22} 26}
23 27
@@ -43,7 +47,7 @@ Kernel::KEvent* ServiceContext::CreateEvent(std::string&& name) {
43 } 47 }
44 48
45 // Initialize the event. 49 // Initialize the event.
46 event->Initialize(std::move(name)); 50 event->Initialize(std::move(name), process);
47 51
48 // Commit the thread reservation. 52 // Commit the thread reservation.
49 event_reservation.Commit(); 53 event_reservation.Commit();
diff --git a/src/core/hle/service/ldr/ldr.cpp b/src/core/hle/service/ldr/ldr.cpp
index 9fc7bb1b1..099276420 100644
--- a/src/core/hle/service/ldr/ldr.cpp
+++ b/src/core/hle/service/ldr/ldr.cpp
@@ -288,7 +288,7 @@ public:
288 } 288 }
289 289
290 bool ValidateRegionForMap(Kernel::KPageTable& page_table, VAddr start, std::size_t size) const { 290 bool ValidateRegionForMap(Kernel::KPageTable& page_table, VAddr start, std::size_t size) const {
291 constexpr std::size_t padding_size{4 * Kernel::PageSize}; 291 const std::size_t padding_size{page_table.GetNumGuardPages() * Kernel::PageSize};
292 const auto start_info{page_table.QueryInfo(start - 1)}; 292 const auto start_info{page_table.QueryInfo(start - 1)};
293 293
294 if (start_info.state != Kernel::KMemoryState::Free) { 294 if (start_info.state != Kernel::KMemoryState::Free) {
@@ -308,31 +308,69 @@ public:
308 return (start + size + padding_size) <= (end_info.GetAddress() + end_info.GetSize()); 308 return (start + size + padding_size) <= (end_info.GetAddress() + end_info.GetSize());
309 } 309 }
310 310
311 VAddr GetRandomMapRegion(const Kernel::KPageTable& page_table, std::size_t size) const { 311 ResultCode GetAvailableMapRegion(Kernel::KPageTable& page_table, u64 size, VAddr& out_addr) {
312 VAddr addr{}; 312 size = Common::AlignUp(size, Kernel::PageSize);
313 const std::size_t end_pages{(page_table.GetAliasCodeRegionSize() - size) >> 313 size += page_table.GetNumGuardPages() * Kernel::PageSize * 4;
314 Kernel::PageBits}; 314
315 do { 315 const auto is_region_available = [&](VAddr addr) {
316 addr = page_table.GetAliasCodeRegionStart() + 316 const auto end_addr = addr + size;
317 (Kernel::KSystemControl::GenerateRandomRange(0, end_pages) << Kernel::PageBits); 317 while (addr < end_addr) {
318 } while (!page_table.IsInsideAddressSpace(addr, size) || 318 if (system.Memory().IsValidVirtualAddress(addr)) {
319 page_table.IsInsideHeapRegion(addr, size) || 319 return false;
320 page_table.IsInsideAliasRegion(addr, size)); 320 }
321 return addr; 321
322 if (!page_table.IsInsideAddressSpace(out_addr, size)) {
323 return false;
324 }
325
326 if (page_table.IsInsideHeapRegion(out_addr, size)) {
327 return false;
328 }
329
330 if (page_table.IsInsideAliasRegion(out_addr, size)) {
331 return false;
332 }
333
334 addr += Kernel::PageSize;
335 }
336 return true;
337 };
338
339 bool succeeded = false;
340 const auto map_region_end =
341 page_table.GetAliasCodeRegionStart() + page_table.GetAliasCodeRegionSize();
342 while (current_map_addr < map_region_end) {
343 if (is_region_available(current_map_addr)) {
344 succeeded = true;
345 break;
346 }
347 current_map_addr += 0x100000;
348 }
349
350 if (!succeeded) {
351 UNREACHABLE_MSG("Out of address space!");
352 return Kernel::ResultOutOfMemory;
353 }
354
355 out_addr = current_map_addr;
356 current_map_addr += size;
357
358 return ResultSuccess;
322 } 359 }
323 360
324 ResultVal<VAddr> MapProcessCodeMemory(Kernel::KProcess* process, VAddr baseAddress, 361 ResultVal<VAddr> MapProcessCodeMemory(Kernel::KProcess* process, VAddr base_addr, u64 size) {
325 u64 size) const { 362 auto& page_table{process->PageTable()};
363 VAddr addr{};
364
326 for (std::size_t retry = 0; retry < MAXIMUM_MAP_RETRIES; retry++) { 365 for (std::size_t retry = 0; retry < MAXIMUM_MAP_RETRIES; retry++) {
327 auto& page_table{process->PageTable()}; 366 R_TRY(GetAvailableMapRegion(page_table, size, addr));
328 const VAddr addr{GetRandomMapRegion(page_table, size)};
329 const ResultCode result{page_table.MapCodeMemory(addr, baseAddress, size)};
330 367
368 const ResultCode result{page_table.MapCodeMemory(addr, base_addr, size)};
331 if (result == Kernel::ResultInvalidCurrentMemory) { 369 if (result == Kernel::ResultInvalidCurrentMemory) {
332 continue; 370 continue;
333 } 371 }
334 372
335 CASCADE_CODE(result); 373 R_TRY(result);
336 374
337 if (ValidateRegionForMap(page_table, addr, size)) { 375 if (ValidateRegionForMap(page_table, addr, size)) {
338 return addr; 376 return addr;
@@ -343,7 +381,7 @@ public:
343 } 381 }
344 382
345 ResultVal<VAddr> MapNro(Kernel::KProcess* process, VAddr nro_addr, std::size_t nro_size, 383 ResultVal<VAddr> MapNro(Kernel::KProcess* process, VAddr nro_addr, std::size_t nro_size,
346 VAddr bss_addr, std::size_t bss_size, std::size_t size) const { 384 VAddr bss_addr, std::size_t bss_size, std::size_t size) {
347 for (std::size_t retry = 0; retry < MAXIMUM_MAP_RETRIES; retry++) { 385 for (std::size_t retry = 0; retry < MAXIMUM_MAP_RETRIES; retry++) {
348 auto& page_table{process->PageTable()}; 386 auto& page_table{process->PageTable()};
349 VAddr addr{}; 387 VAddr addr{};
@@ -597,6 +635,7 @@ public:
597 LOG_WARNING(Service_LDR, "(STUBBED) called"); 635 LOG_WARNING(Service_LDR, "(STUBBED) called");
598 636
599 initialized = true; 637 initialized = true;
638 current_map_addr = system.CurrentProcess()->PageTable().GetAliasCodeRegionStart();
600 639
601 IPC::ResponseBuilder rb{ctx, 2}; 640 IPC::ResponseBuilder rb{ctx, 2};
602 rb.Push(ResultSuccess); 641 rb.Push(ResultSuccess);
@@ -607,6 +646,7 @@ private:
607 646
608 std::map<VAddr, NROInfo> nro; 647 std::map<VAddr, NROInfo> nro;
609 std::map<VAddr, std::vector<SHA256Hash>> nrr; 648 std::map<VAddr, std::vector<SHA256Hash>> nrr;
649 VAddr current_map_addr{};
610 650
611 bool IsValidNROHash(const SHA256Hash& hash) const { 651 bool IsValidNROHash(const SHA256Hash& hash) const {
612 return std::any_of(nrr.begin(), nrr.end(), [&hash](const auto& p) { 652 return std::any_of(nrr.begin(), nrr.end(), [&hash](const auto& p) {
diff --git a/src/core/hle/service/mii/mii_manager.cpp b/src/core/hle/service/mii/mii_manager.cpp
index ca4ed35bb..0a57c3cde 100644
--- a/src/core/hle/service/mii/mii_manager.cpp
+++ b/src/core/hle/service/mii/mii_manager.cpp
@@ -118,16 +118,6 @@ u16 GenerateCrc16(const void* data, std::size_t size) {
118 return Common::swap16(static_cast<u16>(crc)); 118 return Common::swap16(static_cast<u16>(crc));
119} 119}
120 120
121Common::UUID GenerateValidUUID() {
122 auto uuid{Common::UUID::Generate()};
123
124 // Bit 7 must be set, and bit 6 unset for the UUID to be valid
125 uuid.uuid[1] &= 0xFFFFFFFFFFFFFF3FULL;
126 uuid.uuid[1] |= 0x0000000000000080ULL;
127
128 return uuid;
129}
130
131template <typename T> 121template <typename T>
132T GetRandomValue(T min, T max) { 122T GetRandomValue(T min, T max) {
133 std::random_device device; 123 std::random_device device;
@@ -383,7 +373,7 @@ MiiStoreData::MiiStoreData() = default;
383MiiStoreData::MiiStoreData(const MiiStoreData::Name& name, const MiiStoreBitFields& bit_fields, 373MiiStoreData::MiiStoreData(const MiiStoreData::Name& name, const MiiStoreBitFields& bit_fields,
384 const Common::UUID& user_id) { 374 const Common::UUID& user_id) {
385 data.name = name; 375 data.name = name;
386 data.uuid = GenerateValidUUID(); 376 data.uuid = Common::UUID::MakeRandomRFC4122V4();
387 377
388 std::memcpy(data.data.data(), &bit_fields, sizeof(MiiStoreBitFields)); 378 std::memcpy(data.data.data(), &bit_fields, sizeof(MiiStoreBitFields));
389 data_crc = GenerateCrc16(data.data.data(), sizeof(data)); 379 data_crc = GenerateCrc16(data.data.data(), sizeof(data));
diff --git a/src/core/hle/service/mii/mii_manager.h b/src/core/hle/service/mii/mii_manager.h
index 8e048fc56..6999d15b1 100644
--- a/src/core/hle/service/mii/mii_manager.h
+++ b/src/core/hle/service/mii/mii_manager.h
@@ -202,7 +202,7 @@ struct MiiStoreData {
202 static_assert(sizeof(MiiStoreBitFields) == sizeof(data), "data field has incorrect size."); 202 static_assert(sizeof(MiiStoreBitFields) == sizeof(data), "data field has incorrect size.");
203 203
204 Name name{}; 204 Name name{};
205 Common::UUID uuid{Common::INVALID_UUID}; 205 Common::UUID uuid{};
206 } data; 206 } data;
207 207
208 u16 data_crc{}; 208 u16 data_crc{};
@@ -326,7 +326,7 @@ public:
326 ResultCode GetIndex(const MiiInfo& info, u32& index); 326 ResultCode GetIndex(const MiiInfo& info, u32& index);
327 327
328private: 328private:
329 const Common::UUID user_id{Common::INVALID_UUID}; 329 const Common::UUID user_id{};
330 u64 update_counter{}; 330 u64 update_counter{};
331}; 331};
332 332
diff --git a/src/core/hle/service/mnpp/mnpp_app.cpp b/src/core/hle/service/mnpp/mnpp_app.cpp
new file mode 100644
index 000000000..53497612f
--- /dev/null
+++ b/src/core/hle/service/mnpp/mnpp_app.cpp
@@ -0,0 +1,45 @@
1// Copyright 2022 yuzu emulator team
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include "common/logging/log.h"
6#include "core/hle/ipc_helpers.h"
7#include "core/hle/service/mnpp/mnpp_app.h"
8#include "core/hle/service/sm/sm.h"
9
10namespace Service::MNPP {
11
12class MNPP_APP final : public ServiceFramework<MNPP_APP> {
13public:
14 explicit MNPP_APP(Core::System& system_) : ServiceFramework{system_, "mnpp:app"} {
15 // clang-format off
16 static const FunctionInfo functions[] = {
17 {0, &MNPP_APP::Unknown0, "unknown0"},
18 {1, &MNPP_APP::Unknown1, "unknown1"},
19 };
20 // clang-format on
21
22 RegisterHandlers(functions);
23 }
24
25private:
26 void Unknown0(Kernel::HLERequestContext& ctx) {
27 LOG_WARNING(Service_MNPP, "(STUBBED) called");
28
29 IPC::ResponseBuilder rb{ctx, 2};
30 rb.Push(ResultSuccess);
31 }
32
33 void Unknown1(Kernel::HLERequestContext& ctx) {
34 LOG_WARNING(Service_MNPP, "(STUBBED) called");
35
36 IPC::ResponseBuilder rb{ctx, 2};
37 rb.Push(ResultSuccess);
38 }
39};
40
41void InstallInterfaces(SM::ServiceManager& service_manager, Core::System& system) {
42 std::make_shared<MNPP_APP>(system)->InstallAsService(service_manager);
43}
44
45} // namespace Service::MNPP
diff --git a/src/core/hle/service/mnpp/mnpp_app.h b/src/core/hle/service/mnpp/mnpp_app.h
new file mode 100644
index 000000000..6bf20b494
--- /dev/null
+++ b/src/core/hle/service/mnpp/mnpp_app.h
@@ -0,0 +1,20 @@
1// Copyright 2022 yuzu emulator team
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7namespace Core {
8class System;
9}
10
11namespace Service::SM {
12class ServiceManager;
13}
14
15namespace Service::MNPP {
16
17/// Registers all MNPP services with the specified service manager.
18void InstallInterfaces(SM::ServiceManager& service_manager, Core::System& system);
19
20} // namespace Service::MNPP
diff --git a/src/core/hle/service/nfp/nfp.cpp b/src/core/hle/service/nfp/nfp.cpp
index 761d0d3c6..513107715 100644
--- a/src/core/hle/service/nfp/nfp.cpp
+++ b/src/core/hle/service/nfp/nfp.cpp
@@ -7,6 +7,9 @@
7 7
8#include "common/logging/log.h" 8#include "common/logging/log.h"
9#include "core/core.h" 9#include "core/core.h"
10#include "core/hid/emulated_controller.h"
11#include "core/hid/hid_core.h"
12#include "core/hid/hid_types.h"
10#include "core/hle/ipc_helpers.h" 13#include "core/hle/ipc_helpers.h"
11#include "core/hle/kernel/k_event.h" 14#include "core/hle/kernel/k_event.h"
12#include "core/hle/service/nfp/nfp.h" 15#include "core/hle/service/nfp/nfp.h"
@@ -14,343 +17,790 @@
14 17
15namespace Service::NFP { 18namespace Service::NFP {
16namespace ErrCodes { 19namespace ErrCodes {
17constexpr ResultCode ERR_NO_APPLICATION_AREA(ErrorModule::NFP, 152); 20constexpr ResultCode DeviceNotFound(ErrorModule::NFP, 64);
21constexpr ResultCode WrongDeviceState(ErrorModule::NFP, 73);
22constexpr ResultCode ApplicationAreaIsNotInitialized(ErrorModule::NFP, 128);
23constexpr ResultCode ApplicationAreaExist(ErrorModule::NFP, 168);
18} // namespace ErrCodes 24} // namespace ErrCodes
19 25
20Module::Interface::Interface(std::shared_ptr<Module> module_, Core::System& system_, 26constexpr u32 ApplicationAreaSize = 0xD8;
21 const char* name) 27
22 : ServiceFramework{system_, name}, module{std::move(module_)}, service_context{system_, 28IUser::IUser(Module::Interface& nfp_interface_, Core::System& system_)
23 "NFP::IUser"} { 29 : ServiceFramework{system_, "NFP::IUser"}, service_context{system_, service_name},
24 nfc_tag_load = service_context.CreateEvent("NFP::IUser:NFCTagDetected"); 30 nfp_interface{nfp_interface_} {
25} 31 static const FunctionInfo functions[] = {
26 32 {0, &IUser::Initialize, "Initialize"},
27Module::Interface::~Interface() { 33 {1, &IUser::Finalize, "Finalize"},
28 service_context.CloseEvent(nfc_tag_load); 34 {2, &IUser::ListDevices, "ListDevices"},
29} 35 {3, &IUser::StartDetection, "StartDetection"},
30 36 {4, &IUser::StopDetection, "StopDetection"},
31class IUser final : public ServiceFramework<IUser> { 37 {5, &IUser::Mount, "Mount"},
32public: 38 {6, &IUser::Unmount, "Unmount"},
33 explicit IUser(Module::Interface& nfp_interface_, Core::System& system_, 39 {7, &IUser::OpenApplicationArea, "OpenApplicationArea"},
34 KernelHelpers::ServiceContext& service_context_) 40 {8, &IUser::GetApplicationArea, "GetApplicationArea"},
35 : ServiceFramework{system_, "NFP::IUser"}, nfp_interface{nfp_interface_}, 41 {9, &IUser::SetApplicationArea, "SetApplicationArea"},
36 service_context{service_context_} { 42 {10, nullptr, "Flush"},
37 static const FunctionInfo functions[] = { 43 {11, nullptr, "Restore"},
38 {0, &IUser::Initialize, "Initialize"}, 44 {12, &IUser::CreateApplicationArea, "CreateApplicationArea"},
39 {1, &IUser::Finalize, "Finalize"}, 45 {13, &IUser::GetTagInfo, "GetTagInfo"},
40 {2, &IUser::ListDevices, "ListDevices"}, 46 {14, &IUser::GetRegisterInfo, "GetRegisterInfo"},
41 {3, &IUser::StartDetection, "StartDetection"}, 47 {15, &IUser::GetCommonInfo, "GetCommonInfo"},
42 {4, &IUser::StopDetection, "StopDetection"}, 48 {16, &IUser::GetModelInfo, "GetModelInfo"},
43 {5, &IUser::Mount, "Mount"}, 49 {17, &IUser::AttachActivateEvent, "AttachActivateEvent"},
44 {6, &IUser::Unmount, "Unmount"}, 50 {18, &IUser::AttachDeactivateEvent, "AttachDeactivateEvent"},
45 {7, &IUser::OpenApplicationArea, "OpenApplicationArea"}, 51 {19, &IUser::GetState, "GetState"},
46 {8, &IUser::GetApplicationArea, "GetApplicationArea"}, 52 {20, &IUser::GetDeviceState, "GetDeviceState"},
47 {9, nullptr, "SetApplicationArea"}, 53 {21, &IUser::GetNpadId, "GetNpadId"},
48 {10, nullptr, "Flush"}, 54 {22, &IUser::GetApplicationAreaSize, "GetApplicationAreaSize"},
49 {11, nullptr, "Restore"}, 55 {23, &IUser::AttachAvailabilityChangeEvent, "AttachAvailabilityChangeEvent"},
50 {12, nullptr, "CreateApplicationArea"}, 56 {24, nullptr, "RecreateApplicationArea"},
51 {13, &IUser::GetTagInfo, "GetTagInfo"}, 57 };
52 {14, &IUser::GetRegisterInfo, "GetRegisterInfo"}, 58 RegisterHandlers(functions);
53 {15, &IUser::GetCommonInfo, "GetCommonInfo"},
54 {16, &IUser::GetModelInfo, "GetModelInfo"},
55 {17, &IUser::AttachActivateEvent, "AttachActivateEvent"},
56 {18, &IUser::AttachDeactivateEvent, "AttachDeactivateEvent"},
57 {19, &IUser::GetState, "GetState"},
58 {20, &IUser::GetDeviceState, "GetDeviceState"},
59 {21, &IUser::GetNpadId, "GetNpadId"},
60 {22, &IUser::GetApplicationAreaSize, "GetApplicationAreaSize"},
61 {23, &IUser::AttachAvailabilityChangeEvent, "AttachAvailabilityChangeEvent"},
62 {24, nullptr, "RecreateApplicationArea"},
63 };
64 RegisterHandlers(functions);
65 59
66 deactivate_event = service_context.CreateEvent("NFP::IUser:DeactivateEvent"); 60 availability_change_event = service_context.CreateEvent("IUser:AvailabilityChangeEvent");
67 availability_change_event = 61}
68 service_context.CreateEvent("NFP::IUser:AvailabilityChangeEvent");
69 }
70 62
71 ~IUser() override { 63void IUser::Initialize(Kernel::HLERequestContext& ctx) {
72 service_context.CloseEvent(deactivate_event); 64 LOG_INFO(Service_NFC, "called");
73 service_context.CloseEvent(availability_change_event);
74 }
75 65
76private: 66 state = State::Initialized;
77 struct TagInfo {
78 std::array<u8, 10> uuid;
79 u8 uuid_length; // TODO(ogniK): Figure out if this is actual the uuid length or does it
80 // mean something else
81 std::array<u8, 0x15> padding_1;
82 u32_le protocol;
83 u32_le tag_type;
84 std::array<u8, 0x2c> padding_2;
85 };
86 static_assert(sizeof(TagInfo) == 0x54, "TagInfo is an invalid size");
87 67
88 enum class State : u32 { 68 // TODO(german77): Loop through all interfaces
89 NonInitialized = 0, 69 nfp_interface.Initialize();
90 Initialized = 1,
91 };
92 70
93 enum class DeviceState : u32 { 71 IPC::ResponseBuilder rb{ctx, 2, 0};
94 Initialized = 0, 72 rb.Push(ResultSuccess);
95 SearchingForTag = 1, 73}
96 TagFound = 2,
97 TagRemoved = 3,
98 TagNearby = 4,
99 Unknown5 = 5,
100 Finalized = 6
101 };
102 74
103 struct CommonInfo { 75void IUser::Finalize(Kernel::HLERequestContext& ctx) {
104 u16_be last_write_year; 76 LOG_INFO(Service_NFP, "called");
105 u8 last_write_month;
106 u8 last_write_day;
107 u16_be write_counter;
108 u16_be version;
109 u32_be application_area_size;
110 INSERT_PADDING_BYTES(0x34);
111 };
112 static_assert(sizeof(CommonInfo) == 0x40, "CommonInfo is an invalid size");
113 77
114 void Initialize(Kernel::HLERequestContext& ctx) { 78 state = State::NonInitialized;
115 LOG_DEBUG(Service_NFC, "called");
116 79
117 IPC::ResponseBuilder rb{ctx, 2, 0}; 80 // TODO(german77): Loop through all interfaces
118 rb.Push(ResultSuccess); 81 nfp_interface.Finalize();
119 82
120 state = State::Initialized; 83 IPC::ResponseBuilder rb{ctx, 2};
121 } 84 rb.Push(ResultSuccess);
85}
122 86
123 void GetState(Kernel::HLERequestContext& ctx) { 87void IUser::ListDevices(Kernel::HLERequestContext& ctx) {
124 LOG_DEBUG(Service_NFC, "called"); 88 LOG_INFO(Service_NFP, "called");
125 89
126 IPC::ResponseBuilder rb{ctx, 3, 0}; 90 std::vector<u64> devices;
127 rb.Push(ResultSuccess); 91
128 rb.PushRaw<u32>(static_cast<u32>(state)); 92 // TODO(german77): Loop through all interfaces
93 devices.push_back(nfp_interface.GetHandle());
94
95 ctx.WriteBuffer(devices);
96
97 IPC::ResponseBuilder rb{ctx, 3};
98 rb.Push(ResultSuccess);
99 rb.Push(static_cast<s32>(devices.size()));
100}
101
102void IUser::StartDetection(Kernel::HLERequestContext& ctx) {
103 IPC::RequestParser rp{ctx};
104 const auto device_handle{rp.Pop<u64>()};
105 const auto nfp_protocol{rp.Pop<s32>()};
106 LOG_INFO(Service_NFP, "called, device_handle={}, nfp_protocol={}", device_handle, nfp_protocol);
107
108 // TODO(german77): Loop through all interfaces
109 if (device_handle == nfp_interface.GetHandle()) {
110 const auto result = nfp_interface.StartDetection(nfp_protocol);
111 IPC::ResponseBuilder rb{ctx, 2};
112 rb.Push(result);
113 return;
129 } 114 }
130 115
131 void ListDevices(Kernel::HLERequestContext& ctx) { 116 LOG_ERROR(Service_NFP, "Handle not found, device_handle={}", device_handle);
132 IPC::RequestParser rp{ctx};
133 const u32 array_size = rp.Pop<u32>();
134 LOG_DEBUG(Service_NFP, "called, array_size={}", array_size);
135 117
136 ctx.WriteBuffer(device_handle); 118 IPC::ResponseBuilder rb{ctx, 2};
119 rb.Push(ErrCodes::DeviceNotFound);
120}
137 121
138 IPC::ResponseBuilder rb{ctx, 3}; 122void IUser::StopDetection(Kernel::HLERequestContext& ctx) {
139 rb.Push(ResultSuccess); 123 IPC::RequestParser rp{ctx};
140 rb.Push<u32>(1); 124 const auto device_handle{rp.Pop<u64>()};
125 LOG_INFO(Service_NFP, "called, device_handle={}", device_handle);
126
127 // TODO(german77): Loop through all interfaces
128 if (device_handle == nfp_interface.GetHandle()) {
129 const auto result = nfp_interface.StopDetection();
130 IPC::ResponseBuilder rb{ctx, 2};
131 rb.Push(result);
132 return;
141 } 133 }
142 134
143 void GetNpadId(Kernel::HLERequestContext& ctx) { 135 LOG_ERROR(Service_NFP, "Handle not found, device_handle={}", device_handle);
144 IPC::RequestParser rp{ctx};
145 const u64 dev_handle = rp.Pop<u64>();
146 LOG_DEBUG(Service_NFP, "called, dev_handle=0x{:X}", dev_handle);
147 136
148 IPC::ResponseBuilder rb{ctx, 3}; 137 IPC::ResponseBuilder rb{ctx, 2};
149 rb.Push(ResultSuccess); 138 rb.Push(ErrCodes::DeviceNotFound);
150 rb.Push<u32>(npad_id); 139}
140
141void IUser::Mount(Kernel::HLERequestContext& ctx) {
142 IPC::RequestParser rp{ctx};
143 const auto device_handle{rp.Pop<u64>()};
144 const auto model_type{rp.PopEnum<ModelType>()};
145 const auto mount_target{rp.PopEnum<MountTarget>()};
146 LOG_INFO(Service_NFP, "called, device_handle={}, model_type={}, mount_target={}", device_handle,
147 model_type, mount_target);
148
149 // TODO(german77): Loop through all interfaces
150 if (device_handle == nfp_interface.GetHandle()) {
151 const auto result = nfp_interface.Mount();
152 IPC::ResponseBuilder rb{ctx, 2};
153 rb.Push(result);
154 return;
151 } 155 }
152 156
153 void AttachActivateEvent(Kernel::HLERequestContext& ctx) { 157 LOG_ERROR(Service_NFP, "Handle not found, device_handle={}", device_handle);
154 IPC::RequestParser rp{ctx};
155 const u64 dev_handle = rp.Pop<u64>();
156 LOG_DEBUG(Service_NFP, "called, dev_handle=0x{:X}", dev_handle);
157 158
158 IPC::ResponseBuilder rb{ctx, 2, 1}; 159 IPC::ResponseBuilder rb{ctx, 2};
159 rb.Push(ResultSuccess); 160 rb.Push(ErrCodes::DeviceNotFound);
160 rb.PushCopyObjects(nfp_interface.GetNFCEvent()); 161}
161 has_attached_handle = true; 162
163void IUser::Unmount(Kernel::HLERequestContext& ctx) {
164 IPC::RequestParser rp{ctx};
165 const auto device_handle{rp.Pop<u64>()};
166 LOG_INFO(Service_NFP, "called, device_handle={}", device_handle);
167
168 // TODO(german77): Loop through all interfaces
169 if (device_handle == nfp_interface.GetHandle()) {
170 const auto result = nfp_interface.Unmount();
171 IPC::ResponseBuilder rb{ctx, 2};
172 rb.Push(result);
173 return;
162 } 174 }
163 175
164 void AttachDeactivateEvent(Kernel::HLERequestContext& ctx) { 176 LOG_ERROR(Service_NFP, "Handle not found, device_handle={}", device_handle);
165 IPC::RequestParser rp{ctx};
166 const u64 dev_handle = rp.Pop<u64>();
167 LOG_DEBUG(Service_NFP, "called, dev_handle=0x{:X}", dev_handle);
168 177
169 IPC::ResponseBuilder rb{ctx, 2, 1}; 178 IPC::ResponseBuilder rb{ctx, 2};
170 rb.Push(ResultSuccess); 179 rb.Push(ErrCodes::DeviceNotFound);
171 rb.PushCopyObjects(deactivate_event->GetReadableEvent()); 180}
172 } 181
173 182void IUser::OpenApplicationArea(Kernel::HLERequestContext& ctx) {
174 void StopDetection(Kernel::HLERequestContext& ctx) { 183 IPC::RequestParser rp{ctx};
175 LOG_DEBUG(Service_NFP, "called"); 184 const auto device_handle{rp.Pop<u64>()};
176 185 const auto access_id{rp.Pop<u32>()};
177 switch (device_state) { 186 LOG_WARNING(Service_NFP, "(STUBBED) called, device_handle={}, access_id={}", device_handle,
178 case DeviceState::TagFound: 187 access_id);
179 case DeviceState::TagNearby: 188
180 deactivate_event->GetWritableEvent().Signal(); 189 // TODO(german77): Loop through all interfaces
181 device_state = DeviceState::Initialized; 190 if (device_handle == nfp_interface.GetHandle()) {
182 break; 191 const auto result = nfp_interface.OpenApplicationArea(access_id);
183 case DeviceState::SearchingForTag:
184 case DeviceState::TagRemoved:
185 device_state = DeviceState::Initialized;
186 break;
187 default:
188 break;
189 }
190 IPC::ResponseBuilder rb{ctx, 2}; 192 IPC::ResponseBuilder rb{ctx, 2};
191 rb.Push(ResultSuccess); 193 rb.Push(result);
194 return;
192 } 195 }
193 196
194 void GetDeviceState(Kernel::HLERequestContext& ctx) { 197 LOG_ERROR(Service_NFP, "Handle not found, device_handle={}", device_handle);
195 LOG_DEBUG(Service_NFP, "called"); 198
199 IPC::ResponseBuilder rb{ctx, 2};
200 rb.Push(ErrCodes::DeviceNotFound);
201}
202
203void IUser::GetApplicationArea(Kernel::HLERequestContext& ctx) {
204 IPC::RequestParser rp{ctx};
205 const auto device_handle{rp.Pop<u64>()};
206 LOG_INFO(Service_NFP, "called, device_handle={}", device_handle);
196 207
208 // TODO(german77): Loop through all interfaces
209 if (device_handle == nfp_interface.GetHandle()) {
210 std::vector<u8> data{};
211 const auto result = nfp_interface.GetApplicationArea(data);
212 ctx.WriteBuffer(data);
197 IPC::ResponseBuilder rb{ctx, 3}; 213 IPC::ResponseBuilder rb{ctx, 3};
198 rb.Push(ResultSuccess); 214 rb.Push(result);
199 rb.Push<u32>(static_cast<u32>(device_state)); 215 rb.Push(static_cast<u32>(data.size()));
216 return;
200 } 217 }
201 218
202 void StartDetection(Kernel::HLERequestContext& ctx) { 219 LOG_ERROR(Service_NFP, "Handle not found, device_handle={}", device_handle);
203 LOG_DEBUG(Service_NFP, "called");
204 220
205 if (device_state == DeviceState::Initialized || device_state == DeviceState::TagRemoved) { 221 IPC::ResponseBuilder rb{ctx, 2};
206 device_state = DeviceState::SearchingForTag; 222 rb.Push(ErrCodes::DeviceNotFound);
207 } 223}
224
225void IUser::SetApplicationArea(Kernel::HLERequestContext& ctx) {
226 IPC::RequestParser rp{ctx};
227 const auto device_handle{rp.Pop<u64>()};
228 const auto data{ctx.ReadBuffer()};
229 LOG_WARNING(Service_NFP, "(STUBBED) called, device_handle={}, data_size={}", device_handle,
230 data.size());
231
232 // TODO(german77): Loop through all interfaces
233 if (device_handle == nfp_interface.GetHandle()) {
234 const auto result = nfp_interface.SetApplicationArea(data);
208 IPC::ResponseBuilder rb{ctx, 2}; 235 IPC::ResponseBuilder rb{ctx, 2};
209 rb.Push(ResultSuccess); 236 rb.Push(result);
237 return;
210 } 238 }
211 239
212 void GetTagInfo(Kernel::HLERequestContext& ctx) { 240 LOG_ERROR(Service_NFP, "Handle not found, device_handle={}", device_handle);
213 LOG_DEBUG(Service_NFP, "called");
214 241
242 IPC::ResponseBuilder rb{ctx, 2};
243 rb.Push(ErrCodes::DeviceNotFound);
244}
245
246void IUser::CreateApplicationArea(Kernel::HLERequestContext& ctx) {
247 IPC::RequestParser rp{ctx};
248 const auto device_handle{rp.Pop<u64>()};
249 const auto access_id{rp.Pop<u32>()};
250 const auto data{ctx.ReadBuffer()};
251 LOG_WARNING(Service_NFP, "(STUBBED) called, device_handle={}, data_size={}, access_id={}",
252 device_handle, access_id, data.size());
253
254 // TODO(german77): Loop through all interfaces
255 if (device_handle == nfp_interface.GetHandle()) {
256 const auto result = nfp_interface.CreateApplicationArea(access_id, data);
215 IPC::ResponseBuilder rb{ctx, 2}; 257 IPC::ResponseBuilder rb{ctx, 2};
216 const auto& amiibo = nfp_interface.GetAmiiboBuffer(); 258 rb.Push(result);
217 const TagInfo tag_info{ 259 return;
218 .uuid = amiibo.uuid,
219 .uuid_length = static_cast<u8>(amiibo.uuid.size()),
220 .padding_1 = {},
221 .protocol = 1, // TODO(ogniK): Figure out actual values
222 .tag_type = 2,
223 .padding_2 = {},
224 };
225 ctx.WriteBuffer(tag_info);
226 rb.Push(ResultSuccess);
227 } 260 }
228 261
229 void Mount(Kernel::HLERequestContext& ctx) { 262 LOG_ERROR(Service_NFP, "Handle not found, device_handle={}", device_handle);
230 LOG_DEBUG(Service_NFP, "called"); 263
264 IPC::ResponseBuilder rb{ctx, 2};
265 rb.Push(ErrCodes::DeviceNotFound);
266}
267
268void IUser::GetTagInfo(Kernel::HLERequestContext& ctx) {
269 IPC::RequestParser rp{ctx};
270 const auto device_handle{rp.Pop<u64>()};
271 LOG_INFO(Service_NFP, "called, device_handle={}", device_handle);
231 272
232 device_state = DeviceState::TagNearby; 273 // TODO(german77): Loop through all interfaces
274 if (device_handle == nfp_interface.GetHandle()) {
275 TagInfo tag_info{};
276 const auto result = nfp_interface.GetTagInfo(tag_info);
277 ctx.WriteBuffer(tag_info);
233 IPC::ResponseBuilder rb{ctx, 2}; 278 IPC::ResponseBuilder rb{ctx, 2};
234 rb.Push(ResultSuccess); 279 rb.Push(result);
280 return;
235 } 281 }
236 282
237 void GetModelInfo(Kernel::HLERequestContext& ctx) { 283 LOG_ERROR(Service_NFP, "Handle not found, device_handle={}", device_handle);
238 LOG_DEBUG(Service_NFP, "called");
239 284
285 IPC::ResponseBuilder rb{ctx, 2};
286 rb.Push(ErrCodes::DeviceNotFound);
287}
288
289void IUser::GetRegisterInfo(Kernel::HLERequestContext& ctx) {
290 IPC::RequestParser rp{ctx};
291 const auto device_handle{rp.Pop<u64>()};
292 LOG_INFO(Service_NFP, "called, device_handle={}", device_handle);
293
294 // TODO(german77): Loop through all interfaces
295 if (device_handle == nfp_interface.GetHandle()) {
296 RegisterInfo register_info{};
297 const auto result = nfp_interface.GetRegisterInfo(register_info);
298 ctx.WriteBuffer(register_info);
240 IPC::ResponseBuilder rb{ctx, 2}; 299 IPC::ResponseBuilder rb{ctx, 2};
241 const auto& amiibo = nfp_interface.GetAmiiboBuffer(); 300 rb.Push(result);
242 ctx.WriteBuffer(amiibo.model_info); 301 return;
243 rb.Push(ResultSuccess);
244 } 302 }
245 303
246 void Unmount(Kernel::HLERequestContext& ctx) { 304 LOG_ERROR(Service_NFP, "Handle not found, device_handle={}", device_handle);
247 LOG_DEBUG(Service_NFP, "called");
248 305
249 device_state = DeviceState::TagFound; 306 IPC::ResponseBuilder rb{ctx, 2};
307 rb.Push(ErrCodes::DeviceNotFound);
308}
250 309
310void IUser::GetCommonInfo(Kernel::HLERequestContext& ctx) {
311 IPC::RequestParser rp{ctx};
312 const auto device_handle{rp.Pop<u64>()};
313 LOG_INFO(Service_NFP, "called, device_handle={}", device_handle);
314
315 // TODO(german77): Loop through all interfaces
316 if (device_handle == nfp_interface.GetHandle()) {
317 CommonInfo common_info{};
318 const auto result = nfp_interface.GetCommonInfo(common_info);
319 ctx.WriteBuffer(common_info);
251 IPC::ResponseBuilder rb{ctx, 2}; 320 IPC::ResponseBuilder rb{ctx, 2};
252 rb.Push(ResultSuccess); 321 rb.Push(result);
322 return;
253 } 323 }
254 324
255 void Finalize(Kernel::HLERequestContext& ctx) { 325 LOG_ERROR(Service_NFP, "Handle not found, device_handle={}", device_handle);
256 LOG_DEBUG(Service_NFP, "called"); 326
327 IPC::ResponseBuilder rb{ctx, 2};
328 rb.Push(ErrCodes::DeviceNotFound);
329}
257 330
258 device_state = DeviceState::Finalized; 331void IUser::GetModelInfo(Kernel::HLERequestContext& ctx) {
332 IPC::RequestParser rp{ctx};
333 const auto device_handle{rp.Pop<u64>()};
334 LOG_INFO(Service_NFP, "called, device_handle={}", device_handle);
259 335
336 // TODO(german77): Loop through all interfaces
337 if (device_handle == nfp_interface.GetHandle()) {
338 ModelInfo model_info{};
339 const auto result = nfp_interface.GetModelInfo(model_info);
340 ctx.WriteBuffer(model_info);
260 IPC::ResponseBuilder rb{ctx, 2}; 341 IPC::ResponseBuilder rb{ctx, 2};
261 rb.Push(ResultSuccess); 342 rb.Push(result);
343 return;
262 } 344 }
263 345
264 void AttachAvailabilityChangeEvent(Kernel::HLERequestContext& ctx) { 346 LOG_ERROR(Service_NFP, "Handle not found, device_handle={}", device_handle);
265 LOG_WARNING(Service_NFP, "(STUBBED) called"); 347
348 IPC::ResponseBuilder rb{ctx, 2};
349 rb.Push(ErrCodes::DeviceNotFound);
350}
266 351
352void IUser::AttachActivateEvent(Kernel::HLERequestContext& ctx) {
353 IPC::RequestParser rp{ctx};
354 const auto device_handle{rp.Pop<u64>()};
355 LOG_DEBUG(Service_NFP, "called, device_handle={}", device_handle);
356
357 // TODO(german77): Loop through all interfaces
358 if (device_handle == nfp_interface.GetHandle()) {
267 IPC::ResponseBuilder rb{ctx, 2, 1}; 359 IPC::ResponseBuilder rb{ctx, 2, 1};
268 rb.Push(ResultSuccess); 360 rb.Push(ResultSuccess);
269 rb.PushCopyObjects(availability_change_event->GetReadableEvent()); 361 rb.PushCopyObjects(nfp_interface.GetActivateEvent());
362 return;
270 } 363 }
271 364
272 void GetRegisterInfo(Kernel::HLERequestContext& ctx) { 365 LOG_ERROR(Service_NFP, "Handle not found, device_handle={}", device_handle);
273 LOG_WARNING(Service_NFP, "(STUBBED) called");
274 366
275 // TODO(ogniK): Pull Mii and owner data from amiibo 367 IPC::ResponseBuilder rb{ctx, 2};
368 rb.Push(ErrCodes::DeviceNotFound);
369}
276 370
277 IPC::ResponseBuilder rb{ctx, 2}; 371void IUser::AttachDeactivateEvent(Kernel::HLERequestContext& ctx) {
372 IPC::RequestParser rp{ctx};
373 const auto device_handle{rp.Pop<u64>()};
374 LOG_DEBUG(Service_NFP, "called, device_handle={}", device_handle);
375
376 // TODO(german77): Loop through all interfaces
377 if (device_handle == nfp_interface.GetHandle()) {
378 IPC::ResponseBuilder rb{ctx, 2, 1};
278 rb.Push(ResultSuccess); 379 rb.Push(ResultSuccess);
380 rb.PushCopyObjects(nfp_interface.GetDeactivateEvent());
381 return;
279 } 382 }
280 383
281 void GetCommonInfo(Kernel::HLERequestContext& ctx) { 384 LOG_ERROR(Service_NFP, "Handle not found, device_handle={}", device_handle);
282 LOG_WARNING(Service_NFP, "(STUBBED) called");
283 385
284 // TODO(ogniK): Pull common information from amiibo 386 IPC::ResponseBuilder rb{ctx, 2};
387 rb.Push(ErrCodes::DeviceNotFound);
388}
285 389
286 CommonInfo common_info{}; 390void IUser::GetState(Kernel::HLERequestContext& ctx) {
287 common_info.application_area_size = 0; 391 LOG_DEBUG(Service_NFC, "called");
288 ctx.WriteBuffer(common_info);
289 392
290 IPC::ResponseBuilder rb{ctx, 2}; 393 IPC::ResponseBuilder rb{ctx, 3, 0};
394 rb.Push(ResultSuccess);
395 rb.PushEnum(state);
396}
397
398void IUser::GetDeviceState(Kernel::HLERequestContext& ctx) {
399 IPC::RequestParser rp{ctx};
400 const auto device_handle{rp.Pop<u64>()};
401 LOG_DEBUG(Service_NFP, "called, device_handle={}", device_handle);
402
403 // TODO(german77): Loop through all interfaces
404 if (device_handle == nfp_interface.GetHandle()) {
405 IPC::ResponseBuilder rb{ctx, 3};
291 rb.Push(ResultSuccess); 406 rb.Push(ResultSuccess);
407 rb.PushEnum(nfp_interface.GetCurrentState());
408 return;
292 } 409 }
293 410
294 void OpenApplicationArea(Kernel::HLERequestContext& ctx) { 411 LOG_ERROR(Service_NFP, "Handle not found, device_handle={}", device_handle);
295 LOG_WARNING(Service_NFP, "(STUBBED) called");
296 IPC::ResponseBuilder rb{ctx, 2};
297 rb.Push(ErrCodes::ERR_NO_APPLICATION_AREA);
298 }
299 412
300 void GetApplicationAreaSize(Kernel::HLERequestContext& ctx) { 413 IPC::ResponseBuilder rb{ctx, 2};
301 LOG_WARNING(Service_NFP, "(STUBBED) called"); 414 rb.Push(ErrCodes::DeviceNotFound);
302 // We don't need to worry about this since we can just open the file 415}
416
417void IUser::GetNpadId(Kernel::HLERequestContext& ctx) {
418 IPC::RequestParser rp{ctx};
419 const auto device_handle{rp.Pop<u64>()};
420 LOG_DEBUG(Service_NFP, "called, device_handle={}", device_handle);
421
422 // TODO(german77): Loop through all interfaces
423 if (device_handle == nfp_interface.GetHandle()) {
303 IPC::ResponseBuilder rb{ctx, 3}; 424 IPC::ResponseBuilder rb{ctx, 3};
304 rb.Push(ResultSuccess); 425 rb.Push(ResultSuccess);
305 rb.PushRaw<u32>(0); // This is from the GetCommonInfo stub 426 rb.PushEnum(nfp_interface.GetNpadId());
427 return;
306 } 428 }
307 429
308 void GetApplicationArea(Kernel::HLERequestContext& ctx) { 430 LOG_ERROR(Service_NFP, "Handle not found, device_handle={}", device_handle);
309 LOG_WARNING(Service_NFP, "(STUBBED) called"); 431
432 IPC::ResponseBuilder rb{ctx, 2};
433 rb.Push(ErrCodes::DeviceNotFound);
434}
310 435
311 // TODO(ogniK): Pull application area from amiibo 436void IUser::GetApplicationAreaSize(Kernel::HLERequestContext& ctx) {
437 IPC::RequestParser rp{ctx};
438 const auto device_handle{rp.Pop<u64>()};
439 LOG_DEBUG(Service_NFP, "called, device_handle={}", device_handle);
312 440
441 // TODO(german77): Loop through all interfaces
442 if (device_handle == nfp_interface.GetHandle()) {
313 IPC::ResponseBuilder rb{ctx, 3}; 443 IPC::ResponseBuilder rb{ctx, 3};
314 rb.Push(ResultSuccess); 444 rb.Push(ResultSuccess);
315 rb.PushRaw<u32>(0); // This is from the GetCommonInfo stub 445 rb.Push(ApplicationAreaSize);
446 return;
316 } 447 }
317 448
318 Module::Interface& nfp_interface; 449 LOG_ERROR(Service_NFP, "Handle not found, device_handle={}", device_handle);
319 KernelHelpers::ServiceContext& service_context; 450
451 IPC::ResponseBuilder rb{ctx, 2};
452 rb.Push(ErrCodes::DeviceNotFound);
453}
454
455void IUser::AttachAvailabilityChangeEvent(Kernel::HLERequestContext& ctx) {
456 LOG_DEBUG(Service_NFP, "(STUBBED) called");
320 457
321 bool has_attached_handle{}; 458 IPC::ResponseBuilder rb{ctx, 2, 1};
322 const u64 device_handle{0}; // Npad device 1 459 rb.Push(ResultSuccess);
323 const u32 npad_id{0}; // Player 1 controller 460 rb.PushCopyObjects(availability_change_event->GetReadableEvent());
324 State state{State::NonInitialized}; 461}
325 DeviceState device_state{DeviceState::Initialized}; 462
326 Kernel::KEvent* deactivate_event; 463Module::Interface::Interface(std::shared_ptr<Module> module_, Core::System& system_,
327 Kernel::KEvent* availability_change_event; 464 const char* name)
328}; 465 : ServiceFramework{system_, name}, module{std::move(module_)},
466 npad_id{Core::HID::NpadIdType::Player1}, service_context{system_, service_name} {
467 activate_event = service_context.CreateEvent("IUser:NFPActivateEvent");
468 deactivate_event = service_context.CreateEvent("IUser:NFPDeactivateEvent");
469}
470
471Module::Interface::~Interface() = default;
329 472
330void Module::Interface::CreateUserInterface(Kernel::HLERequestContext& ctx) { 473void Module::Interface::CreateUserInterface(Kernel::HLERequestContext& ctx) {
331 LOG_DEBUG(Service_NFP, "called"); 474 LOG_DEBUG(Service_NFP, "called");
332 475
333 IPC::ResponseBuilder rb{ctx, 2, 0, 1}; 476 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
334 rb.Push(ResultSuccess); 477 rb.Push(ResultSuccess);
335 rb.PushIpcInterface<IUser>(*this, system, service_context); 478 rb.PushIpcInterface<IUser>(*this, system);
336} 479}
337 480
338bool Module::Interface::LoadAmiibo(const std::vector<u8>& buffer) { 481bool Module::Interface::LoadAmiibo(const std::vector<u8>& buffer) {
339 if (buffer.size() < sizeof(AmiiboFile)) { 482 if (device_state != DeviceState::SearchingForTag) {
483 LOG_ERROR(Service_NFP, "Game is not looking for amiibos, current state {}", device_state);
484 return false;
485 }
486
487 constexpr auto tag_size = sizeof(NTAG215File);
488 constexpr auto tag_size_without_password = sizeof(NTAG215File) - sizeof(NTAG215Password);
489
490 std::vector<u8> amiibo_buffer = buffer;
491
492 if (amiibo_buffer.size() < tag_size_without_password) {
493 LOG_ERROR(Service_NFP, "Wrong file size {}", buffer.size());
494 return false;
495 }
496
497 // Ensure it has the correct size
498 if (amiibo_buffer.size() != tag_size) {
499 amiibo_buffer.resize(tag_size, 0);
500 }
501
502 LOG_INFO(Service_NFP, "Amiibo detected");
503 std::memcpy(&tag_data, buffer.data(), tag_size);
504
505 if (!IsAmiiboValid()) {
340 return false; 506 return false;
341 } 507 }
342 508
343 std::memcpy(&amiibo, buffer.data(), sizeof(amiibo)); 509 // This value can't be dumped from a tag. Generate it
344 nfc_tag_load->GetWritableEvent().Signal(); 510 tag_data.password.PWD = GetTagPassword(tag_data.uuid);
511
512 device_state = DeviceState::TagFound;
513 activate_event->GetWritableEvent().Signal();
345 return true; 514 return true;
346} 515}
347 516
348Kernel::KReadableEvent& Module::Interface::GetNFCEvent() { 517void Module::Interface::CloseAmiibo() {
349 return nfc_tag_load->GetReadableEvent(); 518 LOG_INFO(Service_NFP, "Remove amiibo");
519 device_state = DeviceState::TagRemoved;
520 is_application_area_initialized = false;
521 application_area_id = 0;
522 application_area_data.clear();
523 deactivate_event->GetWritableEvent().Signal();
524}
525
526bool Module::Interface::IsAmiiboValid() const {
527 const auto& amiibo_data = tag_data.user_memory;
528 LOG_DEBUG(Service_NFP, "uuid_lock=0x{0:x}", tag_data.lock_bytes);
529 LOG_DEBUG(Service_NFP, "compability_container=0x{0:x}", tag_data.compability_container);
530 LOG_DEBUG(Service_NFP, "crypto_init=0x{0:x}", amiibo_data.crypto_init);
531 LOG_DEBUG(Service_NFP, "write_count={}", amiibo_data.write_count);
532
533 LOG_DEBUG(Service_NFP, "character_id=0x{0:x}", amiibo_data.model_info.character_id);
534 LOG_DEBUG(Service_NFP, "character_variant={}", amiibo_data.model_info.character_variant);
535 LOG_DEBUG(Service_NFP, "amiibo_type={}", amiibo_data.model_info.amiibo_type);
536 LOG_DEBUG(Service_NFP, "model_number=0x{0:x}", amiibo_data.model_info.model_number);
537 LOG_DEBUG(Service_NFP, "series={}", amiibo_data.model_info.series);
538 LOG_DEBUG(Service_NFP, "fixed_value=0x{0:x}", amiibo_data.model_info.fixed);
539
540 LOG_DEBUG(Service_NFP, "tag_dynamic_lock=0x{0:x}", tag_data.dynamic_lock);
541 LOG_DEBUG(Service_NFP, "tag_CFG0=0x{0:x}", tag_data.CFG0);
542 LOG_DEBUG(Service_NFP, "tag_CFG1=0x{0:x}", tag_data.CFG1);
543
544 // Check against all know constants on an amiibo binary
545 if (tag_data.lock_bytes != 0xE00F) {
546 return false;
547 }
548 if (tag_data.compability_container != 0xEEFF10F1U) {
549 return false;
550 }
551 if ((amiibo_data.crypto_init & 0xFF) != 0xA5) {
552 return false;
553 }
554 if (amiibo_data.model_info.fixed != 0x02) {
555 return false;
556 }
557 if ((tag_data.dynamic_lock & 0xFFFFFF) != 0x0F0001) {
558 return false;
559 }
560 if (tag_data.CFG0 != 0x04000000U) {
561 return false;
562 }
563 if (tag_data.CFG1 != 0x5F) {
564 return false;
565 }
566 return true;
567}
568
569Kernel::KReadableEvent& Module::Interface::GetActivateEvent() const {
570 return activate_event->GetReadableEvent();
571}
572
573Kernel::KReadableEvent& Module::Interface::GetDeactivateEvent() const {
574 return deactivate_event->GetReadableEvent();
575}
576
577void Module::Interface::Initialize() {
578 device_state = DeviceState::Initialized;
579}
580
581void Module::Interface::Finalize() {
582 device_state = DeviceState::Unaviable;
583 is_application_area_initialized = false;
584 application_area_id = 0;
585 application_area_data.clear();
586}
587
588ResultCode Module::Interface::StartDetection(s32 protocol_) {
589 auto npad_device = system.HIDCore().GetEmulatedController(npad_id);
590
591 // TODO(german77): Add callback for when nfc data is available
592
593 if (device_state == DeviceState::Initialized || device_state == DeviceState::TagRemoved) {
594 npad_device->SetPollingMode(Common::Input::PollingMode::NFC);
595 device_state = DeviceState::SearchingForTag;
596 protocol = protocol_;
597 return ResultSuccess;
598 }
599
600 LOG_ERROR(Service_NFP, "Wrong device state {}", device_state);
601 return ErrCodes::WrongDeviceState;
602}
603
604ResultCode Module::Interface::StopDetection() {
605 auto npad_device = system.HIDCore().GetEmulatedController(npad_id);
606 npad_device->SetPollingMode(Common::Input::PollingMode::Active);
607
608 if (device_state == DeviceState::TagFound || device_state == DeviceState::TagMounted) {
609 CloseAmiibo();
610 return ResultSuccess;
611 }
612 if (device_state == DeviceState::SearchingForTag || device_state == DeviceState::TagRemoved) {
613 device_state = DeviceState::Initialized;
614 return ResultSuccess;
615 }
616
617 LOG_ERROR(Service_NFP, "Wrong device state {}", device_state);
618 return ErrCodes::WrongDeviceState;
619}
620
621ResultCode Module::Interface::Mount() {
622 if (device_state == DeviceState::TagFound) {
623 device_state = DeviceState::TagMounted;
624 return ResultSuccess;
625 }
626
627 LOG_ERROR(Service_NFP, "Wrong device state {}", device_state);
628 return ErrCodes::WrongDeviceState;
629}
630
631ResultCode Module::Interface::Unmount() {
632 if (device_state == DeviceState::TagMounted) {
633 is_application_area_initialized = false;
634 application_area_id = 0;
635 application_area_data.clear();
636 device_state = DeviceState::TagFound;
637 return ResultSuccess;
638 }
639
640 LOG_ERROR(Service_NFP, "Wrong device state {}", device_state);
641 return ErrCodes::WrongDeviceState;
642}
643
644ResultCode Module::Interface::GetTagInfo(TagInfo& tag_info) const {
645 if (device_state == DeviceState::TagFound || device_state == DeviceState::TagMounted) {
646 tag_info = {
647 .uuid = tag_data.uuid,
648 .uuid_length = static_cast<u8>(tag_data.uuid.size()),
649 .protocol = protocol,
650 .tag_type = static_cast<u32>(tag_data.user_memory.model_info.amiibo_type),
651 };
652 return ResultSuccess;
653 }
654
655 LOG_ERROR(Service_NFP, "Wrong device state {}", device_state);
656 return ErrCodes::WrongDeviceState;
657}
658
659ResultCode Module::Interface::GetCommonInfo(CommonInfo& common_info) const {
660 if (device_state != DeviceState::TagMounted) {
661 LOG_ERROR(Service_NFP, "Wrong device state {}", device_state);
662 return ErrCodes::WrongDeviceState;
663 }
664
665 // Read this data from the amiibo save file
666 common_info = {
667 .last_write_year = 2022,
668 .last_write_month = 2,
669 .last_write_day = 7,
670 .write_counter = tag_data.user_memory.write_count,
671 .version = 1,
672 .application_area_size = ApplicationAreaSize,
673 };
674 return ResultSuccess;
675}
676
677ResultCode Module::Interface::GetModelInfo(ModelInfo& model_info) const {
678 if (device_state != DeviceState::TagMounted) {
679 LOG_ERROR(Service_NFP, "Wrong device state {}", device_state);
680 return ErrCodes::WrongDeviceState;
681 }
682
683 model_info = tag_data.user_memory.model_info;
684 return ResultSuccess;
685}
686
687ResultCode Module::Interface::GetRegisterInfo(RegisterInfo& register_info) const {
688 if (device_state != DeviceState::TagMounted) {
689 LOG_ERROR(Service_NFP, "Wrong device state {}", device_state);
690 return ErrCodes::WrongDeviceState;
691 }
692
693 Service::Mii::MiiManager manager;
694
695 // Read this data from the amiibo save file
696 register_info = {
697 .mii_char_info = manager.BuildDefault(0),
698 .first_write_year = 2022,
699 .first_write_month = 2,
700 .first_write_day = 7,
701 .amiibo_name = {'Y', 'u', 'z', 'u', 'A', 'm', 'i', 'i', 'b', 'o', 0},
702 .unknown = {},
703 };
704 return ResultSuccess;
705}
706
707ResultCode Module::Interface::OpenApplicationArea(u32 access_id) {
708 if (device_state != DeviceState::TagMounted) {
709 LOG_ERROR(Service_NFP, "Wrong device state {}", device_state);
710 return ErrCodes::WrongDeviceState;
711 }
712 if (AmiiboApplicationDataExist(access_id)) {
713 application_area_data = LoadAmiiboApplicationData(access_id);
714 application_area_id = access_id;
715 is_application_area_initialized = true;
716 }
717 if (!is_application_area_initialized) {
718 LOG_WARNING(Service_NFP, "Application area is not initialized");
719 return ErrCodes::ApplicationAreaIsNotInitialized;
720 }
721 return ResultSuccess;
722}
723
724ResultCode Module::Interface::GetApplicationArea(std::vector<u8>& data) const {
725 if (device_state != DeviceState::TagMounted) {
726 LOG_ERROR(Service_NFP, "Wrong device state {}", device_state);
727 return ErrCodes::WrongDeviceState;
728 }
729 if (!is_application_area_initialized) {
730 LOG_ERROR(Service_NFP, "Application area is not initialized");
731 return ErrCodes::ApplicationAreaIsNotInitialized;
732 }
733
734 data = application_area_data;
735
736 return ResultSuccess;
737}
738
739ResultCode Module::Interface::SetApplicationArea(const std::vector<u8>& data) {
740 if (device_state != DeviceState::TagMounted) {
741 LOG_ERROR(Service_NFP, "Wrong device state {}", device_state);
742 return ErrCodes::WrongDeviceState;
743 }
744 if (!is_application_area_initialized) {
745 LOG_ERROR(Service_NFP, "Application area is not initialized");
746 return ErrCodes::ApplicationAreaIsNotInitialized;
747 }
748 application_area_data = data;
749 SaveAmiiboApplicationData(application_area_id, application_area_data);
750 return ResultSuccess;
751}
752
753ResultCode Module::Interface::CreateApplicationArea(u32 access_id, const std::vector<u8>& data) {
754 if (device_state != DeviceState::TagMounted) {
755 LOG_ERROR(Service_NFP, "Wrong device state {}", device_state);
756 return ErrCodes::WrongDeviceState;
757 }
758 if (AmiiboApplicationDataExist(access_id)) {
759 LOG_ERROR(Service_NFP, "Application area already exist");
760 return ErrCodes::ApplicationAreaExist;
761 }
762 application_area_data = data;
763 application_area_id = access_id;
764 SaveAmiiboApplicationData(application_area_id, application_area_data);
765 return ResultSuccess;
766}
767
768bool Module::Interface::AmiiboApplicationDataExist(u32 access_id) const {
769 // TODO(german77): Check if file exist
770 return false;
771}
772
773std::vector<u8> Module::Interface::LoadAmiiboApplicationData(u32 access_id) const {
774 // TODO(german77): Read file
775 std::vector<u8> data(ApplicationAreaSize);
776 return data;
777}
778
779void Module::Interface::SaveAmiiboApplicationData(u32 access_id,
780 const std::vector<u8>& data) const {
781 // TODO(german77): Save file
782}
783
784u64 Module::Interface::GetHandle() const {
785 // Generate a handle based of the npad id
786 return static_cast<u64>(npad_id);
787}
788
789DeviceState Module::Interface::GetCurrentState() const {
790 return device_state;
791}
792
793Core::HID::NpadIdType Module::Interface::GetNpadId() const {
794 return npad_id;
350} 795}
351 796
352const Module::Interface::AmiiboFile& Module::Interface::GetAmiiboBuffer() const { 797u32 Module::Interface::GetTagPassword(const TagUuid& uuid) const {
353 return amiibo; 798 // Verifiy that the generated password is correct
799 u32 password = 0xAA ^ (uuid[1] ^ uuid[3]);
800 password &= (0x55 ^ (uuid[2] ^ uuid[4])) << 8;
801 password &= (0xAA ^ (uuid[3] ^ uuid[5])) << 16;
802 password &= (0x55 ^ (uuid[4] ^ uuid[6])) << 24;
803 return password;
354} 804}
355 805
356void InstallInterfaces(SM::ServiceManager& service_manager, Core::System& system) { 806void InstallInterfaces(SM::ServiceManager& service_manager, Core::System& system) {
diff --git a/src/core/hle/service/nfp/nfp.h b/src/core/hle/service/nfp/nfp.h
index 95c127efb..022f13b29 100644
--- a/src/core/hle/service/nfp/nfp.h
+++ b/src/core/hle/service/nfp/nfp.h
@@ -7,15 +7,132 @@
7#include <array> 7#include <array>
8#include <vector> 8#include <vector>
9 9
10#include "common/common_funcs.h"
10#include "core/hle/service/kernel_helpers.h" 11#include "core/hle/service/kernel_helpers.h"
12#include "core/hle/service/mii/mii_manager.h"
11#include "core/hle/service/service.h" 13#include "core/hle/service/service.h"
12 14
13namespace Kernel { 15namespace Kernel {
14class KEvent; 16class KEvent;
15} 17class KReadableEvent;
18} // namespace Kernel
19
20namespace Core::HID {
21enum class NpadIdType : u32;
22} // namespace Core::HID
16 23
17namespace Service::NFP { 24namespace Service::NFP {
18 25
26enum class ServiceType : u32 {
27 User,
28 Debug,
29 System,
30};
31
32enum class State : u32 {
33 NonInitialized,
34 Initialized,
35};
36
37enum class DeviceState : u32 {
38 Initialized,
39 SearchingForTag,
40 TagFound,
41 TagRemoved,
42 TagMounted,
43 Unaviable,
44 Finalized,
45};
46
47enum class ModelType : u32 {
48 Amiibo,
49};
50
51enum class MountTarget : u32 {
52 Rom,
53 Ram,
54 All,
55};
56
57enum class AmiiboType : u8 {
58 Figure,
59 Card,
60 Yarn,
61};
62
63enum class AmiiboSeries : u8 {
64 SuperSmashBros,
65 SuperMario,
66 ChibiRobo,
67 YoshiWoollyWorld,
68 Splatoon,
69 AnimalCrossing,
70 EightBitMario,
71 Skylanders,
72 Unknown8,
73 TheLegendOfZelda,
74 ShovelKnight,
75 Unknown11,
76 Kiby,
77 Pokemon,
78 MarioSportsSuperstars,
79 MonsterHunter,
80 BoxBoy,
81 Pikmin,
82 FireEmblem,
83 Metroid,
84 Others,
85 MegaMan,
86 Diablo
87};
88
89using TagUuid = std::array<u8, 10>;
90
91struct TagInfo {
92 TagUuid uuid;
93 u8 uuid_length;
94 INSERT_PADDING_BYTES(0x15);
95 s32 protocol;
96 u32 tag_type;
97 INSERT_PADDING_BYTES(0x30);
98};
99static_assert(sizeof(TagInfo) == 0x58, "TagInfo is an invalid size");
100
101struct CommonInfo {
102 u16 last_write_year;
103 u8 last_write_month;
104 u8 last_write_day;
105 u16 write_counter;
106 u16 version;
107 u32 application_area_size;
108 INSERT_PADDING_BYTES(0x34);
109};
110static_assert(sizeof(CommonInfo) == 0x40, "CommonInfo is an invalid size");
111
112struct ModelInfo {
113 u16 character_id;
114 u8 character_variant;
115 AmiiboType amiibo_type;
116 u16 model_number;
117 AmiiboSeries series;
118 u8 fixed; // Must be 02
119 INSERT_PADDING_BYTES(0x4); // Unknown
120 INSERT_PADDING_BYTES(0x20); // Probably a SHA256-(HMAC?) hash
121 INSERT_PADDING_BYTES(0x14); // SHA256-HMAC
122};
123static_assert(sizeof(ModelInfo) == 0x40, "ModelInfo is an invalid size");
124
125struct RegisterInfo {
126 Service::Mii::MiiInfo mii_char_info;
127 u16 first_write_year;
128 u8 first_write_month;
129 u8 first_write_day;
130 std::array<u8, 11> amiibo_name;
131 u8 unknown;
132 INSERT_PADDING_BYTES(0x98);
133};
134static_assert(sizeof(RegisterInfo) == 0x100, "RegisterInfo is an invalid size");
135
19class Module final { 136class Module final {
20public: 137public:
21 class Interface : public ServiceFramework<Interface> { 138 class Interface : public ServiceFramework<Interface> {
@@ -24,34 +141,131 @@ public:
24 const char* name); 141 const char* name);
25 ~Interface() override; 142 ~Interface() override;
26 143
27 struct ModelInfo { 144 struct EncryptedAmiiboFile {
28 std::array<u8, 0x8> amiibo_identification_block; 145 u16 crypto_init; // Must be A5 XX
29 INSERT_PADDING_BYTES(0x38); 146 u16 write_count; // Number of times the amiibo has been written?
147 INSERT_PADDING_BYTES(0x20); // System crypts
148 INSERT_PADDING_BYTES(0x20); // SHA256-(HMAC?) hash
149 ModelInfo model_info; // This struct is bigger than documentation
150 INSERT_PADDING_BYTES(0xC); // SHA256-HMAC
151 INSERT_PADDING_BYTES(0x114); // section 1 encrypted buffer
152 INSERT_PADDING_BYTES(0x54); // section 2 encrypted buffer
153 };
154 static_assert(sizeof(EncryptedAmiiboFile) == 0x1F8, "AmiiboFile is an invalid size");
155
156 struct NTAG215Password {
157 u32 PWD; // Password to allow write access
158 u16 PACK; // Password acknowledge reply
159 u16 RFUI; // Reserved for future use
30 }; 160 };
31 static_assert(sizeof(ModelInfo) == 0x40, "ModelInfo is an invalid size"); 161 static_assert(sizeof(NTAG215Password) == 0x8, "NTAG215Password is an invalid size");
32 162
33 struct AmiiboFile { 163 struct NTAG215File {
34 std::array<u8, 10> uuid; 164 TagUuid uuid; // Unique serial number
35 INSERT_PADDING_BYTES(0x4a); 165 u16 lock_bytes; // Set defined pages as read only
36 ModelInfo model_info; 166 u32 compability_container; // Defines available memory
167 EncryptedAmiiboFile user_memory; // Writable data
168 u32 dynamic_lock; // Dynamic lock
169 u32 CFG0; // Defines memory protected by password
170 u32 CFG1; // Defines number of verification attempts
171 NTAG215Password password; // Password data
37 }; 172 };
38 static_assert(sizeof(AmiiboFile) == 0x94, "AmiiboFile is an invalid size"); 173 static_assert(sizeof(NTAG215File) == 0x21C, "NTAG215File is an invalid size");
39 174
40 void CreateUserInterface(Kernel::HLERequestContext& ctx); 175 void CreateUserInterface(Kernel::HLERequestContext& ctx);
41 bool LoadAmiibo(const std::vector<u8>& buffer); 176 bool LoadAmiibo(const std::vector<u8>& buffer);
42 Kernel::KReadableEvent& GetNFCEvent(); 177 void CloseAmiibo();
43 const AmiiboFile& GetAmiiboBuffer() const; 178
179 void Initialize();
180 void Finalize();
181
182 ResultCode StartDetection(s32 protocol_);
183 ResultCode StopDetection();
184 ResultCode Mount();
185 ResultCode Unmount();
186
187 ResultCode GetTagInfo(TagInfo& tag_info) const;
188 ResultCode GetCommonInfo(CommonInfo& common_info) const;
189 ResultCode GetModelInfo(ModelInfo& model_info) const;
190 ResultCode GetRegisterInfo(RegisterInfo& register_info) const;
191
192 ResultCode OpenApplicationArea(u32 access_id);
193 ResultCode GetApplicationArea(std::vector<u8>& data) const;
194 ResultCode SetApplicationArea(const std::vector<u8>& data);
195 ResultCode CreateApplicationArea(u32 access_id, const std::vector<u8>& data);
196
197 u64 GetHandle() const;
198 DeviceState GetCurrentState() const;
199 Core::HID::NpadIdType GetNpadId() const;
200
201 Kernel::KReadableEvent& GetActivateEvent() const;
202 Kernel::KReadableEvent& GetDeactivateEvent() const;
44 203
45 protected: 204 protected:
46 std::shared_ptr<Module> module; 205 std::shared_ptr<Module> module;
47 206
48 private: 207 private:
208 /// Validates that the amiibo file is not corrupted
209 bool IsAmiiboValid() const;
210
211 bool AmiiboApplicationDataExist(u32 access_id) const;
212 std::vector<u8> LoadAmiiboApplicationData(u32 access_id) const;
213 void SaveAmiiboApplicationData(u32 access_id, const std::vector<u8>& data) const;
214
215 /// return password needed to allow write access to protected memory
216 u32 GetTagPassword(const TagUuid& uuid) const;
217
218 const Core::HID::NpadIdType npad_id;
219
220 DeviceState device_state{DeviceState::Unaviable};
49 KernelHelpers::ServiceContext service_context; 221 KernelHelpers::ServiceContext service_context;
50 Kernel::KEvent* nfc_tag_load; 222 Kernel::KEvent* activate_event;
51 AmiiboFile amiibo{}; 223 Kernel::KEvent* deactivate_event;
224 NTAG215File tag_data{};
225 s32 protocol;
226 bool is_application_area_initialized{};
227 u32 application_area_id;
228 std::vector<u8> application_area_data;
52 }; 229 };
53}; 230};
54 231
232class IUser final : public ServiceFramework<IUser> {
233public:
234 explicit IUser(Module::Interface& nfp_interface_, Core::System& system_);
235
236private:
237 void Initialize(Kernel::HLERequestContext& ctx);
238 void Finalize(Kernel::HLERequestContext& ctx);
239 void ListDevices(Kernel::HLERequestContext& ctx);
240 void StartDetection(Kernel::HLERequestContext& ctx);
241 void StopDetection(Kernel::HLERequestContext& ctx);
242 void Mount(Kernel::HLERequestContext& ctx);
243 void Unmount(Kernel::HLERequestContext& ctx);
244 void OpenApplicationArea(Kernel::HLERequestContext& ctx);
245 void GetApplicationArea(Kernel::HLERequestContext& ctx);
246 void SetApplicationArea(Kernel::HLERequestContext& ctx);
247 void CreateApplicationArea(Kernel::HLERequestContext& ctx);
248 void GetTagInfo(Kernel::HLERequestContext& ctx);
249 void GetRegisterInfo(Kernel::HLERequestContext& ctx);
250 void GetCommonInfo(Kernel::HLERequestContext& ctx);
251 void GetModelInfo(Kernel::HLERequestContext& ctx);
252 void AttachActivateEvent(Kernel::HLERequestContext& ctx);
253 void AttachDeactivateEvent(Kernel::HLERequestContext& ctx);
254 void GetState(Kernel::HLERequestContext& ctx);
255 void GetDeviceState(Kernel::HLERequestContext& ctx);
256 void GetNpadId(Kernel::HLERequestContext& ctx);
257 void GetApplicationAreaSize(Kernel::HLERequestContext& ctx);
258 void AttachAvailabilityChangeEvent(Kernel::HLERequestContext& ctx);
259
260 KernelHelpers::ServiceContext service_context;
261
262 // TODO(german77): We should have a vector of interfaces
263 Module::Interface& nfp_interface;
264
265 State state{State::NonInitialized};
266 Kernel::KEvent* availability_change_event;
267};
268
55void InstallInterfaces(SM::ServiceManager& service_manager, Core::System& system); 269void InstallInterfaces(SM::ServiceManager& service_manager, Core::System& system);
56 270
57} // namespace Service::NFP 271} // namespace Service::NFP
diff --git a/src/core/hle/service/ns/pdm_qry.cpp b/src/core/hle/service/ns/pdm_qry.cpp
index e2fab5c3f..36ce46353 100644
--- a/src/core/hle/service/ns/pdm_qry.cpp
+++ b/src/core/hle/service/ns/pdm_qry.cpp
@@ -59,7 +59,7 @@ void PDM_QRY::QueryPlayStatisticsByApplicationIdAndUserAccountId(Kernel::HLERequ
59 59
60 LOG_WARNING(Service_NS, 60 LOG_WARNING(Service_NS,
61 "(STUBBED) called. unknown={}. application_id=0x{:016X}, user_account_uid=0x{}", 61 "(STUBBED) called. unknown={}. application_id=0x{:016X}, user_account_uid=0x{}",
62 unknown, application_id, user_account_uid.Format()); 62 unknown, application_id, user_account_uid.RawString());
63 63
64 IPC::ResponseBuilder rb{ctx, 12}; 64 IPC::ResponseBuilder rb{ctx, 12};
65 rb.Push(ResultSuccess); 65 rb.Push(ResultSuccess);
diff --git a/src/core/hle/service/pm/pm.cpp b/src/core/hle/service/pm/pm.cpp
index 277abc17a..057666021 100644
--- a/src/core/hle/service/pm/pm.cpp
+++ b/src/core/hle/service/pm/pm.cpp
@@ -91,6 +91,8 @@ public:
91 {4, &DebugMonitor::GetApplicationProcessId, "GetApplicationProcessId"}, 91 {4, &DebugMonitor::GetApplicationProcessId, "GetApplicationProcessId"},
92 {5, nullptr, "HookToCreateApplicationProgress"}, 92 {5, nullptr, "HookToCreateApplicationProgress"},
93 {6, nullptr, "ClearHook"}, 93 {6, nullptr, "ClearHook"},
94 {65000, &DebugMonitor::AtmosphereGetProcessInfo, "AtmosphereGetProcessInfo"},
95 {65001, nullptr, "AtmosphereGetCurrentLimitInfo"},
94 }; 96 };
95 // clang-format on 97 // clang-format on
96 98
@@ -125,6 +127,49 @@ private:
125 GetApplicationPidGeneric(ctx, kernel.GetProcessList()); 127 GetApplicationPidGeneric(ctx, kernel.GetProcessList());
126 } 128 }
127 129
130 void AtmosphereGetProcessInfo(Kernel::HLERequestContext& ctx) {
131 // https://github.com/Atmosphere-NX/Atmosphere/blob/master/stratosphere/pm/source/impl/pm_process_manager.cpp#L614
132 // This implementation is incomplete; only a handle to the process is returned.
133 IPC::RequestParser rp{ctx};
134 const auto pid = rp.PopRaw<u64>();
135
136 LOG_WARNING(Service_PM, "(Partial Implementation) called, pid={:016X}", pid);
137
138 const auto process = SearchProcessList(kernel.GetProcessList(), [pid](const auto& proc) {
139 return proc->GetProcessID() == pid;
140 });
141
142 if (!process.has_value()) {
143 IPC::ResponseBuilder rb{ctx, 2};
144 rb.Push(ResultProcessNotFound);
145 return;
146 }
147
148 struct ProgramLocation {
149 u64 program_id;
150 u8 storage_id;
151 };
152 static_assert(sizeof(ProgramLocation) == 0x10, "ProgramLocation has an invalid size");
153
154 struct OverrideStatus {
155 u64 keys_held;
156 u64 flags;
157 };
158 static_assert(sizeof(OverrideStatus) == 0x10, "OverrideStatus has an invalid size");
159
160 OverrideStatus override_status{};
161 ProgramLocation program_location{
162 .program_id = (*process)->GetProgramID(),
163 .storage_id = 0,
164 };
165
166 IPC::ResponseBuilder rb{ctx, 10, 1};
167 rb.Push(ResultSuccess);
168 rb.PushCopyObjects(*process);
169 rb.PushRaw(program_location);
170 rb.PushRaw(override_status);
171 }
172
128 const Kernel::KernelCore& kernel; 173 const Kernel::KernelCore& kernel;
129}; 174};
130 175
diff --git a/src/core/hle/service/service.cpp b/src/core/hle/service/service.cpp
index f54e6fe56..eb1138313 100644
--- a/src/core/hle/service/service.cpp
+++ b/src/core/hle/service/service.cpp
@@ -39,6 +39,7 @@
39#include "core/hle/service/mig/mig.h" 39#include "core/hle/service/mig/mig.h"
40#include "core/hle/service/mii/mii.h" 40#include "core/hle/service/mii/mii.h"
41#include "core/hle/service/mm/mm_u.h" 41#include "core/hle/service/mm/mm_u.h"
42#include "core/hle/service/mnpp/mnpp_app.h"
42#include "core/hle/service/ncm/ncm.h" 43#include "core/hle/service/ncm/ncm.h"
43#include "core/hle/service/nfc/nfc.h" 44#include "core/hle/service/nfc/nfc.h"
44#include "core/hle/service/nfp/nfp.h" 45#include "core/hle/service/nfp/nfp.h"
@@ -265,6 +266,7 @@ Services::Services(std::shared_ptr<SM::ServiceManager>& sm, Core::System& system
265 Migration::InstallInterfaces(*sm, system); 266 Migration::InstallInterfaces(*sm, system);
266 Mii::InstallInterfaces(*sm, system); 267 Mii::InstallInterfaces(*sm, system);
267 MM::InstallInterfaces(*sm, system); 268 MM::InstallInterfaces(*sm, system);
269 MNPP::InstallInterfaces(*sm, system);
268 NCM::InstallInterfaces(*sm, system); 270 NCM::InstallInterfaces(*sm, system);
269 NFC::InstallInterfaces(*sm, system); 271 NFC::InstallInterfaces(*sm, system);
270 NFP::InstallInterfaces(*sm, system); 272 NFP::InstallInterfaces(*sm, system);
diff --git a/src/core/hle/service/sm/sm.cpp b/src/core/hle/service/sm/sm.cpp
index eaa172595..695a1faa6 100644
--- a/src/core/hle/service/sm/sm.cpp
+++ b/src/core/hle/service/sm/sm.cpp
@@ -81,6 +81,8 @@ ResultVal<Kernel::KPort*> ServiceManager::GetServicePort(const std::string& name
81 } 81 }
82 82
83 auto* port = Kernel::KPort::Create(kernel); 83 auto* port = Kernel::KPort::Create(kernel);
84 SCOPE_EXIT({ port->Close(); });
85
84 port->Initialize(ServerSessionCountMax, false, name); 86 port->Initialize(ServerSessionCountMax, false, name);
85 auto handler = it->second; 87 auto handler = it->second;
86 port->GetServerPort().SetSessionHandler(std::move(handler)); 88 port->GetServerPort().SetSessionHandler(std::move(handler));
diff --git a/src/core/hle/service/sockets/bsd.cpp b/src/core/hle/service/sockets/bsd.cpp
index f83272633..3dbac5a23 100644
--- a/src/core/hle/service/sockets/bsd.cpp
+++ b/src/core/hle/service/sockets/bsd.cpp
@@ -569,9 +569,9 @@ std::pair<s32, Errno> BSD::AcceptImpl(s32 fd, std::vector<u8>& write_buffer) {
569 new_descriptor.socket = std::move(result.socket); 569 new_descriptor.socket = std::move(result.socket);
570 new_descriptor.is_connection_based = descriptor.is_connection_based; 570 new_descriptor.is_connection_based = descriptor.is_connection_based;
571 571
572 ASSERT(write_buffer.size() == sizeof(SockAddrIn));
573 const SockAddrIn guest_addr_in = Translate(result.sockaddr_in); 572 const SockAddrIn guest_addr_in = Translate(result.sockaddr_in);
574 std::memcpy(write_buffer.data(), &guest_addr_in, sizeof(guest_addr_in)); 573 const size_t length = std::min(sizeof(guest_addr_in), write_buffer.size());
574 std::memcpy(write_buffer.data(), &guest_addr_in, length);
575 575
576 return {new_fd, Errno::SUCCESS}; 576 return {new_fd, Errno::SUCCESS};
577} 577}
diff --git a/src/core/hle/service/time/clock_types.h b/src/core/hle/service/time/clock_types.h
index 392e16863..d0cacb80c 100644
--- a/src/core/hle/service/time/clock_types.h
+++ b/src/core/hle/service/time/clock_types.h
@@ -36,7 +36,7 @@ struct SteadyClockTimePoint {
36 } 36 }
37 37
38 static SteadyClockTimePoint GetRandom() { 38 static SteadyClockTimePoint GetRandom() {
39 return {0, Common::UUID::Generate()}; 39 return {0, Common::UUID::MakeRandom()};
40 } 40 }
41}; 41};
42static_assert(sizeof(SteadyClockTimePoint) == 0x18, "SteadyClockTimePoint is incorrect size"); 42static_assert(sizeof(SteadyClockTimePoint) == 0x18, "SteadyClockTimePoint is incorrect size");
diff --git a/src/core/hle/service/time/steady_clock_core.h b/src/core/hle/service/time/steady_clock_core.h
index d80a2385f..5ee2c0e0a 100644
--- a/src/core/hle/service/time/steady_clock_core.h
+++ b/src/core/hle/service/time/steady_clock_core.h
@@ -49,7 +49,7 @@ public:
49 } 49 }
50 50
51private: 51private:
52 Common::UUID clock_source_id{Common::UUID::Generate()}; 52 Common::UUID clock_source_id{Common::UUID::MakeRandom()};
53 bool is_initialized{}; 53 bool is_initialized{};
54}; 54};
55 55
diff --git a/src/core/hle/service/time/time_manager.cpp b/src/core/hle/service/time/time_manager.cpp
index c1e4e6cce..00f1ae8cf 100644
--- a/src/core/hle/service/time/time_manager.cpp
+++ b/src/core/hle/service/time/time_manager.cpp
@@ -45,7 +45,7 @@ struct TimeManager::Impl final {
45 time_zone_content_manager{system} { 45 time_zone_content_manager{system} {
46 46
47 const auto system_time{Clock::TimeSpanType::FromSeconds(GetExternalRtcValue())}; 47 const auto system_time{Clock::TimeSpanType::FromSeconds(GetExternalRtcValue())};
48 SetupStandardSteadyClock(system, Common::UUID::Generate(), system_time, {}, {}); 48 SetupStandardSteadyClock(system, Common::UUID::MakeRandom(), system_time, {}, {});
49 SetupStandardLocalSystemClock(system, {}, system_time.ToSeconds()); 49 SetupStandardLocalSystemClock(system, {}, system_time.ToSeconds());
50 50
51 Clock::SystemClockContext clock_context{}; 51 Clock::SystemClockContext clock_context{};
diff --git a/src/core/hle/service/vi/display/vi_display.h b/src/core/hle/service/vi/display/vi_display.h
index 0979fc421..329f4ba86 100644
--- a/src/core/hle/service/vi/display/vi_display.h
+++ b/src/core/hle/service/vi/display/vi_display.h
@@ -28,10 +28,10 @@ class Layer;
28 28
29/// Represents a single display type 29/// Represents a single display type
30class Display { 30class Display {
31public:
31 YUZU_NON_COPYABLE(Display); 32 YUZU_NON_COPYABLE(Display);
32 YUZU_NON_MOVEABLE(Display); 33 YUZU_NON_MOVEABLE(Display);
33 34
34public:
35 /// Constructs a display with a given unique ID and name. 35 /// Constructs a display with a given unique ID and name.
36 /// 36 ///
37 /// @param id The unique ID for this display. 37 /// @param id The unique ID for this display.
diff --git a/src/core/loader/loader.h b/src/core/loader/loader.h
index 7b1bac3f7..8b6b3b68f 100644
--- a/src/core/loader/loader.h
+++ b/src/core/loader/loader.h
@@ -11,6 +11,7 @@
11#include <utility> 11#include <utility>
12#include <vector> 12#include <vector>
13 13
14#include "common/common_funcs.h"
14#include "common/common_types.h" 15#include "common/common_types.h"
15#include "core/file_sys/control_metadata.h" 16#include "core/file_sys/control_metadata.h"
16#include "core/file_sys/vfs.h" 17#include "core/file_sys/vfs.h"
@@ -139,8 +140,11 @@ std::string GetResultStatusString(ResultStatus status);
139std::ostream& operator<<(std::ostream& os, ResultStatus status); 140std::ostream& operator<<(std::ostream& os, ResultStatus status);
140 141
141/// Interface for loading an application 142/// Interface for loading an application
142class AppLoader : NonCopyable { 143class AppLoader {
143public: 144public:
145 YUZU_NON_COPYABLE(AppLoader);
146 YUZU_NON_MOVEABLE(AppLoader);
147
144 struct LoadParameters { 148 struct LoadParameters {
145 s32 main_thread_priority; 149 s32 main_thread_priority;
146 u64 main_thread_stack_size; 150 u64 main_thread_stack_size;
diff --git a/src/core/memory.cpp b/src/core/memory.cpp
index 88d6ec908..28d30eee2 100644
--- a/src/core/memory.cpp
+++ b/src/core/memory.cpp
@@ -39,8 +39,7 @@ struct Memory::Impl {
39 void MapMemoryRegion(Common::PageTable& page_table, VAddr base, u64 size, PAddr target) { 39 void MapMemoryRegion(Common::PageTable& page_table, VAddr base, u64 size, PAddr target) {
40 ASSERT_MSG((size & PAGE_MASK) == 0, "non-page aligned size: {:016X}", size); 40 ASSERT_MSG((size & PAGE_MASK) == 0, "non-page aligned size: {:016X}", size);
41 ASSERT_MSG((base & PAGE_MASK) == 0, "non-page aligned base: {:016X}", base); 41 ASSERT_MSG((base & PAGE_MASK) == 0, "non-page aligned base: {:016X}", base);
42 ASSERT_MSG(target >= DramMemoryMap::Base && target < DramMemoryMap::End, 42 ASSERT_MSG(target >= DramMemoryMap::Base, "Out of bounds target: {:016X}", target);
43 "Out of bounds target: {:016X}", target);
44 MapPages(page_table, base / PAGE_SIZE, size / PAGE_SIZE, target, Common::PageType::Memory); 43 MapPages(page_table, base / PAGE_SIZE, size / PAGE_SIZE, target, Common::PageType::Memory);
45 44
46 if (Settings::IsFastmemEnabled()) { 45 if (Settings::IsFastmemEnabled()) {
diff --git a/src/input_common/drivers/gc_adapter.cpp b/src/input_common/drivers/gc_adapter.cpp
index 7ab4540a8..155caae42 100644
--- a/src/input_common/drivers/gc_adapter.cpp
+++ b/src/input_common/drivers/gc_adapter.cpp
@@ -248,7 +248,7 @@ bool GCAdapter::Setup() {
248 std::size_t port = 0; 248 std::size_t port = 0;
249 for (GCController& pad : pads) { 249 for (GCController& pad : pads) {
250 pad.identifier = { 250 pad.identifier = {
251 .guid = Common::UUID{Common::INVALID_UUID}, 251 .guid = Common::UUID{},
252 .port = port++, 252 .port = port++,
253 .pad = 0, 253 .pad = 0,
254 }; 254 };
diff --git a/src/input_common/drivers/keyboard.cpp b/src/input_common/drivers/keyboard.cpp
index 4c1e5bbec..59e3d9cc0 100644
--- a/src/input_common/drivers/keyboard.cpp
+++ b/src/input_common/drivers/keyboard.cpp
@@ -9,17 +9,17 @@
9namespace InputCommon { 9namespace InputCommon {
10 10
11constexpr PadIdentifier key_identifier = { 11constexpr PadIdentifier key_identifier = {
12 .guid = Common::UUID{Common::INVALID_UUID}, 12 .guid = Common::UUID{},
13 .port = 0, 13 .port = 0,
14 .pad = 0, 14 .pad = 0,
15}; 15};
16constexpr PadIdentifier keyboard_key_identifier = { 16constexpr PadIdentifier keyboard_key_identifier = {
17 .guid = Common::UUID{Common::INVALID_UUID}, 17 .guid = Common::UUID{},
18 .port = 1, 18 .port = 1,
19 .pad = 0, 19 .pad = 0,
20}; 20};
21constexpr PadIdentifier keyboard_modifier_identifier = { 21constexpr PadIdentifier keyboard_modifier_identifier = {
22 .guid = Common::UUID{Common::INVALID_UUID}, 22 .guid = Common::UUID{},
23 .port = 1, 23 .port = 1,
24 .pad = 1, 24 .pad = 1,
25}; 25};
diff --git a/src/input_common/drivers/mouse.cpp b/src/input_common/drivers/mouse.cpp
index d8ae7f0c1..3c9a4e747 100644
--- a/src/input_common/drivers/mouse.cpp
+++ b/src/input_common/drivers/mouse.cpp
@@ -20,7 +20,7 @@ constexpr int motion_wheel_y = 4;
20constexpr int touch_axis_x = 10; 20constexpr int touch_axis_x = 10;
21constexpr int touch_axis_y = 11; 21constexpr int touch_axis_y = 11;
22constexpr PadIdentifier identifier = { 22constexpr PadIdentifier identifier = {
23 .guid = Common::UUID{Common::INVALID_UUID}, 23 .guid = Common::UUID{},
24 .port = 0, 24 .port = 0,
25 .pad = 0, 25 .pad = 0,
26}; 26};
diff --git a/src/input_common/drivers/sdl_driver.cpp b/src/input_common/drivers/sdl_driver.cpp
index 577bf5c31..c17ea305e 100644
--- a/src/input_common/drivers/sdl_driver.cpp
+++ b/src/input_common/drivers/sdl_driver.cpp
@@ -175,23 +175,23 @@ public:
175 return false; 175 return false;
176 } 176 }
177 177
178 BatteryLevel GetBatteryLevel() { 178 Common::Input::BatteryLevel GetBatteryLevel() {
179 const auto level = SDL_JoystickCurrentPowerLevel(sdl_joystick.get()); 179 const auto level = SDL_JoystickCurrentPowerLevel(sdl_joystick.get());
180 switch (level) { 180 switch (level) {
181 case SDL_JOYSTICK_POWER_EMPTY: 181 case SDL_JOYSTICK_POWER_EMPTY:
182 return BatteryLevel::Empty; 182 return Common::Input::BatteryLevel::Empty;
183 case SDL_JOYSTICK_POWER_LOW: 183 case SDL_JOYSTICK_POWER_LOW:
184 return BatteryLevel::Critical; 184 return Common::Input::BatteryLevel::Low;
185 case SDL_JOYSTICK_POWER_MEDIUM: 185 case SDL_JOYSTICK_POWER_MEDIUM:
186 return BatteryLevel::Low; 186 return Common::Input::BatteryLevel::Medium;
187 case SDL_JOYSTICK_POWER_FULL: 187 case SDL_JOYSTICK_POWER_FULL:
188 return BatteryLevel::Medium;
189 case SDL_JOYSTICK_POWER_MAX: 188 case SDL_JOYSTICK_POWER_MAX:
190 return BatteryLevel::Full; 189 return Common::Input::BatteryLevel::Full;
191 case SDL_JOYSTICK_POWER_UNKNOWN:
192 case SDL_JOYSTICK_POWER_WIRED: 190 case SDL_JOYSTICK_POWER_WIRED:
191 return Common::Input::BatteryLevel::Charging;
192 case SDL_JOYSTICK_POWER_UNKNOWN:
193 default: 193 default:
194 return BatteryLevel::Charging; 194 return Common::Input::BatteryLevel::None;
195 } 195 }
196 } 196 }
197 197
@@ -352,6 +352,8 @@ void SDLDriver::HandleGameControllerEvent(const SDL_Event& event) {
352 if (const auto joystick = GetSDLJoystickBySDLID(event.jbutton.which)) { 352 if (const auto joystick = GetSDLJoystickBySDLID(event.jbutton.which)) {
353 const PadIdentifier identifier = joystick->GetPadIdentifier(); 353 const PadIdentifier identifier = joystick->GetPadIdentifier();
354 SetButton(identifier, event.jbutton.button, true); 354 SetButton(identifier, event.jbutton.button, true);
355 // Battery doesn't trigger an event so just update every button press
356 SetBattery(identifier, joystick->GetBatteryLevel());
355 } 357 }
356 break; 358 break;
357 } 359 }
@@ -503,7 +505,7 @@ std::vector<Common::ParamPackage> SDLDriver::GetInputDevices() const {
503Common::Input::VibrationError SDLDriver::SetRumble( 505Common::Input::VibrationError SDLDriver::SetRumble(
504 const PadIdentifier& identifier, const Common::Input::VibrationStatus& vibration) { 506 const PadIdentifier& identifier, const Common::Input::VibrationStatus& vibration) {
505 const auto joystick = 507 const auto joystick =
506 GetSDLJoystickByGUID(identifier.guid.Format(), static_cast<int>(identifier.port)); 508 GetSDLJoystickByGUID(identifier.guid.RawString(), static_cast<int>(identifier.port));
507 const auto process_amplitude_exp = [](f32 amplitude, f32 factor) { 509 const auto process_amplitude_exp = [](f32 amplitude, f32 factor) {
508 return (amplitude + std::pow(amplitude, factor)) * 0.5f * 0xFFFF; 510 return (amplitude + std::pow(amplitude, factor)) * 0.5f * 0xFFFF;
509 }; 511 };
@@ -600,7 +602,7 @@ Common::ParamPackage SDLDriver::BuildParamPackageForAnalog(PadIdentifier identif
600 Common::ParamPackage params; 602 Common::ParamPackage params;
601 params.Set("engine", GetEngineName()); 603 params.Set("engine", GetEngineName());
602 params.Set("port", static_cast<int>(identifier.port)); 604 params.Set("port", static_cast<int>(identifier.port));
603 params.Set("guid", identifier.guid.Format()); 605 params.Set("guid", identifier.guid.RawString());
604 params.Set("axis_x", axis_x); 606 params.Set("axis_x", axis_x);
605 params.Set("axis_y", axis_y); 607 params.Set("axis_y", axis_y);
606 params.Set("offset_x", offset_x); 608 params.Set("offset_x", offset_x);
@@ -812,7 +814,7 @@ AnalogMapping SDLDriver::GetAnalogMappingForDevice(const Common::ParamPackage& p
812 PreSetAxis(identifier, binding_left_x.value.axis); 814 PreSetAxis(identifier, binding_left_x.value.axis);
813 PreSetAxis(identifier, binding_left_y.value.axis); 815 PreSetAxis(identifier, binding_left_y.value.axis);
814 const auto left_offset_x = -GetAxis(identifier, binding_left_x.value.axis); 816 const auto left_offset_x = -GetAxis(identifier, binding_left_x.value.axis);
815 const auto left_offset_y = -GetAxis(identifier, binding_left_y.value.axis); 817 const auto left_offset_y = GetAxis(identifier, binding_left_y.value.axis);
816 mapping.insert_or_assign(Settings::NativeAnalog::LStick, 818 mapping.insert_or_assign(Settings::NativeAnalog::LStick,
817 BuildParamPackageForAnalog(identifier, binding_left_x.value.axis, 819 BuildParamPackageForAnalog(identifier, binding_left_x.value.axis,
818 binding_left_y.value.axis, 820 binding_left_y.value.axis,
@@ -823,7 +825,7 @@ AnalogMapping SDLDriver::GetAnalogMappingForDevice(const Common::ParamPackage& p
823 PreSetAxis(identifier, binding_left_x.value.axis); 825 PreSetAxis(identifier, binding_left_x.value.axis);
824 PreSetAxis(identifier, binding_left_y.value.axis); 826 PreSetAxis(identifier, binding_left_y.value.axis);
825 const auto left_offset_x = -GetAxis(identifier, binding_left_x.value.axis); 827 const auto left_offset_x = -GetAxis(identifier, binding_left_x.value.axis);
826 const auto left_offset_y = -GetAxis(identifier, binding_left_y.value.axis); 828 const auto left_offset_y = GetAxis(identifier, binding_left_y.value.axis);
827 mapping.insert_or_assign(Settings::NativeAnalog::LStick, 829 mapping.insert_or_assign(Settings::NativeAnalog::LStick,
828 BuildParamPackageForAnalog(identifier, binding_left_x.value.axis, 830 BuildParamPackageForAnalog(identifier, binding_left_x.value.axis,
829 binding_left_y.value.axis, 831 binding_left_y.value.axis,
@@ -838,7 +840,7 @@ AnalogMapping SDLDriver::GetAnalogMappingForDevice(const Common::ParamPackage& p
838 PreSetAxis(identifier, binding_right_x.value.axis); 840 PreSetAxis(identifier, binding_right_x.value.axis);
839 PreSetAxis(identifier, binding_right_y.value.axis); 841 PreSetAxis(identifier, binding_right_y.value.axis);
840 const auto right_offset_x = -GetAxis(identifier, binding_right_x.value.axis); 842 const auto right_offset_x = -GetAxis(identifier, binding_right_x.value.axis);
841 const auto right_offset_y = -GetAxis(identifier, binding_right_y.value.axis); 843 const auto right_offset_y = GetAxis(identifier, binding_right_y.value.axis);
842 mapping.insert_or_assign(Settings::NativeAnalog::RStick, 844 mapping.insert_or_assign(Settings::NativeAnalog::RStick,
843 BuildParamPackageForAnalog(identifier, binding_right_x.value.axis, 845 BuildParamPackageForAnalog(identifier, binding_right_x.value.axis,
844 binding_right_y.value.axis, right_offset_x, 846 binding_right_y.value.axis, right_offset_x,
diff --git a/src/input_common/drivers/touch_screen.cpp b/src/input_common/drivers/touch_screen.cpp
index 880781825..30c727df4 100644
--- a/src/input_common/drivers/touch_screen.cpp
+++ b/src/input_common/drivers/touch_screen.cpp
@@ -8,7 +8,7 @@
8namespace InputCommon { 8namespace InputCommon {
9 9
10constexpr PadIdentifier identifier = { 10constexpr PadIdentifier identifier = {
11 .guid = Common::UUID{Common::INVALID_UUID}, 11 .guid = Common::UUID{},
12 .port = 0, 12 .port = 0,
13 .pad = 0, 13 .pad = 0,
14}; 14};
diff --git a/src/input_common/drivers/udp_client.cpp b/src/input_common/drivers/udp_client.cpp
index 333173e3d..9780ead10 100644
--- a/src/input_common/drivers/udp_client.cpp
+++ b/src/input_common/drivers/udp_client.cpp
@@ -192,22 +192,22 @@ std::size_t UDPClient::GetClientNumber(std::string_view host, u16 port) const {
192 return MAX_UDP_CLIENTS; 192 return MAX_UDP_CLIENTS;
193} 193}
194 194
195BatteryLevel UDPClient::GetBatteryLevel(Response::Battery battery) const { 195Common::Input::BatteryLevel UDPClient::GetBatteryLevel(Response::Battery battery) const {
196 switch (battery) { 196 switch (battery) {
197 case Response::Battery::Dying: 197 case Response::Battery::Dying:
198 return BatteryLevel::Empty; 198 return Common::Input::BatteryLevel::Empty;
199 case Response::Battery::Low: 199 case Response::Battery::Low:
200 return BatteryLevel::Critical; 200 return Common::Input::BatteryLevel::Critical;
201 case Response::Battery::Medium: 201 case Response::Battery::Medium:
202 return BatteryLevel::Low; 202 return Common::Input::BatteryLevel::Low;
203 case Response::Battery::High: 203 case Response::Battery::High:
204 return BatteryLevel::Medium; 204 return Common::Input::BatteryLevel::Medium;
205 case Response::Battery::Full: 205 case Response::Battery::Full:
206 case Response::Battery::Charged: 206 case Response::Battery::Charged:
207 return BatteryLevel::Full; 207 return Common::Input::BatteryLevel::Full;
208 case Response::Battery::Charging: 208 case Response::Battery::Charging:
209 default: 209 default:
210 return BatteryLevel::Charging; 210 return Common::Input::BatteryLevel::Charging;
211 } 211 }
212} 212}
213 213
@@ -353,7 +353,7 @@ PadIdentifier UDPClient::GetPadIdentifier(std::size_t pad_index) const {
353 353
354Common::UUID UDPClient::GetHostUUID(const std::string& host) const { 354Common::UUID UDPClient::GetHostUUID(const std::string& host) const {
355 const auto ip = boost::asio::ip::make_address_v4(host); 355 const auto ip = boost::asio::ip::make_address_v4(host);
356 const auto hex_host = fmt::format("{:06x}", ip.to_uint()); 356 const auto hex_host = fmt::format("00000000-0000-0000-0000-0000{:06x}", ip.to_uint());
357 return Common::UUID{hex_host}; 357 return Common::UUID{hex_host};
358} 358}
359 359
@@ -385,7 +385,7 @@ std::vector<Common::ParamPackage> UDPClient::GetInputDevices() const {
385 Common::ParamPackage identifier{}; 385 Common::ParamPackage identifier{};
386 identifier.Set("engine", GetEngineName()); 386 identifier.Set("engine", GetEngineName());
387 identifier.Set("display", fmt::format("UDP Controller {}", pad_identifier.pad)); 387 identifier.Set("display", fmt::format("UDP Controller {}", pad_identifier.pad));
388 identifier.Set("guid", pad_identifier.guid.Format()); 388 identifier.Set("guid", pad_identifier.guid.RawString());
389 identifier.Set("port", static_cast<int>(pad_identifier.port)); 389 identifier.Set("port", static_cast<int>(pad_identifier.port));
390 identifier.Set("pad", static_cast<int>(pad_identifier.pad)); 390 identifier.Set("pad", static_cast<int>(pad_identifier.pad));
391 devices.emplace_back(identifier); 391 devices.emplace_back(identifier);
diff --git a/src/input_common/drivers/udp_client.h b/src/input_common/drivers/udp_client.h
index e9c178139..c7cc7d846 100644
--- a/src/input_common/drivers/udp_client.h
+++ b/src/input_common/drivers/udp_client.h
@@ -126,7 +126,7 @@ private:
126 struct ClientConnection { 126 struct ClientConnection {
127 ClientConnection(); 127 ClientConnection();
128 ~ClientConnection(); 128 ~ClientConnection();
129 Common::UUID uuid{"7F000001"}; 129 Common::UUID uuid{"00000000-0000-0000-0000-00007F000001"};
130 std::string host{"127.0.0.1"}; 130 std::string host{"127.0.0.1"};
131 u16 port{26760}; 131 u16 port{26760};
132 s8 active{-1}; 132 s8 active{-1};
@@ -141,7 +141,7 @@ private:
141 std::size_t GetClientNumber(std::string_view host, u16 port) const; 141 std::size_t GetClientNumber(std::string_view host, u16 port) const;
142 142
143 // Translates UDP battery level to input engine battery level 143 // Translates UDP battery level to input engine battery level
144 BatteryLevel GetBatteryLevel(Response::Battery battery) const; 144 Common::Input::BatteryLevel GetBatteryLevel(Response::Battery battery) const;
145 145
146 void OnVersion(Response::Version); 146 void OnVersion(Response::Version);
147 void OnPortInfo(Response::PortInfo); 147 void OnPortInfo(Response::PortInfo);
diff --git a/src/input_common/helpers/stick_from_buttons.cpp b/src/input_common/helpers/stick_from_buttons.cpp
index e23394f5f..31e6f62ab 100644
--- a/src/input_common/helpers/stick_from_buttons.cpp
+++ b/src/input_common/helpers/stick_from_buttons.cpp
@@ -167,12 +167,34 @@ public:
167 } 167 }
168 168
169 void UpdateModButtonStatus(const Common::Input::CallbackStatus& button_callback) { 169 void UpdateModButtonStatus(const Common::Input::CallbackStatus& button_callback) {
170 modifier_status = button_callback.button_status.value; 170 const auto& new_status = button_callback.button_status;
171 const bool new_button_value = new_status.inverted ? !new_status.value : new_status.value;
172 modifier_status.toggle = new_status.toggle;
173
174 // Update button status with current
175 if (!modifier_status.toggle) {
176 modifier_status.locked = false;
177 if (modifier_status.value != new_button_value) {
178 modifier_status.value = new_button_value;
179 }
180 } else {
181 // Toggle button and lock status
182 if (new_button_value && !modifier_status.locked) {
183 modifier_status.locked = true;
184 modifier_status.value = !modifier_status.value;
185 }
186
187 // Unlock button ready for next press
188 if (!new_button_value && modifier_status.locked) {
189 modifier_status.locked = false;
190 }
191 }
192
171 UpdateStatus(); 193 UpdateStatus();
172 } 194 }
173 195
174 void UpdateStatus() { 196 void UpdateStatus() {
175 const float coef = modifier_status ? modifier_scale : 1.0f; 197 const float coef = modifier_status.value ? modifier_scale : 1.0f;
176 198
177 bool r = right_status; 199 bool r = right_status;
178 bool l = left_status; 200 bool l = left_status;
@@ -266,7 +288,7 @@ public:
266 if (down_status) { 288 if (down_status) {
267 --y; 289 --y;
268 } 290 }
269 const float coef = modifier_status ? modifier_scale : 1.0f; 291 const float coef = modifier_status.value ? modifier_scale : 1.0f;
270 status.x.raw_value = static_cast<float>(x) * coef * (y == 0 ? 1.0f : SQRT_HALF); 292 status.x.raw_value = static_cast<float>(x) * coef * (y == 0 ? 1.0f : SQRT_HALF);
271 status.y.raw_value = static_cast<float>(y) * coef * (x == 0 ? 1.0f : SQRT_HALF); 293 status.y.raw_value = static_cast<float>(y) * coef * (x == 0 ? 1.0f : SQRT_HALF);
272 return status; 294 return status;
@@ -287,9 +309,9 @@ private:
287 bool down_status{}; 309 bool down_status{};
288 bool left_status{}; 310 bool left_status{};
289 bool right_status{}; 311 bool right_status{};
290 bool modifier_status{};
291 float last_x_axis_value{}; 312 float last_x_axis_value{};
292 float last_y_axis_value{}; 313 float last_y_axis_value{};
314 Common::Input::ButtonStatus modifier_status{};
293 const Common::Input::AnalogProperties properties{0.0f, 1.0f, 0.5f, 0.0f, false}; 315 const Common::Input::AnalogProperties properties{0.0f, 1.0f, 0.5f, 0.0f, false};
294 std::chrono::time_point<std::chrono::steady_clock> last_update; 316 std::chrono::time_point<std::chrono::steady_clock> last_update;
295}; 317};
diff --git a/src/input_common/helpers/touch_from_buttons.cpp b/src/input_common/helpers/touch_from_buttons.cpp
index ece1e3b32..f1b57d03a 100644
--- a/src/input_common/helpers/touch_from_buttons.cpp
+++ b/src/input_common/helpers/touch_from_buttons.cpp
@@ -4,7 +4,6 @@
4 4
5#include <algorithm> 5#include <algorithm>
6#include "common/settings.h" 6#include "common/settings.h"
7#include "core/frontend/framebuffer_layout.h"
8#include "input_common/helpers/touch_from_buttons.h" 7#include "input_common/helpers/touch_from_buttons.h"
9 8
10namespace InputCommon { 9namespace InputCommon {
diff --git a/src/input_common/input_engine.cpp b/src/input_common/input_engine.cpp
index 0508b408d..7adf7e3d7 100644
--- a/src/input_common/input_engine.cpp
+++ b/src/input_common/input_engine.cpp
@@ -70,7 +70,7 @@ void InputEngine::SetAxis(const PadIdentifier& identifier, int axis, f32 value)
70 TriggerOnAxisChange(identifier, axis, value); 70 TriggerOnAxisChange(identifier, axis, value);
71} 71}
72 72
73void InputEngine::SetBattery(const PadIdentifier& identifier, BatteryLevel value) { 73void InputEngine::SetBattery(const PadIdentifier& identifier, Common::Input::BatteryLevel value) {
74 { 74 {
75 std::lock_guard lock{mutex}; 75 std::lock_guard lock{mutex};
76 ControllerData& controller = controller_list.at(identifier); 76 ControllerData& controller = controller_list.at(identifier);
@@ -96,7 +96,7 @@ bool InputEngine::GetButton(const PadIdentifier& identifier, int button) const {
96 std::lock_guard lock{mutex}; 96 std::lock_guard lock{mutex};
97 const auto controller_iter = controller_list.find(identifier); 97 const auto controller_iter = controller_list.find(identifier);
98 if (controller_iter == controller_list.cend()) { 98 if (controller_iter == controller_list.cend()) {
99 LOG_ERROR(Input, "Invalid identifier guid={}, pad={}, port={}", identifier.guid.Format(), 99 LOG_ERROR(Input, "Invalid identifier guid={}, pad={}, port={}", identifier.guid.RawString(),
100 identifier.pad, identifier.port); 100 identifier.pad, identifier.port);
101 return false; 101 return false;
102 } 102 }
@@ -113,7 +113,7 @@ bool InputEngine::GetHatButton(const PadIdentifier& identifier, int button, u8 d
113 std::lock_guard lock{mutex}; 113 std::lock_guard lock{mutex};
114 const auto controller_iter = controller_list.find(identifier); 114 const auto controller_iter = controller_list.find(identifier);
115 if (controller_iter == controller_list.cend()) { 115 if (controller_iter == controller_list.cend()) {
116 LOG_ERROR(Input, "Invalid identifier guid={}, pad={}, port={}", identifier.guid.Format(), 116 LOG_ERROR(Input, "Invalid identifier guid={}, pad={}, port={}", identifier.guid.RawString(),
117 identifier.pad, identifier.port); 117 identifier.pad, identifier.port);
118 return false; 118 return false;
119 } 119 }
@@ -130,7 +130,7 @@ f32 InputEngine::GetAxis(const PadIdentifier& identifier, int axis) const {
130 std::lock_guard lock{mutex}; 130 std::lock_guard lock{mutex};
131 const auto controller_iter = controller_list.find(identifier); 131 const auto controller_iter = controller_list.find(identifier);
132 if (controller_iter == controller_list.cend()) { 132 if (controller_iter == controller_list.cend()) {
133 LOG_ERROR(Input, "Invalid identifier guid={}, pad={}, port={}", identifier.guid.Format(), 133 LOG_ERROR(Input, "Invalid identifier guid={}, pad={}, port={}", identifier.guid.RawString(),
134 identifier.pad, identifier.port); 134 identifier.pad, identifier.port);
135 return 0.0f; 135 return 0.0f;
136 } 136 }
@@ -143,13 +143,13 @@ f32 InputEngine::GetAxis(const PadIdentifier& identifier, int axis) const {
143 return axis_iter->second; 143 return axis_iter->second;
144} 144}
145 145
146BatteryLevel InputEngine::GetBattery(const PadIdentifier& identifier) const { 146Common::Input::BatteryLevel InputEngine::GetBattery(const PadIdentifier& identifier) const {
147 std::lock_guard lock{mutex}; 147 std::lock_guard lock{mutex};
148 const auto controller_iter = controller_list.find(identifier); 148 const auto controller_iter = controller_list.find(identifier);
149 if (controller_iter == controller_list.cend()) { 149 if (controller_iter == controller_list.cend()) {
150 LOG_ERROR(Input, "Invalid identifier guid={}, pad={}, port={}", identifier.guid.Format(), 150 LOG_ERROR(Input, "Invalid identifier guid={}, pad={}, port={}", identifier.guid.RawString(),
151 identifier.pad, identifier.port); 151 identifier.pad, identifier.port);
152 return BatteryLevel::Charging; 152 return Common::Input::BatteryLevel::Charging;
153 } 153 }
154 const ControllerData& controller = controller_iter->second; 154 const ControllerData& controller = controller_iter->second;
155 return controller.battery; 155 return controller.battery;
@@ -159,7 +159,7 @@ BasicMotion InputEngine::GetMotion(const PadIdentifier& identifier, int motion)
159 std::lock_guard lock{mutex}; 159 std::lock_guard lock{mutex};
160 const auto controller_iter = controller_list.find(identifier); 160 const auto controller_iter = controller_list.find(identifier);
161 if (controller_iter == controller_list.cend()) { 161 if (controller_iter == controller_list.cend()) {
162 LOG_ERROR(Input, "Invalid identifier guid={}, pad={}, port={}", identifier.guid.Format(), 162 LOG_ERROR(Input, "Invalid identifier guid={}, pad={}, port={}", identifier.guid.RawString(),
163 identifier.pad, identifier.port); 163 identifier.pad, identifier.port);
164 return {}; 164 return {};
165 } 165 }
@@ -270,7 +270,7 @@ void InputEngine::TriggerOnAxisChange(const PadIdentifier& identifier, int axis,
270} 270}
271 271
272void InputEngine::TriggerOnBatteryChange(const PadIdentifier& identifier, 272void InputEngine::TriggerOnBatteryChange(const PadIdentifier& identifier,
273 [[maybe_unused]] BatteryLevel value) { 273 [[maybe_unused]] Common::Input::BatteryLevel value) {
274 std::lock_guard lock{mutex_callback}; 274 std::lock_guard lock{mutex_callback};
275 for (const auto& poller_pair : callback_list) { 275 for (const auto& poller_pair : callback_list) {
276 const InputIdentifier& poller = poller_pair.second; 276 const InputIdentifier& poller = poller_pair.second;
diff --git a/src/input_common/input_engine.h b/src/input_common/input_engine.h
index fe2faee5a..f44e0799b 100644
--- a/src/input_common/input_engine.h
+++ b/src/input_common/input_engine.h
@@ -16,7 +16,7 @@
16 16
17// Pad Identifier of data source 17// Pad Identifier of data source
18struct PadIdentifier { 18struct PadIdentifier {
19 Common::UUID guid{Common::INVALID_UUID}; 19 Common::UUID guid{};
20 std::size_t port{}; 20 std::size_t port{};
21 std::size_t pad{}; 21 std::size_t pad{};
22 22
@@ -34,16 +34,6 @@ struct BasicMotion {
34 u64 delta_timestamp{}; 34 u64 delta_timestamp{};
35}; 35};
36 36
37// Stages of a battery charge
38enum class BatteryLevel {
39 Empty,
40 Critical,
41 Low,
42 Medium,
43 Full,
44 Charging,
45};
46
47// Types of input that are stored in the engine 37// Types of input that are stored in the engine
48enum class EngineInputType { 38enum class EngineInputType {
49 None, 39 None,
@@ -59,7 +49,7 @@ namespace std {
59template <> 49template <>
60struct hash<PadIdentifier> { 50struct hash<PadIdentifier> {
61 size_t operator()(const PadIdentifier& pad_id) const noexcept { 51 size_t operator()(const PadIdentifier& pad_id) const noexcept {
62 u64 hash_value = pad_id.guid.uuid[1] ^ pad_id.guid.uuid[0]; 52 u64 hash_value = pad_id.guid.Hash();
63 hash_value ^= (static_cast<u64>(pad_id.port) << 32); 53 hash_value ^= (static_cast<u64>(pad_id.port) << 32);
64 hash_value ^= static_cast<u64>(pad_id.pad); 54 hash_value ^= static_cast<u64>(pad_id.pad);
65 return static_cast<size_t>(hash_value); 55 return static_cast<size_t>(hash_value);
@@ -178,7 +168,7 @@ public:
178 bool GetButton(const PadIdentifier& identifier, int button) const; 168 bool GetButton(const PadIdentifier& identifier, int button) const;
179 bool GetHatButton(const PadIdentifier& identifier, int button, u8 direction) const; 169 bool GetHatButton(const PadIdentifier& identifier, int button, u8 direction) const;
180 f32 GetAxis(const PadIdentifier& identifier, int axis) const; 170 f32 GetAxis(const PadIdentifier& identifier, int axis) const;
181 BatteryLevel GetBattery(const PadIdentifier& identifier) const; 171 Common::Input::BatteryLevel GetBattery(const PadIdentifier& identifier) const;
182 BasicMotion GetMotion(const PadIdentifier& identifier, int motion) const; 172 BasicMotion GetMotion(const PadIdentifier& identifier, int motion) const;
183 173
184 int SetCallback(InputIdentifier input_identifier); 174 int SetCallback(InputIdentifier input_identifier);
@@ -189,7 +179,7 @@ protected:
189 void SetButton(const PadIdentifier& identifier, int button, bool value); 179 void SetButton(const PadIdentifier& identifier, int button, bool value);
190 void SetHatButton(const PadIdentifier& identifier, int button, u8 value); 180 void SetHatButton(const PadIdentifier& identifier, int button, u8 value);
191 void SetAxis(const PadIdentifier& identifier, int axis, f32 value); 181 void SetAxis(const PadIdentifier& identifier, int axis, f32 value);
192 void SetBattery(const PadIdentifier& identifier, BatteryLevel value); 182 void SetBattery(const PadIdentifier& identifier, Common::Input::BatteryLevel value);
193 void SetMotion(const PadIdentifier& identifier, int motion, const BasicMotion& value); 183 void SetMotion(const PadIdentifier& identifier, int motion, const BasicMotion& value);
194 184
195 virtual std::string GetHatButtonName([[maybe_unused]] u8 direction_value) const { 185 virtual std::string GetHatButtonName([[maybe_unused]] u8 direction_value) const {
@@ -202,13 +192,13 @@ private:
202 std::unordered_map<int, u8> hat_buttons; 192 std::unordered_map<int, u8> hat_buttons;
203 std::unordered_map<int, float> axes; 193 std::unordered_map<int, float> axes;
204 std::unordered_map<int, BasicMotion> motions; 194 std::unordered_map<int, BasicMotion> motions;
205 BatteryLevel battery{}; 195 Common::Input::BatteryLevel battery{};
206 }; 196 };
207 197
208 void TriggerOnButtonChange(const PadIdentifier& identifier, int button, bool value); 198 void TriggerOnButtonChange(const PadIdentifier& identifier, int button, bool value);
209 void TriggerOnHatButtonChange(const PadIdentifier& identifier, int button, u8 value); 199 void TriggerOnHatButtonChange(const PadIdentifier& identifier, int button, u8 value);
210 void TriggerOnAxisChange(const PadIdentifier& identifier, int axis, f32 value); 200 void TriggerOnAxisChange(const PadIdentifier& identifier, int axis, f32 value);
211 void TriggerOnBatteryChange(const PadIdentifier& identifier, BatteryLevel value); 201 void TriggerOnBatteryChange(const PadIdentifier& identifier, Common::Input::BatteryLevel value);
212 void TriggerOnMotionChange(const PadIdentifier& identifier, int motion, 202 void TriggerOnMotionChange(const PadIdentifier& identifier, int motion,
213 const BasicMotion& value); 203 const BasicMotion& value);
214 204
diff --git a/src/input_common/input_mapping.cpp b/src/input_common/input_mapping.cpp
index a7a6ad8c2..fb78093b8 100644
--- a/src/input_common/input_mapping.cpp
+++ b/src/input_common/input_mapping.cpp
@@ -57,7 +57,7 @@ void MappingFactory::RegisterButton(const MappingData& data) {
57 Common::ParamPackage new_input; 57 Common::ParamPackage new_input;
58 new_input.Set("engine", data.engine); 58 new_input.Set("engine", data.engine);
59 if (data.pad.guid.IsValid()) { 59 if (data.pad.guid.IsValid()) {
60 new_input.Set("guid", data.pad.guid.Format()); 60 new_input.Set("guid", data.pad.guid.RawString());
61 } 61 }
62 new_input.Set("port", static_cast<int>(data.pad.port)); 62 new_input.Set("port", static_cast<int>(data.pad.port));
63 new_input.Set("pad", static_cast<int>(data.pad.pad)); 63 new_input.Set("pad", static_cast<int>(data.pad.pad));
@@ -93,7 +93,7 @@ void MappingFactory::RegisterStick(const MappingData& data) {
93 Common::ParamPackage new_input; 93 Common::ParamPackage new_input;
94 new_input.Set("engine", data.engine); 94 new_input.Set("engine", data.engine);
95 if (data.pad.guid.IsValid()) { 95 if (data.pad.guid.IsValid()) {
96 new_input.Set("guid", data.pad.guid.Format()); 96 new_input.Set("guid", data.pad.guid.RawString());
97 } 97 }
98 new_input.Set("port", static_cast<int>(data.pad.port)); 98 new_input.Set("port", static_cast<int>(data.pad.port));
99 new_input.Set("pad", static_cast<int>(data.pad.pad)); 99 new_input.Set("pad", static_cast<int>(data.pad.pad));
@@ -138,7 +138,7 @@ void MappingFactory::RegisterMotion(const MappingData& data) {
138 Common::ParamPackage new_input; 138 Common::ParamPackage new_input;
139 new_input.Set("engine", data.engine); 139 new_input.Set("engine", data.engine);
140 if (data.pad.guid.IsValid()) { 140 if (data.pad.guid.IsValid()) {
141 new_input.Set("guid", data.pad.guid.Format()); 141 new_input.Set("guid", data.pad.guid.RawString());
142 } 142 }
143 new_input.Set("port", static_cast<int>(data.pad.port)); 143 new_input.Set("port", static_cast<int>(data.pad.port));
144 new_input.Set("pad", static_cast<int>(data.pad.pad)); 144 new_input.Set("pad", static_cast<int>(data.pad.pad));
diff --git a/src/input_common/input_poller.cpp b/src/input_common/input_poller.cpp
index 2f3c0735a..82b585ff2 100644
--- a/src/input_common/input_poller.cpp
+++ b/src/input_common/input_poller.cpp
@@ -181,7 +181,7 @@ public:
181 .raw_value = input_engine->GetAxis(identifier, axis_y), 181 .raw_value = input_engine->GetAxis(identifier, axis_y),
182 .properties = properties_y, 182 .properties = properties_y,
183 }; 183 };
184 // This is a workaround too keep compatibility with old yuzu versions. Vertical axis is 184 // This is a workaround to keep compatibility with old yuzu versions. Vertical axis is
185 // inverted on SDL compared to Nintendo 185 // inverted on SDL compared to Nintendo
186 if (invert_axis_y) { 186 if (invert_axis_y) {
187 status.y.raw_value = -status.y.raw_value; 187 status.y.raw_value = -status.y.raw_value;
@@ -470,7 +470,7 @@ public:
470 } 470 }
471 471
472 Common::Input::BatteryStatus GetStatus() const { 472 Common::Input::BatteryStatus GetStatus() const {
473 return static_cast<Common::Input::BatteryLevel>(input_engine->GetBattery(identifier)); 473 return input_engine->GetBattery(identifier);
474 } 474 }
475 475
476 void ForceUpdate() override { 476 void ForceUpdate() override {
diff --git a/src/shader_recompiler/backend/glasm/emit_glasm_instructions.h b/src/shader_recompiler/backend/glasm/emit_glasm_instructions.h
index b48007856..5efbe4e6f 100644
--- a/src/shader_recompiler/backend/glasm/emit_glasm_instructions.h
+++ b/src/shader_recompiler/backend/glasm/emit_glasm_instructions.h
@@ -372,6 +372,8 @@ void EmitSharedAtomicExchange32(EmitContext& ctx, IR::Inst& inst, ScalarU32 poin
372 ScalarU32 value); 372 ScalarU32 value);
373void EmitSharedAtomicExchange64(EmitContext& ctx, IR::Inst& inst, ScalarU32 pointer_offset, 373void EmitSharedAtomicExchange64(EmitContext& ctx, IR::Inst& inst, ScalarU32 pointer_offset,
374 Register value); 374 Register value);
375void EmitSharedAtomicExchange32x2(EmitContext& ctx, IR::Inst& inst, ScalarU32 pointer_offset,
376 Register value);
375void EmitStorageAtomicIAdd32(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding, 377void EmitStorageAtomicIAdd32(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
376 ScalarU32 offset, ScalarU32 value); 378 ScalarU32 offset, ScalarU32 value);
377void EmitStorageAtomicSMin32(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding, 379void EmitStorageAtomicSMin32(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
@@ -412,6 +414,24 @@ void EmitStorageAtomicXor64(EmitContext& ctx, IR::Inst& inst, const IR::Value& b
412 ScalarU32 offset, Register value); 414 ScalarU32 offset, Register value);
413void EmitStorageAtomicExchange64(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding, 415void EmitStorageAtomicExchange64(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
414 ScalarU32 offset, Register value); 416 ScalarU32 offset, Register value);
417void EmitStorageAtomicIAdd32x2(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
418 ScalarU32 offset, Register value);
419void EmitStorageAtomicSMin32x2(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
420 ScalarU32 offset, Register value);
421void EmitStorageAtomicUMin32x2(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
422 ScalarU32 offset, Register value);
423void EmitStorageAtomicSMax32x2(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
424 ScalarU32 offset, Register value);
425void EmitStorageAtomicUMax32x2(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
426 ScalarU32 offset, Register value);
427void EmitStorageAtomicAnd32x2(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
428 ScalarU32 offset, Register value);
429void EmitStorageAtomicOr32x2(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
430 ScalarU32 offset, Register value);
431void EmitStorageAtomicXor32x2(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
432 ScalarU32 offset, Register value);
433void EmitStorageAtomicExchange32x2(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
434 ScalarU32 offset, Register value);
415void EmitStorageAtomicAddF32(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding, 435void EmitStorageAtomicAddF32(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
416 ScalarU32 offset, ScalarF32 value); 436 ScalarU32 offset, ScalarF32 value);
417void EmitStorageAtomicAddF16x2(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding, 437void EmitStorageAtomicAddF16x2(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
@@ -448,6 +468,17 @@ void EmitGlobalAtomicAnd64(EmitContext& ctx);
448void EmitGlobalAtomicOr64(EmitContext& ctx); 468void EmitGlobalAtomicOr64(EmitContext& ctx);
449void EmitGlobalAtomicXor64(EmitContext& ctx); 469void EmitGlobalAtomicXor64(EmitContext& ctx);
450void EmitGlobalAtomicExchange64(EmitContext& ctx); 470void EmitGlobalAtomicExchange64(EmitContext& ctx);
471void EmitGlobalAtomicIAdd32x2(EmitContext& ctx);
472void EmitGlobalAtomicSMin32x2(EmitContext& ctx);
473void EmitGlobalAtomicUMin32x2(EmitContext& ctx);
474void EmitGlobalAtomicSMax32x2(EmitContext& ctx);
475void EmitGlobalAtomicUMax32x2(EmitContext& ctx);
476void EmitGlobalAtomicInc32x2(EmitContext& ctx);
477void EmitGlobalAtomicDec32x2(EmitContext& ctx);
478void EmitGlobalAtomicAnd32x2(EmitContext& ctx);
479void EmitGlobalAtomicOr32x2(EmitContext& ctx);
480void EmitGlobalAtomicXor32x2(EmitContext& ctx);
481void EmitGlobalAtomicExchange32x2(EmitContext& ctx);
451void EmitGlobalAtomicAddF32(EmitContext& ctx); 482void EmitGlobalAtomicAddF32(EmitContext& ctx);
452void EmitGlobalAtomicAddF16x2(EmitContext& ctx); 483void EmitGlobalAtomicAddF16x2(EmitContext& ctx);
453void EmitGlobalAtomicAddF32x2(EmitContext& ctx); 484void EmitGlobalAtomicAddF32x2(EmitContext& ctx);
diff --git a/src/shader_recompiler/backend/glasm/emit_glasm_memory.cpp b/src/shader_recompiler/backend/glasm/emit_glasm_memory.cpp
index f135b67f5..f0fd94a28 100644
--- a/src/shader_recompiler/backend/glasm/emit_glasm_memory.cpp
+++ b/src/shader_recompiler/backend/glasm/emit_glasm_memory.cpp
@@ -311,6 +311,13 @@ void EmitSharedAtomicExchange64(EmitContext& ctx, IR::Inst& inst, ScalarU32 poin
311 ctx.LongAdd("ATOMS.EXCH.U64 {}.x,{},shared_mem[{}];", inst, value, pointer_offset); 311 ctx.LongAdd("ATOMS.EXCH.U64 {}.x,{},shared_mem[{}];", inst, value, pointer_offset);
312} 312}
313 313
314void EmitSharedAtomicExchange32x2([[maybe_unused]] EmitContext& ctx,
315 [[maybe_unused]] IR::Inst& inst,
316 [[maybe_unused]] ScalarU32 pointer_offset,
317 [[maybe_unused]] Register value) {
318 throw NotImplementedException("GLASM instruction");
319}
320
314void EmitStorageAtomicIAdd32(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding, 321void EmitStorageAtomicIAdd32(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
315 ScalarU32 offset, ScalarU32 value) { 322 ScalarU32 offset, ScalarU32 value) {
316 Atom(ctx, inst, binding, offset, value, "ADD", "U32"); 323 Atom(ctx, inst, binding, offset, value, "ADD", "U32");
@@ -411,6 +418,62 @@ void EmitStorageAtomicExchange64(EmitContext& ctx, IR::Inst& inst, const IR::Val
411 Atom(ctx, inst, binding, offset, value, "EXCH", "U64"); 418 Atom(ctx, inst, binding, offset, value, "EXCH", "U64");
412} 419}
413 420
421void EmitStorageAtomicIAdd32x2([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst,
422 [[maybe_unused]] const IR::Value& binding,
423 [[maybe_unused]] ScalarU32 offset, [[maybe_unused]] Register value) {
424 throw NotImplementedException("GLASM instruction");
425}
426
427void EmitStorageAtomicSMin32x2([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst,
428 [[maybe_unused]] const IR::Value& binding,
429 [[maybe_unused]] ScalarU32 offset, [[maybe_unused]] Register value) {
430 throw NotImplementedException("GLASM instruction");
431}
432
433void EmitStorageAtomicUMin32x2([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst,
434 [[maybe_unused]] const IR::Value& binding,
435 [[maybe_unused]] ScalarU32 offset, [[maybe_unused]] Register value) {
436 throw NotImplementedException("GLASM instruction");
437}
438
439void EmitStorageAtomicSMax32x2([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst,
440 [[maybe_unused]] const IR::Value& binding,
441 [[maybe_unused]] ScalarU32 offset, [[maybe_unused]] Register value) {
442 throw NotImplementedException("GLASM instruction");
443}
444
445void EmitStorageAtomicUMax32x2([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst,
446 [[maybe_unused]] const IR::Value& binding,
447 [[maybe_unused]] ScalarU32 offset, [[maybe_unused]] Register value) {
448 throw NotImplementedException("GLASM instruction");
449}
450
451void EmitStorageAtomicAnd32x2([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst,
452 [[maybe_unused]] const IR::Value& binding,
453 [[maybe_unused]] ScalarU32 offset, [[maybe_unused]] Register value) {
454 throw NotImplementedException("GLASM instruction");
455}
456
457void EmitStorageAtomicOr32x2([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst,
458 [[maybe_unused]] const IR::Value& binding,
459 [[maybe_unused]] ScalarU32 offset, [[maybe_unused]] Register value) {
460 throw NotImplementedException("GLASM instruction");
461}
462
463void EmitStorageAtomicXor32x2([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst,
464 [[maybe_unused]] const IR::Value& binding,
465 [[maybe_unused]] ScalarU32 offset, [[maybe_unused]] Register value) {
466 throw NotImplementedException("GLASM instruction");
467}
468
469void EmitStorageAtomicExchange32x2([[maybe_unused]] EmitContext& ctx,
470 [[maybe_unused]] IR::Inst& inst,
471 [[maybe_unused]] const IR::Value& binding,
472 [[maybe_unused]] ScalarU32 offset,
473 [[maybe_unused]] Register value) {
474 throw NotImplementedException("GLASM instruction");
475}
476
414void EmitStorageAtomicAddF32(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding, 477void EmitStorageAtomicAddF32(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
415 ScalarU32 offset, ScalarF32 value) { 478 ScalarU32 offset, ScalarF32 value) {
416 Atom(ctx, inst, binding, offset, value, "ADD", "F32"); 479 Atom(ctx, inst, binding, offset, value, "ADD", "F32");
@@ -537,6 +600,50 @@ void EmitGlobalAtomicExchange64(EmitContext&) {
537 throw NotImplementedException("GLASM instruction"); 600 throw NotImplementedException("GLASM instruction");
538} 601}
539 602
603void EmitGlobalAtomicIAdd32x2(EmitContext&) {
604 throw NotImplementedException("GLASM instruction");
605}
606
607void EmitGlobalAtomicSMin32x2(EmitContext&) {
608 throw NotImplementedException("GLASM instruction");
609}
610
611void EmitGlobalAtomicUMin32x2(EmitContext&) {
612 throw NotImplementedException("GLASM instruction");
613}
614
615void EmitGlobalAtomicSMax32x2(EmitContext&) {
616 throw NotImplementedException("GLASM instruction");
617}
618
619void EmitGlobalAtomicUMax32x2(EmitContext&) {
620 throw NotImplementedException("GLASM instruction");
621}
622
623void EmitGlobalAtomicInc32x2(EmitContext&) {
624 throw NotImplementedException("GLASM instruction");
625}
626
627void EmitGlobalAtomicDec32x2(EmitContext&) {
628 throw NotImplementedException("GLASM instruction");
629}
630
631void EmitGlobalAtomicAnd32x2(EmitContext&) {
632 throw NotImplementedException("GLASM instruction");
633}
634
635void EmitGlobalAtomicOr32x2(EmitContext&) {
636 throw NotImplementedException("GLASM instruction");
637}
638
639void EmitGlobalAtomicXor32x2(EmitContext&) {
640 throw NotImplementedException("GLASM instruction");
641}
642
643void EmitGlobalAtomicExchange32x2(EmitContext&) {
644 throw NotImplementedException("GLASM instruction");
645}
646
540void EmitGlobalAtomicAddF32(EmitContext&) { 647void EmitGlobalAtomicAddF32(EmitContext&) {
541 throw NotImplementedException("GLASM instruction"); 648 throw NotImplementedException("GLASM instruction");
542} 649}
diff --git a/src/shader_recompiler/backend/glsl/emit_glsl_atomic.cpp b/src/shader_recompiler/backend/glsl/emit_glsl_atomic.cpp
index dc377b053..a409a7ab3 100644
--- a/src/shader_recompiler/backend/glsl/emit_glsl_atomic.cpp
+++ b/src/shader_recompiler/backend/glsl/emit_glsl_atomic.cpp
@@ -105,6 +105,13 @@ void EmitSharedAtomicExchange64(EmitContext& ctx, IR::Inst& inst, std::string_vi
105 pointer_offset, value, pointer_offset, value); 105 pointer_offset, value, pointer_offset, value);
106} 106}
107 107
108void EmitSharedAtomicExchange32x2(EmitContext& ctx, IR::Inst& inst, std::string_view pointer_offset,
109 std::string_view value) {
110 LOG_WARNING(Shader_GLSL, "Int64 atomics not supported, fallback to non-atomic");
111 ctx.AddU32x2("{}=uvec2(smem[{}>>2],smem[({}+4)>>2]);", inst, pointer_offset, pointer_offset);
112 ctx.Add("smem[{}>>2]={}.x;smem[({}+4)>>2]={}.y;", pointer_offset, value, pointer_offset, value);
113}
114
108void EmitStorageAtomicIAdd32(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding, 115void EmitStorageAtomicIAdd32(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
109 const IR::Value& offset, std::string_view value) { 116 const IR::Value& offset, std::string_view value) {
110 ctx.AddU32("{}=atomicAdd({}_ssbo{}[{}>>2],{});", inst, ctx.stage_name, binding.U32(), 117 ctx.AddU32("{}=atomicAdd({}_ssbo{}[{}>>2],{});", inst, ctx.stage_name, binding.U32(),
@@ -265,6 +272,97 @@ void EmitStorageAtomicExchange64(EmitContext& ctx, IR::Inst& inst, const IR::Val
265 ctx.stage_name, binding.U32(), ctx.var_alloc.Consume(offset), value); 272 ctx.stage_name, binding.U32(), ctx.var_alloc.Consume(offset), value);
266} 273}
267 274
275void EmitStorageAtomicIAdd32x2(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
276 const IR::Value& offset, std::string_view value) {
277 LOG_WARNING(Shader_GLSL, "Int64 atomics not supported, fallback to non-atomic");
278 ctx.AddU32x2("{}=uvec2({}_ssbo{}[{}>>2],{}_ssbo{}[({}>>2)+1]);", inst, ctx.stage_name,
279 binding.U32(), ctx.var_alloc.Consume(offset), ctx.stage_name, binding.U32(),
280 ctx.var_alloc.Consume(offset));
281 ctx.Add("{}_ssbo{}[{}>>2]+={}.x;{}_ssbo{}[({}>>2)+1]+={}.y;", ctx.stage_name, binding.U32(),
282 ctx.var_alloc.Consume(offset), value, ctx.stage_name, binding.U32(),
283 ctx.var_alloc.Consume(offset), value);
284}
285
286void EmitStorageAtomicSMin32x2(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
287 const IR::Value& offset, std::string_view value) {
288 LOG_WARNING(Shader_GLSL, "Int64 atomics not supported, fallback to non-atomic");
289 ctx.AddU32x2("{}=ivec2({}_ssbo{}[{}>>2],{}_ssbo{}[({}>>2)+1]);", inst, ctx.stage_name,
290 binding.U32(), ctx.var_alloc.Consume(offset), ctx.stage_name, binding.U32(),
291 ctx.var_alloc.Consume(offset));
292 ctx.Add("for(int "
293 "i=0;i<2;++i){{{}_ssbo{}[({}>>2)+i]=uint(min(int({}_ssbo{}[({}>>2)+i]),int({}[i])));}}",
294 ctx.stage_name, binding.U32(), ctx.var_alloc.Consume(offset), ctx.stage_name,
295 binding.U32(), ctx.var_alloc.Consume(offset), value);
296}
297
298void EmitStorageAtomicUMin32x2(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
299 const IR::Value& offset, std::string_view value) {
300 LOG_WARNING(Shader_GLSL, "Int64 atomics not supported, fallback to non-atomic");
301 ctx.AddU32x2("{}=uvec2({}_ssbo{}[{}>>2],{}_ssbo{}[({}>>2)+1]);", inst, ctx.stage_name,
302 binding.U32(), ctx.var_alloc.Consume(offset), ctx.stage_name, binding.U32(),
303 ctx.var_alloc.Consume(offset));
304 ctx.Add("for(int i=0;i<2;++i){{ "
305 "{}_ssbo{}[({}>>2)+i]=min({}_ssbo{}[({}>>2)+i],{}[i]);}}",
306 ctx.stage_name, binding.U32(), ctx.var_alloc.Consume(offset), ctx.stage_name,
307 binding.U32(), ctx.var_alloc.Consume(offset), value);
308}
309
310void EmitStorageAtomicSMax32x2(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
311 const IR::Value& offset, std::string_view value) {
312 LOG_WARNING(Shader_GLSL, "Int64 atomics not supported, fallback to non-atomic");
313 ctx.AddU32x2("{}=ivec2({}_ssbo{}[{}>>2],{}_ssbo{}[({}>>2)+1]);", inst, ctx.stage_name,
314 binding.U32(), ctx.var_alloc.Consume(offset), ctx.stage_name, binding.U32(),
315 ctx.var_alloc.Consume(offset));
316 ctx.Add("for(int "
317 "i=0;i<2;++i){{{}_ssbo{}[({}>>2)+i]=uint(max(int({}_ssbo{}[({}>>2)+i]),int({}[i])));}}",
318 ctx.stage_name, binding.U32(), ctx.var_alloc.Consume(offset), ctx.stage_name,
319 binding.U32(), ctx.var_alloc.Consume(offset), value);
320}
321
322void EmitStorageAtomicUMax32x2(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
323 const IR::Value& offset, std::string_view value) {
324 LOG_WARNING(Shader_GLSL, "Int64 atomics not supported, fallback to non-atomic");
325 ctx.AddU32x2("{}=uvec2({}_ssbo{}[{}>>2],{}_ssbo{}[({}>>2)+1]);", inst, ctx.stage_name,
326 binding.U32(), ctx.var_alloc.Consume(offset), ctx.stage_name, binding.U32(),
327 ctx.var_alloc.Consume(offset));
328 ctx.Add("for(int i=0;i<2;++i){{{}_ssbo{}[({}>>2)+i]=max({}_ssbo{}[({}>>2)+i],{}[i]);}}",
329 ctx.stage_name, binding.U32(), ctx.var_alloc.Consume(offset), ctx.stage_name,
330 binding.U32(), ctx.var_alloc.Consume(offset), value);
331}
332
333void EmitStorageAtomicAnd32x2(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
334 const IR::Value& offset, std::string_view value) {
335 LOG_WARNING(Shader_GLSL, "Int64 atomics not supported, fallback to 32x2");
336 ctx.AddU32x2("{}=uvec2(atomicAnd({}_ssbo{}[{}>>2],{}.x),atomicAnd({}_ssbo{}[({}>>2)+1],{}.y));",
337 inst, ctx.stage_name, binding.U32(), ctx.var_alloc.Consume(offset), value,
338 ctx.stage_name, binding.U32(), ctx.var_alloc.Consume(offset), value);
339}
340
341void EmitStorageAtomicOr32x2(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
342 const IR::Value& offset, std::string_view value) {
343 LOG_WARNING(Shader_GLSL, "Int64 atomics not supported, fallback to 32x2");
344 ctx.AddU32x2("{}=uvec2(atomicOr({}_ssbo{}[{}>>2],{}.x),atomicOr({}_ssbo{}[({}>>2)+1],{}.y));",
345 inst, ctx.stage_name, binding.U32(), ctx.var_alloc.Consume(offset), value,
346 ctx.stage_name, binding.U32(), ctx.var_alloc.Consume(offset), value);
347}
348
349void EmitStorageAtomicXor32x2(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
350 const IR::Value& offset, std::string_view value) {
351 LOG_WARNING(Shader_GLSL, "Int64 atomics not supported, fallback to 32x2");
352 ctx.AddU32x2("{}=uvec2(atomicXor({}_ssbo{}[{}>>2],{}.x),atomicXor({}_ssbo{}[({}>>2)+1],{}.y));",
353 inst, ctx.stage_name, binding.U32(), ctx.var_alloc.Consume(offset), value,
354 ctx.stage_name, binding.U32(), ctx.var_alloc.Consume(offset), value);
355}
356
357void EmitStorageAtomicExchange32x2(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
358 const IR::Value& offset, std::string_view value) {
359 LOG_WARNING(Shader_GLSL, "Int64 atomics not supported, fallback to 32x2");
360 ctx.AddU32x2("{}=uvec2(atomicExchange({}_ssbo{}[{}>>2],{}.x),atomicExchange({}_ssbo{}[({}>>2)+"
361 "1],{}.y));",
362 inst, ctx.stage_name, binding.U32(), ctx.var_alloc.Consume(offset), value,
363 ctx.stage_name, binding.U32(), ctx.var_alloc.Consume(offset), value);
364}
365
268void EmitStorageAtomicAddF32(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding, 366void EmitStorageAtomicAddF32(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
269 const IR::Value& offset, std::string_view value) { 367 const IR::Value& offset, std::string_view value) {
270 SsboCasFunctionF32(ctx, inst, binding, offset, value, "CasFloatAdd"); 368 SsboCasFunctionF32(ctx, inst, binding, offset, value, "CasFloatAdd");
@@ -388,6 +486,50 @@ void EmitGlobalAtomicExchange64(EmitContext&) {
388 throw NotImplementedException("GLSL Instrucion"); 486 throw NotImplementedException("GLSL Instrucion");
389} 487}
390 488
489void EmitGlobalAtomicIAdd32x2(EmitContext&) {
490 throw NotImplementedException("GLSL Instrucion");
491}
492
493void EmitGlobalAtomicSMin32x2(EmitContext&) {
494 throw NotImplementedException("GLSL Instrucion");
495}
496
497void EmitGlobalAtomicUMin32x2(EmitContext&) {
498 throw NotImplementedException("GLSL Instrucion");
499}
500
501void EmitGlobalAtomicSMax32x2(EmitContext&) {
502 throw NotImplementedException("GLSL Instrucion");
503}
504
505void EmitGlobalAtomicUMax32x2(EmitContext&) {
506 throw NotImplementedException("GLSL Instrucion");
507}
508
509void EmitGlobalAtomicInc32x2(EmitContext&) {
510 throw NotImplementedException("GLSL Instrucion");
511}
512
513void EmitGlobalAtomicDec32x2(EmitContext&) {
514 throw NotImplementedException("GLSL Instrucion");
515}
516
517void EmitGlobalAtomicAnd32x2(EmitContext&) {
518 throw NotImplementedException("GLSL Instrucion");
519}
520
521void EmitGlobalAtomicOr32x2(EmitContext&) {
522 throw NotImplementedException("GLSL Instrucion");
523}
524
525void EmitGlobalAtomicXor32x2(EmitContext&) {
526 throw NotImplementedException("GLSL Instrucion");
527}
528
529void EmitGlobalAtomicExchange32x2(EmitContext&) {
530 throw NotImplementedException("GLSL Instrucion");
531}
532
391void EmitGlobalAtomicAddF32(EmitContext&) { 533void EmitGlobalAtomicAddF32(EmitContext&) {
392 throw NotImplementedException("GLSL Instrucion"); 534 throw NotImplementedException("GLSL Instrucion");
393} 535}
diff --git a/src/shader_recompiler/backend/glsl/emit_glsl_instructions.h b/src/shader_recompiler/backend/glsl/emit_glsl_instructions.h
index 6cabbc717..704baddc9 100644
--- a/src/shader_recompiler/backend/glsl/emit_glsl_instructions.h
+++ b/src/shader_recompiler/backend/glsl/emit_glsl_instructions.h
@@ -442,6 +442,8 @@ void EmitSharedAtomicExchange32(EmitContext& ctx, IR::Inst& inst, std::string_vi
442 std::string_view value); 442 std::string_view value);
443void EmitSharedAtomicExchange64(EmitContext& ctx, IR::Inst& inst, std::string_view pointer_offset, 443void EmitSharedAtomicExchange64(EmitContext& ctx, IR::Inst& inst, std::string_view pointer_offset,
444 std::string_view value); 444 std::string_view value);
445void EmitSharedAtomicExchange32x2(EmitContext& ctx, IR::Inst& inst, std::string_view pointer_offset,
446 std::string_view value);
445void EmitStorageAtomicIAdd32(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding, 447void EmitStorageAtomicIAdd32(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
446 const IR::Value& offset, std::string_view value); 448 const IR::Value& offset, std::string_view value);
447void EmitStorageAtomicSMin32(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding, 449void EmitStorageAtomicSMin32(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
@@ -482,6 +484,24 @@ void EmitStorageAtomicXor64(EmitContext& ctx, IR::Inst& inst, const IR::Value& b
482 const IR::Value& offset, std::string_view value); 484 const IR::Value& offset, std::string_view value);
483void EmitStorageAtomicExchange64(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding, 485void EmitStorageAtomicExchange64(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
484 const IR::Value& offset, std::string_view value); 486 const IR::Value& offset, std::string_view value);
487void EmitStorageAtomicIAdd32x2(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
488 const IR::Value& offset, std::string_view value);
489void EmitStorageAtomicSMin32x2(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
490 const IR::Value& offset, std::string_view value);
491void EmitStorageAtomicUMin32x2(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
492 const IR::Value& offset, std::string_view value);
493void EmitStorageAtomicSMax32x2(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
494 const IR::Value& offset, std::string_view value);
495void EmitStorageAtomicUMax32x2(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
496 const IR::Value& offset, std::string_view value);
497void EmitStorageAtomicAnd32x2(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
498 const IR::Value& offset, std::string_view value);
499void EmitStorageAtomicOr32x2(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
500 const IR::Value& offset, std::string_view value);
501void EmitStorageAtomicXor32x2(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
502 const IR::Value& offset, std::string_view value);
503void EmitStorageAtomicExchange32x2(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
504 const IR::Value& offset, std::string_view value);
485void EmitStorageAtomicAddF32(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding, 505void EmitStorageAtomicAddF32(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
486 const IR::Value& offset, std::string_view value); 506 const IR::Value& offset, std::string_view value);
487void EmitStorageAtomicAddF16x2(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding, 507void EmitStorageAtomicAddF16x2(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
@@ -518,6 +538,17 @@ void EmitGlobalAtomicAnd64(EmitContext& ctx);
518void EmitGlobalAtomicOr64(EmitContext& ctx); 538void EmitGlobalAtomicOr64(EmitContext& ctx);
519void EmitGlobalAtomicXor64(EmitContext& ctx); 539void EmitGlobalAtomicXor64(EmitContext& ctx);
520void EmitGlobalAtomicExchange64(EmitContext& ctx); 540void EmitGlobalAtomicExchange64(EmitContext& ctx);
541void EmitGlobalAtomicIAdd32x2(EmitContext& ctx);
542void EmitGlobalAtomicSMin32x2(EmitContext& ctx);
543void EmitGlobalAtomicUMin32x2(EmitContext& ctx);
544void EmitGlobalAtomicSMax32x2(EmitContext& ctx);
545void EmitGlobalAtomicUMax32x2(EmitContext& ctx);
546void EmitGlobalAtomicInc32x2(EmitContext& ctx);
547void EmitGlobalAtomicDec32x2(EmitContext& ctx);
548void EmitGlobalAtomicAnd32x2(EmitContext& ctx);
549void EmitGlobalAtomicOr32x2(EmitContext& ctx);
550void EmitGlobalAtomicXor32x2(EmitContext& ctx);
551void EmitGlobalAtomicExchange32x2(EmitContext& ctx);
521void EmitGlobalAtomicAddF32(EmitContext& ctx); 552void EmitGlobalAtomicAddF32(EmitContext& ctx);
522void EmitGlobalAtomicAddF16x2(EmitContext& ctx); 553void EmitGlobalAtomicAddF16x2(EmitContext& ctx);
523void EmitGlobalAtomicAddF32x2(EmitContext& ctx); 554void EmitGlobalAtomicAddF32x2(EmitContext& ctx);
diff --git a/src/shader_recompiler/backend/spirv/emit_spirv.h b/src/shader_recompiler/backend/spirv/emit_spirv.h
index b412957c7..2b360e073 100644
--- a/src/shader_recompiler/backend/spirv/emit_spirv.h
+++ b/src/shader_recompiler/backend/spirv/emit_spirv.h
@@ -22,7 +22,7 @@ constexpr u32 NUM_TEXTURE_AND_IMAGE_SCALING_WORDS =
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_atomic.cpp b/src/shader_recompiler/backend/spirv/emit_spirv_atomic.cpp
index 46ba52a25..d3cbb14a9 100644
--- a/src/shader_recompiler/backend/spirv/emit_spirv_atomic.cpp
+++ b/src/shader_recompiler/backend/spirv/emit_spirv_atomic.cpp
@@ -82,6 +82,17 @@ Id StorageAtomicU64(EmitContext& ctx, const IR::Value& binding, const IR::Value&
82 ctx.OpStore(pointer, ctx.OpBitcast(ctx.U32[2], result)); 82 ctx.OpStore(pointer, ctx.OpBitcast(ctx.U32[2], result));
83 return original_value; 83 return original_value;
84} 84}
85
86Id StorageAtomicU32x2(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset, Id value,
87 Id (Sirit::Module::*non_atomic_func)(Id, Id, Id)) {
88 LOG_WARNING(Shader_SPIRV, "Int64 atomics not supported, fallback to non-atomic");
89 const Id pointer{StoragePointer(ctx, ctx.storage_types.U32x2, &StorageDefinitions::U32x2,
90 binding, offset, sizeof(u32[2]))};
91 const Id original_value{ctx.OpLoad(ctx.U32[2], pointer)};
92 const Id result{(ctx.*non_atomic_func)(ctx.U32[2], value, original_value)};
93 ctx.OpStore(pointer, result);
94 return original_value;
95}
85} // Anonymous namespace 96} // Anonymous namespace
86 97
87Id EmitSharedAtomicIAdd32(EmitContext& ctx, Id offset, Id value) { 98Id EmitSharedAtomicIAdd32(EmitContext& ctx, Id offset, Id value) {
@@ -141,7 +152,7 @@ Id EmitSharedAtomicExchange64(EmitContext& ctx, Id offset, Id value) {
141 const auto [scope, semantics]{AtomicArgs(ctx)}; 152 const auto [scope, semantics]{AtomicArgs(ctx)};
142 return ctx.OpAtomicExchange(ctx.U64, pointer, scope, semantics, value); 153 return ctx.OpAtomicExchange(ctx.U64, pointer, scope, semantics, value);
143 } 154 }
144 LOG_ERROR(Shader_SPIRV, "Int64 atomics not supported, fallback to non-atomic"); 155 LOG_WARNING(Shader_SPIRV, "Int64 atomics not supported, fallback to non-atomic");
145 const Id pointer_1{SharedPointer(ctx, offset, 0)}; 156 const Id pointer_1{SharedPointer(ctx, offset, 0)};
146 const Id pointer_2{SharedPointer(ctx, offset, 1)}; 157 const Id pointer_2{SharedPointer(ctx, offset, 1)};
147 const Id value_1{ctx.OpLoad(ctx.U32[1], pointer_1)}; 158 const Id value_1{ctx.OpLoad(ctx.U32[1], pointer_1)};
@@ -152,6 +163,18 @@ Id EmitSharedAtomicExchange64(EmitContext& ctx, Id offset, Id value) {
152 return ctx.OpBitcast(ctx.U64, ctx.OpCompositeConstruct(ctx.U32[2], value_1, value_2)); 163 return ctx.OpBitcast(ctx.U64, ctx.OpCompositeConstruct(ctx.U32[2], value_1, value_2));
153} 164}
154 165
166Id EmitSharedAtomicExchange32x2(EmitContext& ctx, Id offset, Id value) {
167 LOG_WARNING(Shader_SPIRV, "Int64 atomics not supported, fallback to non-atomic");
168 const Id pointer_1{SharedPointer(ctx, offset, 0)};
169 const Id pointer_2{SharedPointer(ctx, offset, 1)};
170 const Id value_1{ctx.OpLoad(ctx.U32[1], pointer_1)};
171 const Id value_2{ctx.OpLoad(ctx.U32[1], pointer_2)};
172 const Id new_vector{ctx.OpBitcast(ctx.U32[2], value)};
173 ctx.OpStore(pointer_1, ctx.OpCompositeExtract(ctx.U32[1], new_vector, 0U));
174 ctx.OpStore(pointer_2, ctx.OpCompositeExtract(ctx.U32[1], new_vector, 1U));
175 return ctx.OpCompositeConstruct(ctx.U32[2], value_1, value_2);
176}
177
155Id EmitStorageAtomicIAdd32(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset, 178Id EmitStorageAtomicIAdd32(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset,
156 Id value) { 179 Id value) {
157 return StorageAtomicU32(ctx, binding, offset, value, &Sirit::Module::OpAtomicIAdd); 180 return StorageAtomicU32(ctx, binding, offset, value, &Sirit::Module::OpAtomicIAdd);
@@ -275,6 +298,56 @@ Id EmitStorageAtomicExchange64(EmitContext& ctx, const IR::Value& binding, const
275 return original; 298 return original;
276} 299}
277 300
301Id EmitStorageAtomicIAdd32x2(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset,
302 Id value) {
303 return StorageAtomicU32x2(ctx, binding, offset, value, &Sirit::Module::OpIAdd);
304}
305
306Id EmitStorageAtomicSMin32x2(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset,
307 Id value) {
308 return StorageAtomicU32x2(ctx, binding, offset, value, &Sirit::Module::OpSMin);
309}
310
311Id EmitStorageAtomicUMin32x2(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset,
312 Id value) {
313 return StorageAtomicU32x2(ctx, binding, offset, value, &Sirit::Module::OpUMin);
314}
315
316Id EmitStorageAtomicSMax32x2(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset,
317 Id value) {
318 return StorageAtomicU32x2(ctx, binding, offset, value, &Sirit::Module::OpSMax);
319}
320
321Id EmitStorageAtomicUMax32x2(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset,
322 Id value) {
323 return StorageAtomicU32x2(ctx, binding, offset, value, &Sirit::Module::OpUMax);
324}
325
326Id EmitStorageAtomicAnd32x2(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset,
327 Id value) {
328 return StorageAtomicU32x2(ctx, binding, offset, value, &Sirit::Module::OpBitwiseAnd);
329}
330
331Id EmitStorageAtomicOr32x2(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset,
332 Id value) {
333 return StorageAtomicU32x2(ctx, binding, offset, value, &Sirit::Module::OpBitwiseOr);
334}
335
336Id EmitStorageAtomicXor32x2(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset,
337 Id value) {
338 return StorageAtomicU32x2(ctx, binding, offset, value, &Sirit::Module::OpBitwiseXor);
339}
340
341Id EmitStorageAtomicExchange32x2(EmitContext& ctx, const IR::Value& binding,
342 const IR::Value& offset, Id value) {
343 LOG_WARNING(Shader_SPIRV, "Int64 atomics not supported, fallback to non-atomic");
344 const Id pointer{StoragePointer(ctx, ctx.storage_types.U32x2, &StorageDefinitions::U32x2,
345 binding, offset, sizeof(u32[2]))};
346 const Id original{ctx.OpLoad(ctx.U32[2], pointer)};
347 ctx.OpStore(pointer, value);
348 return original;
349}
350
278Id EmitStorageAtomicAddF32(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset, 351Id EmitStorageAtomicAddF32(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset,
279 Id value) { 352 Id value) {
280 const Id ssbo{ctx.ssbos[binding.U32()].U32}; 353 const Id ssbo{ctx.ssbos[binding.U32()].U32};
@@ -418,6 +491,50 @@ Id EmitGlobalAtomicExchange64(EmitContext&) {
418 throw NotImplementedException("SPIR-V Instruction"); 491 throw NotImplementedException("SPIR-V Instruction");
419} 492}
420 493
494Id EmitGlobalAtomicIAdd32x2(EmitContext&) {
495 throw NotImplementedException("SPIR-V Instruction");
496}
497
498Id EmitGlobalAtomicSMin32x2(EmitContext&) {
499 throw NotImplementedException("SPIR-V Instruction");
500}
501
502Id EmitGlobalAtomicUMin32x2(EmitContext&) {
503 throw NotImplementedException("SPIR-V Instruction");
504}
505
506Id EmitGlobalAtomicSMax32x2(EmitContext&) {
507 throw NotImplementedException("SPIR-V Instruction");
508}
509
510Id EmitGlobalAtomicUMax32x2(EmitContext&) {
511 throw NotImplementedException("SPIR-V Instruction");
512}
513
514Id EmitGlobalAtomicInc32x2(EmitContext&) {
515 throw NotImplementedException("SPIR-V Instruction");
516}
517
518Id EmitGlobalAtomicDec32x2(EmitContext&) {
519 throw NotImplementedException("SPIR-V Instruction");
520}
521
522Id EmitGlobalAtomicAnd32x2(EmitContext&) {
523 throw NotImplementedException("SPIR-V Instruction");
524}
525
526Id EmitGlobalAtomicOr32x2(EmitContext&) {
527 throw NotImplementedException("SPIR-V Instruction");
528}
529
530Id EmitGlobalAtomicXor32x2(EmitContext&) {
531 throw NotImplementedException("SPIR-V Instruction");
532}
533
534Id EmitGlobalAtomicExchange32x2(EmitContext&) {
535 throw NotImplementedException("SPIR-V Instruction");
536}
537
421Id EmitGlobalAtomicAddF32(EmitContext&) { 538Id EmitGlobalAtomicAddF32(EmitContext&) {
422 throw NotImplementedException("SPIR-V Instruction"); 539 throw NotImplementedException("SPIR-V Instruction");
423} 540}
diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_context_get_set.cpp b/src/shader_recompiler/backend/spirv/emit_spirv_context_get_set.cpp
index 8ea730c80..80b4bbd27 100644
--- a/src/shader_recompiler/backend/spirv/emit_spirv_context_get_set.cpp
+++ b/src/shader_recompiler/backend/spirv/emit_spirv_context_get_set.cpp
@@ -123,34 +123,36 @@ std::optional<OutAttr> OutputAttrPointer(EmitContext& ctx, IR::Attribute attr) {
123} 123}
124 124
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/emit_spirv_instructions.h b/src/shader_recompiler/backend/spirv/emit_spirv_instructions.h
index 887112deb..f263b41b0 100644
--- a/src/shader_recompiler/backend/spirv/emit_spirv_instructions.h
+++ b/src/shader_recompiler/backend/spirv/emit_spirv_instructions.h
@@ -335,6 +335,7 @@ Id EmitSharedAtomicOr32(EmitContext& ctx, Id pointer_offset, Id value);
335Id EmitSharedAtomicXor32(EmitContext& ctx, Id pointer_offset, Id value); 335Id EmitSharedAtomicXor32(EmitContext& ctx, Id pointer_offset, Id value);
336Id EmitSharedAtomicExchange32(EmitContext& ctx, Id pointer_offset, Id value); 336Id EmitSharedAtomicExchange32(EmitContext& ctx, Id pointer_offset, Id value);
337Id EmitSharedAtomicExchange64(EmitContext& ctx, Id pointer_offset, Id value); 337Id EmitSharedAtomicExchange64(EmitContext& ctx, Id pointer_offset, Id value);
338Id EmitSharedAtomicExchange32x2(EmitContext& ctx, Id pointer_offset, Id value);
338Id EmitStorageAtomicIAdd32(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset, 339Id EmitStorageAtomicIAdd32(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset,
339 Id value); 340 Id value);
340Id EmitStorageAtomicSMin32(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset, 341Id EmitStorageAtomicSMin32(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset,
@@ -375,6 +376,24 @@ Id EmitStorageAtomicXor64(EmitContext& ctx, const IR::Value& binding, const IR::
375 Id value); 376 Id value);
376Id EmitStorageAtomicExchange64(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset, 377Id EmitStorageAtomicExchange64(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset,
377 Id value); 378 Id value);
379Id EmitStorageAtomicIAdd32x2(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset,
380 Id value);
381Id EmitStorageAtomicSMin32x2(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset,
382 Id value);
383Id EmitStorageAtomicUMin32x2(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset,
384 Id value);
385Id EmitStorageAtomicSMax32x2(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset,
386 Id value);
387Id EmitStorageAtomicUMax32x2(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset,
388 Id value);
389Id EmitStorageAtomicAnd32x2(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset,
390 Id value);
391Id EmitStorageAtomicOr32x2(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset,
392 Id value);
393Id EmitStorageAtomicXor32x2(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset,
394 Id value);
395Id EmitStorageAtomicExchange32x2(EmitContext& ctx, const IR::Value& binding,
396 const IR::Value& offset, Id value);
378Id EmitStorageAtomicAddF32(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset, 397Id EmitStorageAtomicAddF32(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset,
379 Id value); 398 Id value);
380Id EmitStorageAtomicAddF16x2(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset, 399Id EmitStorageAtomicAddF16x2(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset,
@@ -411,6 +430,17 @@ Id EmitGlobalAtomicAnd64(EmitContext& ctx);
411Id EmitGlobalAtomicOr64(EmitContext& ctx); 430Id EmitGlobalAtomicOr64(EmitContext& ctx);
412Id EmitGlobalAtomicXor64(EmitContext& ctx); 431Id EmitGlobalAtomicXor64(EmitContext& ctx);
413Id EmitGlobalAtomicExchange64(EmitContext& ctx); 432Id EmitGlobalAtomicExchange64(EmitContext& ctx);
433Id EmitGlobalAtomicIAdd32x2(EmitContext& ctx);
434Id EmitGlobalAtomicSMin32x2(EmitContext& ctx);
435Id EmitGlobalAtomicUMin32x2(EmitContext& ctx);
436Id EmitGlobalAtomicSMax32x2(EmitContext& ctx);
437Id EmitGlobalAtomicUMax32x2(EmitContext& ctx);
438Id EmitGlobalAtomicInc32x2(EmitContext& ctx);
439Id EmitGlobalAtomicDec32x2(EmitContext& ctx);
440Id EmitGlobalAtomicAnd32x2(EmitContext& ctx);
441Id EmitGlobalAtomicOr32x2(EmitContext& ctx);
442Id EmitGlobalAtomicXor32x2(EmitContext& ctx);
443Id EmitGlobalAtomicExchange32x2(EmitContext& ctx);
414Id EmitGlobalAtomicAddF32(EmitContext& ctx); 444Id EmitGlobalAtomicAddF32(EmitContext& ctx);
415Id EmitGlobalAtomicAddF16x2(EmitContext& ctx); 445Id EmitGlobalAtomicAddF16x2(EmitContext& ctx);
416Id EmitGlobalAtomicAddF32x2(EmitContext& ctx); 446Id EmitGlobalAtomicAddF32x2(EmitContext& ctx);
diff --git a/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp b/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp
index cd90c084a..aa5b6c9b7 100644
--- a/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp
+++ b/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp
@@ -464,6 +464,7 @@ EmitContext::EmitContext(const Profile& profile_, const RuntimeInfo& runtime_inf
464 DefineSharedMemory(program); 464 DefineSharedMemory(program);
465 DefineSharedMemoryFunctions(program); 465 DefineSharedMemoryFunctions(program);
466 DefineConstantBuffers(program.info, uniform_binding); 466 DefineConstantBuffers(program.info, uniform_binding);
467 DefineConstantBufferIndirectFunctions(program.info);
467 DefineStorageBuffers(program.info, storage_binding); 468 DefineStorageBuffers(program.info, storage_binding);
468 DefineTextureBuffers(program.info, texture_binding); 469 DefineTextureBuffers(program.info, texture_binding);
469 DefineImageBuffers(program.info, image_binding); 470 DefineImageBuffers(program.info, image_binding);
@@ -993,7 +994,7 @@ void EmitContext::DefineConstantBuffers(const Info& info, u32& binding) {
993 } 994 }
994 return; 995 return;
995 } 996 }
996 IR::Type types{info.used_constant_buffer_types}; 997 IR::Type types{info.used_constant_buffer_types | info.used_indirect_cbuf_types};
997 if (True(types & IR::Type::U8)) { 998 if (True(types & IR::Type::U8)) {
998 if (profile.support_int8) { 999 if (profile.support_int8) {
999 DefineConstBuffers(*this, info, &UniformDefinitions::U8, binding, U8, 'u', sizeof(u8)); 1000 DefineConstBuffers(*this, info, &UniformDefinitions::U8, binding, U8, 'u', sizeof(u8));
@@ -1027,6 +1028,62 @@ void EmitContext::DefineConstantBuffers(const Info& info, u32& binding) {
1027 binding += static_cast<u32>(info.constant_buffer_descriptors.size()); 1028 binding += static_cast<u32>(info.constant_buffer_descriptors.size());
1028} 1029}
1029 1030
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/ir/microinstruction.cpp b/src/shader_recompiler/frontend/ir/microinstruction.cpp
index 97e2bf6af..631446cf7 100644
--- a/src/shader_recompiler/frontend/ir/microinstruction.cpp
+++ b/src/shader_recompiler/frontend/ir/microinstruction.cpp
@@ -118,6 +118,7 @@ bool Inst::MayHaveSideEffects() const noexcept {
118 case Opcode::SharedAtomicXor32: 118 case Opcode::SharedAtomicXor32:
119 case Opcode::SharedAtomicExchange32: 119 case Opcode::SharedAtomicExchange32:
120 case Opcode::SharedAtomicExchange64: 120 case Opcode::SharedAtomicExchange64:
121 case Opcode::SharedAtomicExchange32x2:
121 case Opcode::GlobalAtomicIAdd32: 122 case Opcode::GlobalAtomicIAdd32:
122 case Opcode::GlobalAtomicSMin32: 123 case Opcode::GlobalAtomicSMin32:
123 case Opcode::GlobalAtomicUMin32: 124 case Opcode::GlobalAtomicUMin32:
@@ -138,6 +139,15 @@ bool Inst::MayHaveSideEffects() const noexcept {
138 case Opcode::GlobalAtomicOr64: 139 case Opcode::GlobalAtomicOr64:
139 case Opcode::GlobalAtomicXor64: 140 case Opcode::GlobalAtomicXor64:
140 case Opcode::GlobalAtomicExchange64: 141 case Opcode::GlobalAtomicExchange64:
142 case Opcode::GlobalAtomicIAdd32x2:
143 case Opcode::GlobalAtomicSMin32x2:
144 case Opcode::GlobalAtomicUMin32x2:
145 case Opcode::GlobalAtomicSMax32x2:
146 case Opcode::GlobalAtomicUMax32x2:
147 case Opcode::GlobalAtomicAnd32x2:
148 case Opcode::GlobalAtomicOr32x2:
149 case Opcode::GlobalAtomicXor32x2:
150 case Opcode::GlobalAtomicExchange32x2:
141 case Opcode::GlobalAtomicAddF32: 151 case Opcode::GlobalAtomicAddF32:
142 case Opcode::GlobalAtomicAddF16x2: 152 case Opcode::GlobalAtomicAddF16x2:
143 case Opcode::GlobalAtomicAddF32x2: 153 case Opcode::GlobalAtomicAddF32x2:
@@ -165,6 +175,15 @@ bool Inst::MayHaveSideEffects() const noexcept {
165 case Opcode::StorageAtomicOr64: 175 case Opcode::StorageAtomicOr64:
166 case Opcode::StorageAtomicXor64: 176 case Opcode::StorageAtomicXor64:
167 case Opcode::StorageAtomicExchange64: 177 case Opcode::StorageAtomicExchange64:
178 case Opcode::StorageAtomicIAdd32x2:
179 case Opcode::StorageAtomicSMin32x2:
180 case Opcode::StorageAtomicUMin32x2:
181 case Opcode::StorageAtomicSMax32x2:
182 case Opcode::StorageAtomicUMax32x2:
183 case Opcode::StorageAtomicAnd32x2:
184 case Opcode::StorageAtomicOr32x2:
185 case Opcode::StorageAtomicXor32x2:
186 case Opcode::StorageAtomicExchange32x2:
168 case Opcode::StorageAtomicAddF32: 187 case Opcode::StorageAtomicAddF32:
169 case Opcode::StorageAtomicAddF16x2: 188 case Opcode::StorageAtomicAddF16x2:
170 case Opcode::StorageAtomicAddF32x2: 189 case Opcode::StorageAtomicAddF32x2:
diff --git a/src/shader_recompiler/frontend/ir/opcodes.inc b/src/shader_recompiler/frontend/ir/opcodes.inc
index b94ce7406..efb6bfac3 100644
--- a/src/shader_recompiler/frontend/ir/opcodes.inc
+++ b/src/shader_recompiler/frontend/ir/opcodes.inc
@@ -341,6 +341,7 @@ OPCODE(SharedAtomicOr32, U32, U32,
341OPCODE(SharedAtomicXor32, U32, U32, U32, ) 341OPCODE(SharedAtomicXor32, U32, U32, U32, )
342OPCODE(SharedAtomicExchange32, U32, U32, U32, ) 342OPCODE(SharedAtomicExchange32, U32, U32, U32, )
343OPCODE(SharedAtomicExchange64, U64, U32, U64, ) 343OPCODE(SharedAtomicExchange64, U64, U32, U64, )
344OPCODE(SharedAtomicExchange32x2, U32x2, U32, U32x2, )
344 345
345OPCODE(GlobalAtomicIAdd32, U32, U64, U32, ) 346OPCODE(GlobalAtomicIAdd32, U32, U64, U32, )
346OPCODE(GlobalAtomicSMin32, U32, U64, U32, ) 347OPCODE(GlobalAtomicSMin32, U32, U64, U32, )
@@ -362,6 +363,15 @@ OPCODE(GlobalAtomicAnd64, U64, U64,
362OPCODE(GlobalAtomicOr64, U64, U64, U64, ) 363OPCODE(GlobalAtomicOr64, U64, U64, U64, )
363OPCODE(GlobalAtomicXor64, U64, U64, U64, ) 364OPCODE(GlobalAtomicXor64, U64, U64, U64, )
364OPCODE(GlobalAtomicExchange64, U64, U64, U64, ) 365OPCODE(GlobalAtomicExchange64, U64, U64, U64, )
366OPCODE(GlobalAtomicIAdd32x2, U32x2, U32x2, U32x2, )
367OPCODE(GlobalAtomicSMin32x2, U32x2, U32x2, U32x2, )
368OPCODE(GlobalAtomicUMin32x2, U32x2, U32x2, U32x2, )
369OPCODE(GlobalAtomicSMax32x2, U32x2, U32x2, U32x2, )
370OPCODE(GlobalAtomicUMax32x2, U32x2, U32x2, U32x2, )
371OPCODE(GlobalAtomicAnd32x2, U32x2, U32x2, U32x2, )
372OPCODE(GlobalAtomicOr32x2, U32x2, U32x2, U32x2, )
373OPCODE(GlobalAtomicXor32x2, U32x2, U32x2, U32x2, )
374OPCODE(GlobalAtomicExchange32x2, U32x2, U32x2, U32x2, )
365OPCODE(GlobalAtomicAddF32, F32, U64, F32, ) 375OPCODE(GlobalAtomicAddF32, F32, U64, F32, )
366OPCODE(GlobalAtomicAddF16x2, U32, U64, F16x2, ) 376OPCODE(GlobalAtomicAddF16x2, U32, U64, F16x2, )
367OPCODE(GlobalAtomicAddF32x2, U32, U64, F32x2, ) 377OPCODE(GlobalAtomicAddF32x2, U32, U64, F32x2, )
@@ -390,6 +400,15 @@ OPCODE(StorageAtomicAnd64, U64, U32,
390OPCODE(StorageAtomicOr64, U64, U32, U32, U64, ) 400OPCODE(StorageAtomicOr64, U64, U32, U32, U64, )
391OPCODE(StorageAtomicXor64, U64, U32, U32, U64, ) 401OPCODE(StorageAtomicXor64, U64, U32, U32, U64, )
392OPCODE(StorageAtomicExchange64, U64, U32, U32, U64, ) 402OPCODE(StorageAtomicExchange64, U64, U32, U32, U64, )
403OPCODE(StorageAtomicIAdd32x2, U32x2, U32, U32, U32x2, )
404OPCODE(StorageAtomicSMin32x2, U32x2, U32, U32, U32x2, )
405OPCODE(StorageAtomicUMin32x2, U32x2, U32, U32, U32x2, )
406OPCODE(StorageAtomicSMax32x2, U32x2, U32, U32, U32x2, )
407OPCODE(StorageAtomicUMax32x2, U32x2, U32, U32, U32x2, )
408OPCODE(StorageAtomicAnd32x2, U32x2, U32, U32, U32x2, )
409OPCODE(StorageAtomicOr32x2, U32x2, U32, U32, U32x2, )
410OPCODE(StorageAtomicXor32x2, U32x2, U32, U32, U32x2, )
411OPCODE(StorageAtomicExchange32x2, U32x2, U32, U32, U32x2, )
393OPCODE(StorageAtomicAddF32, F32, U32, U32, F32, ) 412OPCODE(StorageAtomicAddF32, F32, U32, U32, F32, )
394OPCODE(StorageAtomicAddF16x2, U32, U32, U32, F16x2, ) 413OPCODE(StorageAtomicAddF16x2, U32, U32, U32, F16x2, )
395OPCODE(StorageAtomicAddF32x2, U32, U32, U32, F32x2, ) 414OPCODE(StorageAtomicAddF32x2, U32, U32, U32, F32x2, )
diff --git a/src/shader_recompiler/frontend/maxwell/translate/impl/load_constant.cpp b/src/shader_recompiler/frontend/maxwell/translate/impl/load_constant.cpp
index 2300088e3..8007a4d46 100644
--- a/src/shader_recompiler/frontend/maxwell/translate/impl/load_constant.cpp
+++ b/src/shader_recompiler/frontend/maxwell/translate/impl/load_constant.cpp
@@ -11,10 +11,20 @@ namespace Shader::Maxwell {
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/impl/logic_operation_three_input.cpp b/src/shader_recompiler/frontend/maxwell/translate/impl/logic_operation_three_input.cpp
index e0fe47912..f3c7ceb57 100644
--- a/src/shader_recompiler/frontend/maxwell/translate/impl/logic_operation_three_input.cpp
+++ b/src/shader_recompiler/frontend/maxwell/translate/impl/logic_operation_three_input.cpp
@@ -13,59 +13,535 @@ namespace {
13// Emulate GPU's LOP3.LUT (three-input logic op with 8-bit truth table) 13// Emulate GPU's LOP3.LUT (three-input logic op with 8-bit truth table)
14IR::U32 ApplyLUT(IR::IREmitter& ir, const IR::U32& a, const IR::U32& b, const IR::U32& c, 14IR::U32 ApplyLUT(IR::IREmitter& ir, const IR::U32& a, const IR::U32& b, const IR::U32& c,
15 u64 ttbl) { 15 u64 ttbl) {
16 IR::U32 r{ir.Imm32(0)}; 16 switch (ttbl) {
17 const IR::U32 not_a{ir.BitwiseNot(a)}; 17 // generated code, do not edit manually
18 const IR::U32 not_b{ir.BitwiseNot(b)}; 18 case 0:
19 const IR::U32 not_c{ir.BitwiseNot(c)}; 19 return ir.Imm32(0);
20 if (ttbl & 0x01) { 20 case 1:
21 // r |= ~a & ~b & ~c; 21 return ir.BitwiseNot(ir.BitwiseOr(a, ir.BitwiseOr(b, c)));
22 const auto lhs{ir.BitwiseAnd(not_a, not_b)}; 22 case 2:
23 const auto rhs{ir.BitwiseAnd(lhs, not_c)}; 23 return ir.BitwiseAnd(c, ir.BitwiseNot(ir.BitwiseOr(a, b)));
24 r = ir.BitwiseOr(r, rhs); 24 case 3:
25 return ir.BitwiseNot(ir.BitwiseOr(a, b));
26 case 4:
27 return ir.BitwiseAnd(b, ir.BitwiseNot(ir.BitwiseOr(a, c)));
28 case 5:
29 return ir.BitwiseNot(ir.BitwiseOr(a, c));
30 case 6:
31 return ir.BitwiseAnd(ir.BitwiseNot(a), ir.BitwiseXor(b, c));
32 case 7:
33 return ir.BitwiseNot(ir.BitwiseOr(a, ir.BitwiseAnd(b, c)));
34 case 8:
35 return ir.BitwiseAnd(ir.BitwiseAnd(b, c), ir.BitwiseNot(a));
36 case 9:
37 return ir.BitwiseNot(ir.BitwiseOr(a, ir.BitwiseXor(b, c)));
38 case 10:
39 return ir.BitwiseAnd(c, ir.BitwiseNot(a));
40 case 11:
41 return ir.BitwiseAnd(ir.BitwiseNot(a), ir.BitwiseOr(c, ir.BitwiseNot(b)));
42 case 12:
43 return ir.BitwiseAnd(b, ir.BitwiseNot(a));
44 case 13:
45 return ir.BitwiseAnd(ir.BitwiseNot(a), ir.BitwiseOr(b, ir.BitwiseNot(c)));
46 case 14:
47 return ir.BitwiseAnd(ir.BitwiseNot(a), ir.BitwiseOr(b, c));
48 case 15:
49 return ir.BitwiseNot(a);
50 case 16:
51 return ir.BitwiseAnd(a, ir.BitwiseNot(ir.BitwiseOr(b, c)));
52 case 17:
53 return ir.BitwiseNot(ir.BitwiseOr(b, c));
54 case 18:
55 return ir.BitwiseAnd(ir.BitwiseNot(b), ir.BitwiseXor(a, c));
56 case 19:
57 return ir.BitwiseNot(ir.BitwiseOr(b, ir.BitwiseAnd(a, c)));
58 case 20:
59 return ir.BitwiseAnd(ir.BitwiseNot(c), ir.BitwiseXor(a, b));
60 case 21:
61 return ir.BitwiseNot(ir.BitwiseOr(c, ir.BitwiseAnd(a, b)));
62 case 22:
63 return ir.BitwiseXor(ir.BitwiseOr(a, b), ir.BitwiseOr(c, ir.BitwiseAnd(a, b)));
64 case 23:
65 return ir.BitwiseXor(ir.BitwiseAnd(ir.BitwiseXor(a, b), ir.BitwiseXor(a, c)),
66 ir.BitwiseNot(a));
67 case 24:
68 return ir.BitwiseAnd(ir.BitwiseXor(a, b), ir.BitwiseXor(a, c));
69 case 25:
70 return ir.BitwiseNot(ir.BitwiseOr(ir.BitwiseAnd(a, b), ir.BitwiseXor(b, c)));
71 case 26:
72 return ir.BitwiseAnd(ir.BitwiseOr(c, ir.BitwiseNot(b)), ir.BitwiseXor(a, c));
73 case 27:
74 return ir.BitwiseXor(ir.BitwiseOr(a, ir.BitwiseNot(c)), ir.BitwiseOr(b, c));
75 case 28:
76 return ir.BitwiseAnd(ir.BitwiseOr(b, ir.BitwiseNot(c)), ir.BitwiseXor(a, b));
77 case 29:
78 return ir.BitwiseXor(ir.BitwiseOr(a, ir.BitwiseNot(b)), ir.BitwiseOr(b, c));
79 case 30:
80 return ir.BitwiseXor(a, ir.BitwiseOr(b, c));
81 case 31:
82 return ir.BitwiseNot(ir.BitwiseAnd(a, ir.BitwiseOr(b, c)));
83 case 32:
84 return ir.BitwiseAnd(ir.BitwiseAnd(a, c), ir.BitwiseNot(b));
85 case 33:
86 return ir.BitwiseNot(ir.BitwiseOr(b, ir.BitwiseXor(a, c)));
87 case 34:
88 return ir.BitwiseAnd(c, ir.BitwiseNot(b));
89 case 35:
90 return ir.BitwiseAnd(ir.BitwiseNot(b), ir.BitwiseOr(c, ir.BitwiseNot(a)));
91 case 36:
92 return ir.BitwiseAnd(ir.BitwiseXor(a, b), ir.BitwiseXor(b, c));
93 case 37:
94 return ir.BitwiseNot(ir.BitwiseOr(ir.BitwiseAnd(a, b), ir.BitwiseXor(a, c)));
95 case 38:
96 return ir.BitwiseAnd(ir.BitwiseOr(c, ir.BitwiseNot(a)), ir.BitwiseXor(b, c));
97 case 39:
98 return ir.BitwiseXor(ir.BitwiseOr(a, c), ir.BitwiseOr(b, ir.BitwiseNot(c)));
99 case 40:
100 return ir.BitwiseAnd(c, ir.BitwiseXor(a, b));
101 case 41:
102 return ir.BitwiseXor(ir.BitwiseOr(a, b),
103 ir.BitwiseOr(ir.BitwiseAnd(a, b), ir.BitwiseNot(c)));
104 case 42:
105 return ir.BitwiseAnd(c, ir.BitwiseNot(ir.BitwiseAnd(a, b)));
106 case 43:
107 return ir.BitwiseXor(ir.BitwiseOr(a, ir.BitwiseNot(c)),
108 ir.BitwiseOr(b, ir.BitwiseXor(a, c)));
109 case 44:
110 return ir.BitwiseAnd(ir.BitwiseOr(b, c), ir.BitwiseXor(a, b));
111 case 45:
112 return ir.BitwiseXor(a, ir.BitwiseOr(b, ir.BitwiseNot(c)));
113 case 46:
114 return ir.BitwiseXor(ir.BitwiseAnd(a, b), ir.BitwiseOr(b, c));
115 case 47:
116 return ir.BitwiseOr(ir.BitwiseAnd(c, ir.BitwiseNot(b)), ir.BitwiseNot(a));
117 case 48:
118 return ir.BitwiseAnd(a, ir.BitwiseNot(b));
119 case 49:
120 return ir.BitwiseAnd(ir.BitwiseNot(b), ir.BitwiseOr(a, ir.BitwiseNot(c)));
121 case 50:
122 return ir.BitwiseAnd(ir.BitwiseNot(b), ir.BitwiseOr(a, c));
123 case 51:
124 return ir.BitwiseNot(b);
125 case 52:
126 return ir.BitwiseAnd(ir.BitwiseOr(a, ir.BitwiseNot(c)), ir.BitwiseXor(a, b));
127 case 53:
128 return ir.BitwiseXor(ir.BitwiseOr(a, c), ir.BitwiseOr(b, ir.BitwiseNot(a)));
129 case 54:
130 return ir.BitwiseXor(b, ir.BitwiseOr(a, c));
131 case 55:
132 return ir.BitwiseNot(ir.BitwiseAnd(b, ir.BitwiseOr(a, c)));
133 case 56:
134 return ir.BitwiseAnd(ir.BitwiseOr(a, c), ir.BitwiseXor(a, b));
135 case 57:
136 return ir.BitwiseXor(b, ir.BitwiseOr(a, ir.BitwiseNot(c)));
137 case 58:
138 return ir.BitwiseXor(ir.BitwiseAnd(a, b), ir.BitwiseOr(a, c));
139 case 59:
140 return ir.BitwiseOr(ir.BitwiseAnd(c, ir.BitwiseNot(a)), ir.BitwiseNot(b));
141 case 60:
142 return ir.BitwiseXor(a, b);
143 case 61:
144 return ir.BitwiseOr(ir.BitwiseNot(ir.BitwiseOr(a, c)), ir.BitwiseXor(a, b));
145 case 62:
146 return ir.BitwiseOr(ir.BitwiseAnd(c, ir.BitwiseNot(a)), ir.BitwiseXor(a, b));
147 case 63:
148 return ir.BitwiseNot(ir.BitwiseAnd(a, b));
149 case 64:
150 return ir.BitwiseAnd(ir.BitwiseAnd(a, b), ir.BitwiseNot(c));
151 case 65:
152 return ir.BitwiseNot(ir.BitwiseOr(c, ir.BitwiseXor(a, b)));
153 case 66:
154 return ir.BitwiseAnd(ir.BitwiseXor(a, c), ir.BitwiseXor(b, c));
155 case 67:
156 return ir.BitwiseNot(ir.BitwiseOr(ir.BitwiseAnd(a, c), ir.BitwiseXor(a, b)));
157 case 68:
158 return ir.BitwiseAnd(b, ir.BitwiseNot(c));
159 case 69:
160 return ir.BitwiseAnd(ir.BitwiseNot(c), ir.BitwiseOr(b, ir.BitwiseNot(a)));
161 case 70:
162 return ir.BitwiseAnd(ir.BitwiseOr(b, ir.BitwiseNot(a)), ir.BitwiseXor(b, c));
163 case 71:
164 return ir.BitwiseXor(ir.BitwiseOr(a, b), ir.BitwiseOr(c, ir.BitwiseNot(b)));
165 case 72:
166 return ir.BitwiseAnd(b, ir.BitwiseXor(a, c));
167 case 73:
168 return ir.BitwiseXor(ir.BitwiseOr(a, c),
169 ir.BitwiseOr(ir.BitwiseAnd(a, c), ir.BitwiseNot(b)));
170 case 74:
171 return ir.BitwiseAnd(ir.BitwiseOr(b, c), ir.BitwiseXor(a, c));
172 case 75:
173 return ir.BitwiseXor(a, ir.BitwiseOr(c, ir.BitwiseNot(b)));
174 case 76:
175 return ir.BitwiseAnd(b, ir.BitwiseNot(ir.BitwiseAnd(a, c)));
176 case 77:
177 return ir.BitwiseXor(ir.BitwiseOr(a, ir.BitwiseNot(b)),
178 ir.BitwiseOr(c, ir.BitwiseXor(a, b)));
179 case 78:
180 return ir.BitwiseXor(ir.BitwiseAnd(a, c), ir.BitwiseOr(b, c));
181 case 79:
182 return ir.BitwiseOr(ir.BitwiseAnd(b, ir.BitwiseNot(c)), ir.BitwiseNot(a));
183 case 80:
184 return ir.BitwiseAnd(a, ir.BitwiseNot(c));
185 case 81:
186 return ir.BitwiseAnd(ir.BitwiseNot(c), ir.BitwiseOr(a, ir.BitwiseNot(b)));
187 case 82:
188 return ir.BitwiseAnd(ir.BitwiseOr(a, ir.BitwiseNot(b)), ir.BitwiseXor(a, c));
189 case 83:
190 return ir.BitwiseXor(ir.BitwiseOr(a, b), ir.BitwiseOr(c, ir.BitwiseNot(a)));
191 case 84:
192 return ir.BitwiseAnd(ir.BitwiseNot(c), ir.BitwiseOr(a, b));
193 case 85:
194 return ir.BitwiseNot(c);
195 case 86:
196 return ir.BitwiseXor(c, ir.BitwiseOr(a, b));
197 case 87:
198 return ir.BitwiseNot(ir.BitwiseAnd(c, ir.BitwiseOr(a, b)));
199 case 88:
200 return ir.BitwiseAnd(ir.BitwiseOr(a, b), ir.BitwiseXor(a, c));
201 case 89:
202 return ir.BitwiseXor(c, ir.BitwiseOr(a, ir.BitwiseNot(b)));
203 case 90:
204 return ir.BitwiseXor(a, c);
205 case 91:
206 return ir.BitwiseOr(ir.BitwiseNot(ir.BitwiseOr(a, b)), ir.BitwiseXor(a, c));
207 case 92:
208 return ir.BitwiseXor(ir.BitwiseAnd(a, c), ir.BitwiseOr(a, b));
209 case 93:
210 return ir.BitwiseOr(ir.BitwiseAnd(b, ir.BitwiseNot(a)), ir.BitwiseNot(c));
211 case 94:
212 return ir.BitwiseOr(ir.BitwiseAnd(b, ir.BitwiseNot(a)), ir.BitwiseXor(a, c));
213 case 95:
214 return ir.BitwiseNot(ir.BitwiseAnd(a, c));
215 case 96:
216 return ir.BitwiseAnd(a, ir.BitwiseXor(b, c));
217 case 97:
218 return ir.BitwiseXor(ir.BitwiseOr(b, c),
219 ir.BitwiseOr(ir.BitwiseAnd(b, c), ir.BitwiseNot(a)));
220 case 98:
221 return ir.BitwiseAnd(ir.BitwiseOr(a, c), ir.BitwiseXor(b, c));
222 case 99:
223 return ir.BitwiseXor(b, ir.BitwiseOr(c, ir.BitwiseNot(a)));
224 case 100:
225 return ir.BitwiseAnd(ir.BitwiseOr(a, b), ir.BitwiseXor(b, c));
226 case 101:
227 return ir.BitwiseXor(c, ir.BitwiseOr(b, ir.BitwiseNot(a)));
228 case 102:
229 return ir.BitwiseXor(b, c);
230 case 103:
231 return ir.BitwiseOr(ir.BitwiseNot(ir.BitwiseOr(a, b)), ir.BitwiseXor(b, c));
232 case 104:
233 return ir.BitwiseAnd(ir.BitwiseOr(a, b), ir.BitwiseXor(c, ir.BitwiseAnd(a, b)));
234 case 105:
235 return ir.BitwiseXor(ir.BitwiseNot(a), ir.BitwiseXor(b, c));
236 case 106:
237 return ir.BitwiseXor(c, ir.BitwiseAnd(a, b));
238 case 107:
239 return ir.BitwiseXor(ir.BitwiseAnd(c, ir.BitwiseOr(a, b)),
240 ir.BitwiseXor(a, ir.BitwiseNot(b)));
241 case 108:
242 return ir.BitwiseXor(b, ir.BitwiseAnd(a, c));
243 case 109:
244 return ir.BitwiseXor(ir.BitwiseAnd(b, ir.BitwiseOr(a, c)),
245 ir.BitwiseXor(a, ir.BitwiseNot(c)));
246 case 110:
247 return ir.BitwiseOr(ir.BitwiseAnd(b, ir.BitwiseNot(a)), ir.BitwiseXor(b, c));
248 case 111:
249 return ir.BitwiseOr(ir.BitwiseNot(a), ir.BitwiseXor(b, c));
250 case 112:
251 return ir.BitwiseAnd(a, ir.BitwiseNot(ir.BitwiseAnd(b, c)));
252 case 113:
253 return ir.BitwiseXor(ir.BitwiseOr(b, ir.BitwiseNot(a)),
254 ir.BitwiseOr(c, ir.BitwiseXor(a, b)));
255 case 114:
256 return ir.BitwiseXor(ir.BitwiseAnd(b, c), ir.BitwiseOr(a, c));
257 case 115:
258 return ir.BitwiseOr(ir.BitwiseAnd(a, ir.BitwiseNot(c)), ir.BitwiseNot(b));
259 case 116:
260 return ir.BitwiseXor(ir.BitwiseAnd(b, c), ir.BitwiseOr(a, b));
261 case 117:
262 return ir.BitwiseOr(ir.BitwiseAnd(a, ir.BitwiseNot(b)), ir.BitwiseNot(c));
263 case 118:
264 return ir.BitwiseOr(ir.BitwiseAnd(a, ir.BitwiseNot(b)), ir.BitwiseXor(b, c));
265 case 119:
266 return ir.BitwiseNot(ir.BitwiseAnd(b, c));
267 case 120:
268 return ir.BitwiseXor(a, ir.BitwiseAnd(b, c));
269 case 121:
270 return ir.BitwiseXor(ir.BitwiseAnd(a, ir.BitwiseOr(b, c)),
271 ir.BitwiseXor(b, ir.BitwiseNot(c)));
272 case 122:
273 return ir.BitwiseOr(ir.BitwiseAnd(a, ir.BitwiseNot(b)), ir.BitwiseXor(a, c));
274 case 123:
275 return ir.BitwiseOr(ir.BitwiseNot(b), ir.BitwiseXor(a, c));
276 case 124:
277 return ir.BitwiseOr(ir.BitwiseAnd(a, ir.BitwiseNot(c)), ir.BitwiseXor(a, b));
278 case 125:
279 return ir.BitwiseOr(ir.BitwiseNot(c), ir.BitwiseXor(a, b));
280 case 126:
281 return ir.BitwiseOr(ir.BitwiseXor(a, b), ir.BitwiseXor(a, c));
282 case 127:
283 return ir.BitwiseNot(ir.BitwiseAnd(a, ir.BitwiseAnd(b, c)));
284 case 128:
285 return ir.BitwiseAnd(a, ir.BitwiseAnd(b, c));
286 case 129:
287 return ir.BitwiseNot(ir.BitwiseOr(ir.BitwiseXor(a, b), ir.BitwiseXor(a, c)));
288 case 130:
289 return ir.BitwiseAnd(c, ir.BitwiseXor(a, ir.BitwiseNot(b)));
290 case 131:
291 return ir.BitwiseAnd(ir.BitwiseOr(c, ir.BitwiseNot(a)), ir.BitwiseXor(a, ir.BitwiseNot(b)));
292 case 132:
293 return ir.BitwiseAnd(b, ir.BitwiseXor(a, ir.BitwiseNot(c)));
294 case 133:
295 return ir.BitwiseAnd(ir.BitwiseOr(b, ir.BitwiseNot(a)), ir.BitwiseXor(a, ir.BitwiseNot(c)));
296 case 134:
297 return ir.BitwiseAnd(ir.BitwiseOr(b, c), ir.BitwiseXor(a, ir.BitwiseXor(b, c)));
298 case 135:
299 return ir.BitwiseXor(ir.BitwiseAnd(b, c), ir.BitwiseNot(a));
300 case 136:
301 return ir.BitwiseAnd(b, c);
302 case 137:
303 return ir.BitwiseAnd(ir.BitwiseOr(b, ir.BitwiseNot(a)), ir.BitwiseXor(b, ir.BitwiseNot(c)));
304 case 138:
305 return ir.BitwiseAnd(c, ir.BitwiseOr(b, ir.BitwiseNot(a)));
306 case 139:
307 return ir.BitwiseOr(ir.BitwiseAnd(b, c), ir.BitwiseNot(ir.BitwiseOr(a, b)));
308 case 140:
309 return ir.BitwiseAnd(b, ir.BitwiseOr(c, ir.BitwiseNot(a)));
310 case 141:
311 return ir.BitwiseOr(ir.BitwiseAnd(b, c), ir.BitwiseNot(ir.BitwiseOr(a, c)));
312 case 142:
313 return ir.BitwiseXor(a, ir.BitwiseOr(ir.BitwiseXor(a, b), ir.BitwiseXor(a, c)));
314 case 143:
315 return ir.BitwiseOr(ir.BitwiseAnd(b, c), ir.BitwiseNot(a));
316 case 144:
317 return ir.BitwiseAnd(a, ir.BitwiseXor(b, ir.BitwiseNot(c)));
318 case 145:
319 return ir.BitwiseAnd(ir.BitwiseOr(a, ir.BitwiseNot(b)), ir.BitwiseXor(b, ir.BitwiseNot(c)));
320 case 146:
321 return ir.BitwiseAnd(ir.BitwiseOr(a, c), ir.BitwiseXor(a, ir.BitwiseXor(b, c)));
322 case 147:
323 return ir.BitwiseXor(ir.BitwiseAnd(a, c), ir.BitwiseNot(b));
324 case 148:
325 return ir.BitwiseAnd(ir.BitwiseOr(a, b), ir.BitwiseXor(a, ir.BitwiseXor(b, c)));
326 case 149:
327 return ir.BitwiseXor(ir.BitwiseAnd(a, b), ir.BitwiseNot(c));
328 case 150:
329 return ir.BitwiseXor(a, ir.BitwiseXor(b, c));
330 case 151:
331 return ir.BitwiseOr(ir.BitwiseNot(ir.BitwiseOr(a, b)),
332 ir.BitwiseXor(a, ir.BitwiseXor(b, c)));
333 case 152:
334 return ir.BitwiseAnd(ir.BitwiseOr(a, b), ir.BitwiseXor(b, ir.BitwiseNot(c)));
335 case 153:
336 return ir.BitwiseXor(b, ir.BitwiseNot(c));
337 case 154:
338 return ir.BitwiseXor(c, ir.BitwiseAnd(a, ir.BitwiseNot(b)));
339 case 155:
340 return ir.BitwiseNot(ir.BitwiseAnd(ir.BitwiseOr(a, b), ir.BitwiseXor(b, c)));
341 case 156:
342 return ir.BitwiseXor(b, ir.BitwiseAnd(a, ir.BitwiseNot(c)));
343 case 157:
344 return ir.BitwiseNot(ir.BitwiseAnd(ir.BitwiseOr(a, c), ir.BitwiseXor(b, c)));
345 case 158:
346 return ir.BitwiseOr(ir.BitwiseAnd(b, c), ir.BitwiseXor(a, ir.BitwiseOr(b, c)));
347 case 159:
348 return ir.BitwiseNot(ir.BitwiseAnd(a, ir.BitwiseXor(b, c)));
349 case 160:
350 return ir.BitwiseAnd(a, c);
351 case 161:
352 return ir.BitwiseAnd(ir.BitwiseOr(a, ir.BitwiseNot(b)), ir.BitwiseXor(a, ir.BitwiseNot(c)));
353 case 162:
354 return ir.BitwiseAnd(c, ir.BitwiseOr(a, ir.BitwiseNot(b)));
355 case 163:
356 return ir.BitwiseOr(ir.BitwiseAnd(a, c), ir.BitwiseNot(ir.BitwiseOr(a, b)));
357 case 164:
358 return ir.BitwiseAnd(ir.BitwiseOr(a, b), ir.BitwiseXor(a, ir.BitwiseNot(c)));
359 case 165:
360 return ir.BitwiseXor(a, ir.BitwiseNot(c));
361 case 166:
362 return ir.BitwiseXor(c, ir.BitwiseAnd(b, ir.BitwiseNot(a)));
363 case 167:
364 return ir.BitwiseNot(ir.BitwiseAnd(ir.BitwiseOr(a, b), ir.BitwiseXor(a, c)));
365 case 168:
366 return ir.BitwiseAnd(c, ir.BitwiseOr(a, b));
367 case 169:
368 return ir.BitwiseXor(ir.BitwiseNot(c), ir.BitwiseOr(a, b));
369 case 170:
370 return c;
371 case 171:
372 return ir.BitwiseOr(c, ir.BitwiseNot(ir.BitwiseOr(a, b)));
373 case 172:
374 return ir.BitwiseAnd(ir.BitwiseOr(a, b), ir.BitwiseOr(c, ir.BitwiseNot(a)));
375 case 173:
376 return ir.BitwiseOr(ir.BitwiseAnd(b, c), ir.BitwiseXor(a, ir.BitwiseNot(c)));
377 case 174:
378 return ir.BitwiseOr(c, ir.BitwiseAnd(b, ir.BitwiseNot(a)));
379 case 175:
380 return ir.BitwiseOr(c, ir.BitwiseNot(a));
381 case 176:
382 return ir.BitwiseAnd(a, ir.BitwiseOr(c, ir.BitwiseNot(b)));
383 case 177:
384 return ir.BitwiseOr(ir.BitwiseAnd(a, c), ir.BitwiseNot(ir.BitwiseOr(b, c)));
385 case 178:
386 return ir.BitwiseXor(b, ir.BitwiseOr(ir.BitwiseXor(a, b), ir.BitwiseXor(a, c)));
387 case 179:
388 return ir.BitwiseOr(ir.BitwiseAnd(a, c), ir.BitwiseNot(b));
389 case 180:
390 return ir.BitwiseXor(a, ir.BitwiseAnd(b, ir.BitwiseNot(c)));
391 case 181:
392 return ir.BitwiseNot(ir.BitwiseAnd(ir.BitwiseOr(b, c), ir.BitwiseXor(a, c)));
393 case 182:
394 return ir.BitwiseOr(ir.BitwiseAnd(a, c), ir.BitwiseXor(b, ir.BitwiseOr(a, c)));
395 case 183:
396 return ir.BitwiseNot(ir.BitwiseAnd(b, ir.BitwiseXor(a, c)));
397 case 184:
398 return ir.BitwiseAnd(ir.BitwiseOr(a, b), ir.BitwiseOr(c, ir.BitwiseNot(b)));
399 case 185:
400 return ir.BitwiseOr(ir.BitwiseAnd(a, c), ir.BitwiseXor(b, ir.BitwiseNot(c)));
401 case 186:
402 return ir.BitwiseOr(c, ir.BitwiseAnd(a, ir.BitwiseNot(b)));
403 case 187:
404 return ir.BitwiseOr(c, ir.BitwiseNot(b));
405 case 188:
406 return ir.BitwiseOr(ir.BitwiseAnd(a, c), ir.BitwiseXor(a, b));
407 case 189:
408 return ir.BitwiseOr(ir.BitwiseXor(a, b), ir.BitwiseXor(a, ir.BitwiseNot(c)));
409 case 190:
410 return ir.BitwiseOr(c, ir.BitwiseXor(a, b));
411 case 191:
412 return ir.BitwiseOr(c, ir.BitwiseNot(ir.BitwiseAnd(a, b)));
413 case 192:
414 return ir.BitwiseAnd(a, b);
415 case 193:
416 return ir.BitwiseAnd(ir.BitwiseOr(a, ir.BitwiseNot(c)), ir.BitwiseXor(a, ir.BitwiseNot(b)));
417 case 194:
418 return ir.BitwiseAnd(ir.BitwiseOr(a, c), ir.BitwiseXor(a, ir.BitwiseNot(b)));
419 case 195:
420 return ir.BitwiseXor(a, ir.BitwiseNot(b));
421 case 196:
422 return ir.BitwiseAnd(b, ir.BitwiseOr(a, ir.BitwiseNot(c)));
423 case 197:
424 return ir.BitwiseOr(ir.BitwiseAnd(a, b), ir.BitwiseNot(ir.BitwiseOr(a, c)));
425 case 198:
426 return ir.BitwiseXor(b, ir.BitwiseAnd(c, ir.BitwiseNot(a)));
427 case 199:
428 return ir.BitwiseNot(ir.BitwiseAnd(ir.BitwiseOr(a, c), ir.BitwiseXor(a, b)));
429 case 200:
430 return ir.BitwiseAnd(b, ir.BitwiseOr(a, c));
431 case 201:
432 return ir.BitwiseXor(ir.BitwiseNot(b), ir.BitwiseOr(a, c));
433 case 202:
434 return ir.BitwiseAnd(ir.BitwiseOr(a, c), ir.BitwiseOr(b, ir.BitwiseNot(a)));
435 case 203:
436 return ir.BitwiseOr(ir.BitwiseAnd(b, c), ir.BitwiseXor(a, ir.BitwiseNot(b)));
437 case 204:
438 return b;
439 case 205:
440 return ir.BitwiseOr(b, ir.BitwiseNot(ir.BitwiseOr(a, c)));
441 case 206:
442 return ir.BitwiseOr(b, ir.BitwiseAnd(c, ir.BitwiseNot(a)));
443 case 207:
444 return ir.BitwiseOr(b, ir.BitwiseNot(a));
445 case 208:
446 return ir.BitwiseAnd(a, ir.BitwiseOr(b, ir.BitwiseNot(c)));
447 case 209:
448 return ir.BitwiseOr(ir.BitwiseAnd(a, b), ir.BitwiseNot(ir.BitwiseOr(b, c)));
449 case 210:
450 return ir.BitwiseXor(a, ir.BitwiseAnd(c, ir.BitwiseNot(b)));
451 case 211:
452 return ir.BitwiseNot(ir.BitwiseAnd(ir.BitwiseOr(b, c), ir.BitwiseXor(a, b)));
453 case 212:
454 return ir.BitwiseXor(c, ir.BitwiseOr(ir.BitwiseXor(a, b), ir.BitwiseXor(a, c)));
455 case 213:
456 return ir.BitwiseOr(ir.BitwiseAnd(a, b), ir.BitwiseNot(c));
457 case 214:
458 return ir.BitwiseOr(ir.BitwiseAnd(a, b), ir.BitwiseXor(c, ir.BitwiseOr(a, b)));
459 case 215:
460 return ir.BitwiseNot(ir.BitwiseAnd(c, ir.BitwiseXor(a, b)));
461 case 216:
462 return ir.BitwiseAnd(ir.BitwiseOr(a, c), ir.BitwiseOr(b, ir.BitwiseNot(c)));
463 case 217:
464 return ir.BitwiseOr(ir.BitwiseAnd(a, b), ir.BitwiseXor(b, ir.BitwiseNot(c)));
465 case 218:
466 return ir.BitwiseOr(ir.BitwiseAnd(a, b), ir.BitwiseXor(a, c));
467 case 219:
468 return ir.BitwiseOr(ir.BitwiseXor(a, c), ir.BitwiseXor(a, ir.BitwiseNot(b)));
469 case 220:
470 return ir.BitwiseOr(b, ir.BitwiseAnd(a, ir.BitwiseNot(c)));
471 case 221:
472 return ir.BitwiseOr(b, ir.BitwiseNot(c));
473 case 222:
474 return ir.BitwiseOr(b, ir.BitwiseXor(a, c));
475 case 223:
476 return ir.BitwiseOr(b, ir.BitwiseNot(ir.BitwiseAnd(a, c)));
477 case 224:
478 return ir.BitwiseAnd(a, ir.BitwiseOr(b, c));
479 case 225:
480 return ir.BitwiseXor(ir.BitwiseNot(a), ir.BitwiseOr(b, c));
481 case 226:
482 return ir.BitwiseAnd(ir.BitwiseOr(a, ir.BitwiseNot(b)), ir.BitwiseOr(b, c));
483 case 227:
484 return ir.BitwiseOr(ir.BitwiseAnd(a, c), ir.BitwiseXor(a, ir.BitwiseNot(b)));
485 case 228:
486 return ir.BitwiseAnd(ir.BitwiseOr(a, ir.BitwiseNot(c)), ir.BitwiseOr(b, c));
487 case 229:
488 return ir.BitwiseOr(ir.BitwiseAnd(a, b), ir.BitwiseXor(a, ir.BitwiseNot(c)));
489 case 230:
490 return ir.BitwiseOr(ir.BitwiseAnd(a, b), ir.BitwiseXor(b, c));
491 case 231:
492 return ir.BitwiseOr(ir.BitwiseXor(a, ir.BitwiseNot(b)), ir.BitwiseXor(b, c));
493 case 232:
494 return ir.BitwiseAnd(ir.BitwiseOr(a, b), ir.BitwiseOr(c, ir.BitwiseAnd(a, b)));
495 case 233:
496 return ir.BitwiseOr(ir.BitwiseAnd(a, b),
497 ir.BitwiseXor(ir.BitwiseNot(c), ir.BitwiseOr(a, b)));
498 case 234:
499 return ir.BitwiseOr(c, ir.BitwiseAnd(a, b));
500 case 235:
501 return ir.BitwiseOr(c, ir.BitwiseXor(a, ir.BitwiseNot(b)));
502 case 236:
503 return ir.BitwiseOr(b, ir.BitwiseAnd(a, c));
504 case 237:
505 return ir.BitwiseOr(b, ir.BitwiseXor(a, ir.BitwiseNot(c)));
506 case 238:
507 return ir.BitwiseOr(b, c);
508 case 239:
509 return ir.BitwiseOr(ir.BitwiseNot(a), ir.BitwiseOr(b, c));
510 case 240:
511 return a;
512 case 241:
513 return ir.BitwiseOr(a, ir.BitwiseNot(ir.BitwiseOr(b, c)));
514 case 242:
515 return ir.BitwiseOr(a, ir.BitwiseAnd(c, ir.BitwiseNot(b)));
516 case 243:
517 return ir.BitwiseOr(a, ir.BitwiseNot(b));
518 case 244:
519 return ir.BitwiseOr(a, ir.BitwiseAnd(b, ir.BitwiseNot(c)));
520 case 245:
521 return ir.BitwiseOr(a, ir.BitwiseNot(c));
522 case 246:
523 return ir.BitwiseOr(a, ir.BitwiseXor(b, c));
524 case 247:
525 return ir.BitwiseOr(a, ir.BitwiseNot(ir.BitwiseAnd(b, c)));
526 case 248:
527 return ir.BitwiseOr(a, ir.BitwiseAnd(b, c));
528 case 249:
529 return ir.BitwiseOr(a, ir.BitwiseXor(b, ir.BitwiseNot(c)));
530 case 250:
531 return ir.BitwiseOr(a, c);
532 case 251:
533 return ir.BitwiseOr(ir.BitwiseNot(b), ir.BitwiseOr(a, c));
534 case 252:
535 return ir.BitwiseOr(a, b);
536 case 253:
537 return ir.BitwiseOr(ir.BitwiseNot(c), ir.BitwiseOr(a, b));
538 case 254:
539 return ir.BitwiseOr(a, ir.BitwiseOr(b, c));
540 case 255:
541 return ir.Imm32(0xFFFFFFFF);
542 // end of generated code
25 } 543 }
26 if (ttbl & 0x02) { 544 throw NotImplementedException("LOP3 with out of range ttbl");
27 // r |= ~a & ~b & c;
28 const auto lhs{ir.BitwiseAnd(not_a, not_b)};
29 const auto rhs{ir.BitwiseAnd(lhs, c)};
30 r = ir.BitwiseOr(r, rhs);
31 }
32 if (ttbl & 0x04) {
33 // r |= ~a & b & ~c;
34 const auto lhs{ir.BitwiseAnd(not_a, b)};
35 const auto rhs{ir.BitwiseAnd(lhs, not_c)};
36 r = ir.BitwiseOr(r, rhs);
37 }
38 if (ttbl & 0x08) {
39 // r |= ~a & b & c;
40 const auto lhs{ir.BitwiseAnd(not_a, b)};
41 const auto rhs{ir.BitwiseAnd(lhs, c)};
42 r = ir.BitwiseOr(r, rhs);
43 }
44 if (ttbl & 0x10) {
45 // r |= a & ~b & ~c;
46 const auto lhs{ir.BitwiseAnd(a, not_b)};
47 const auto rhs{ir.BitwiseAnd(lhs, not_c)};
48 r = ir.BitwiseOr(r, rhs);
49 }
50 if (ttbl & 0x20) {
51 // r |= a & ~b & c;
52 const auto lhs{ir.BitwiseAnd(a, not_b)};
53 const auto rhs{ir.BitwiseAnd(lhs, c)};
54 r = ir.BitwiseOr(r, rhs);
55 }
56 if (ttbl & 0x40) {
57 // r |= a & b & ~c;
58 const auto lhs{ir.BitwiseAnd(a, b)};
59 const auto rhs{ir.BitwiseAnd(lhs, not_c)};
60 r = ir.BitwiseOr(r, rhs);
61 }
62 if (ttbl & 0x80) {
63 // r |= a & b & c;
64 const auto lhs{ir.BitwiseAnd(a, b)};
65 const auto rhs{ir.BitwiseAnd(lhs, c)};
66 r = ir.BitwiseOr(r, rhs);
67 }
68 return r;
69} 545}
70 546
71IR::U32 LOP3(TranslatorVisitor& v, u64 insn, const IR::U32& op_b, const IR::U32& op_c, u64 lut) { 547IR::U32 LOP3(TranslatorVisitor& v, u64 insn, const IR::U32& op_b, const IR::U32& op_c, u64 lut) {
diff --git a/src/shader_recompiler/frontend/maxwell/translate/impl/logic_operation_three_input_lut3.py b/src/shader_recompiler/frontend/maxwell/translate/impl/logic_operation_three_input_lut3.py
new file mode 100644
index 000000000..8f547c266
--- /dev/null
+++ b/src/shader_recompiler/frontend/maxwell/translate/impl/logic_operation_three_input_lut3.py
@@ -0,0 +1,92 @@
1# Copyright © 2022 degasus <markus@selfnet.de>
2# This work is free. You can redistribute it and/or modify it under the
3# terms of the Do What The Fuck You Want To Public License, Version 2,
4# as published by Sam Hocevar. See http://www.wtfpl.net/ for more details.
5
6from itertools import product
7
8# The primitive instructions
9OPS = {
10 'ir.BitwiseAnd({}, {})' : (2, 1, lambda a,b: a&b),
11 'ir.BitwiseOr({}, {})' : (2, 1, lambda a,b: a|b),
12 'ir.BitwiseXor({}, {})' : (2, 1, lambda a,b: a^b),
13 'ir.BitwiseNot({})' : (1, 0.1, lambda a: (~a) & 255), # Only tiny cost, as this can often inlined in other instructions
14}
15
16# Our database of combination of instructions
17optimized_calls = {}
18def cmp(lhs, rhs):
19 if lhs is None: # new entry
20 return True
21 if lhs[3] > rhs[3]: # costs
22 return True
23 if lhs[3] < rhs[3]: # costs
24 return False
25 if len(lhs[0]) > len(rhs[0]): # string len
26 return True
27 if len(lhs[0]) < len(rhs[0]): # string len
28 return False
29 if lhs[0] > rhs[0]: # string sorting
30 return True
31 if lhs[0] < rhs[0]: # string sorting
32 return False
33 assert lhs == rhs, "redundant instruction, bug in brute force"
34 return False
35def register(imm, instruction, count, latency):
36 # Use the sum of instruction count and latency as costs to evaluate which combination is best
37 costs = count + latency
38
39 old = optimized_calls.get(imm, None)
40 new = (instruction, count, latency, costs)
41
42 # Update if new or better
43 if cmp(old, new):
44 optimized_calls[imm] = new
45 return True
46
47 return False
48
49# Constants: 0, 1 (for free)
50register(0, 'ir.Imm32(0)', 0, 0)
51register(255, 'ir.Imm32(0xFFFFFFFF)', 0, 0)
52
53# Inputs: a, b, c (for free)
54ta = 0xF0
55tb = 0xCC
56tc = 0xAA
57inputs = {
58 ta : 'a',
59 tb : 'b',
60 tc : 'c',
61}
62for imm, instruction in inputs.items():
63 register(imm, instruction, 0, 0)
64 register((~imm) & 255, 'ir.BitwiseNot({})'.format(instruction), 0.099, 0.099) # slightly cheaper NEG on inputs
65
66# Try to combine two values from the db with an instruction.
67# If it is better than the old method, update it.
68while True:
69 registered = 0
70 calls_copy = optimized_calls.copy()
71 for OP, (argc, cost, f) in OPS.items():
72 for args in product(calls_copy.items(), repeat=argc):
73 # unpack(transponse) the arrays
74 imm = [arg[0] for arg in args]
75 value = [arg[1][0] for arg in args]
76 count = [arg[1][1] for arg in args]
77 latency = [arg[1][2] for arg in args]
78
79 registered += register(
80 f(*imm),
81 OP.format(*value),
82 sum(count) + cost,
83 max(latency) + cost)
84 if registered == 0:
85 # No update at all? So terminate
86 break
87
88# Hacky output. Please improve me to output valid C++ instead.
89s = """ case {imm}:
90 return {op};"""
91for imm in range(256):
92 print(s.format(imm=imm, op=optimized_calls[imm][0]))
diff --git a/src/shader_recompiler/frontend/maxwell/translate_program.cpp b/src/shader_recompiler/frontend/maxwell/translate_program.cpp
index 248ad3ced..b22725584 100644
--- a/src/shader_recompiler/frontend/maxwell/translate_program.cpp
+++ b/src/shader_recompiler/frontend/maxwell/translate_program.cpp
@@ -212,11 +212,11 @@ IR::Program TranslateProgram(ObjectPool<IR::Inst>& inst_pool, ObjectPool<IR::Blo
212 } 212 }
213 Optimization::SsaRewritePass(program); 213 Optimization::SsaRewritePass(program);
214 214
215 Optimization::ConstantPropagationPass(program);
216
215 Optimization::GlobalMemoryToStorageBufferPass(program); 217 Optimization::GlobalMemoryToStorageBufferPass(program);
216 Optimization::TexturePass(env, program); 218 Optimization::TexturePass(env, program);
217 219
218 Optimization::ConstantPropagationPass(program);
219
220 if (Settings::values.resolution_info.active) { 220 if (Settings::values.resolution_info.active) {
221 Optimization::RescalingPass(program); 221 Optimization::RescalingPass(program);
222 } 222 }
diff --git a/src/shader_recompiler/ir_opt/collect_shader_info_pass.cpp b/src/shader_recompiler/ir_opt/collect_shader_info_pass.cpp
index b6a20f904..0b2c60842 100644
--- a/src/shader_recompiler/ir_opt/collect_shader_info_pass.cpp
+++ b/src/shader_recompiler/ir_opt/collect_shader_info_pass.cpp
@@ -29,6 +29,46 @@ void AddConstantBufferDescriptor(Info& info, u32 index, u32 count) {
29 }); 29 });
30} 30}
31 31
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);
@@ -360,6 +400,15 @@ void VisitUsages(Info& info, IR::Inst& inst) {
360 case IR::Opcode::GlobalAtomicOr64: 400 case IR::Opcode::GlobalAtomicOr64:
361 case IR::Opcode::GlobalAtomicXor64: 401 case IR::Opcode::GlobalAtomicXor64:
362 case IR::Opcode::GlobalAtomicExchange64: 402 case IR::Opcode::GlobalAtomicExchange64:
403 case IR::Opcode::GlobalAtomicIAdd32x2:
404 case IR::Opcode::GlobalAtomicSMin32x2:
405 case IR::Opcode::GlobalAtomicUMin32x2:
406 case IR::Opcode::GlobalAtomicSMax32x2:
407 case IR::Opcode::GlobalAtomicUMax32x2:
408 case IR::Opcode::GlobalAtomicAnd32x2:
409 case IR::Opcode::GlobalAtomicOr32x2:
410 case IR::Opcode::GlobalAtomicXor32x2:
411 case IR::Opcode::GlobalAtomicExchange32x2:
363 case IR::Opcode::GlobalAtomicAddF32: 412 case IR::Opcode::GlobalAtomicAddF32:
364 case IR::Opcode::GlobalAtomicAddF16x2: 413 case IR::Opcode::GlobalAtomicAddF16x2:
365 case IR::Opcode::GlobalAtomicAddF32x2: 414 case IR::Opcode::GlobalAtomicAddF32x2:
@@ -454,42 +503,18 @@ void VisitUsages(Info& info, IR::Inst& inst) {
454 case IR::Opcode::GetCbufU32x2: { 503 case IR::Opcode::GetCbufU32x2: {
455 const IR::Value index{inst.Arg(0)}; 504 const IR::Value index{inst.Arg(0)};
456 const IR::Value offset{inst.Arg(1)}; 505 const IR::Value offset{inst.Arg(1)};
457 if (!index.IsImmediate()) { 506 if (index.IsImmediate()) {
458 throw NotImplementedException("Constant buffer with non-immediate index"); 507 AddConstantBufferDescriptor(info, index.U32(), 1);
459 } 508 u32 element_size = GetElementSize(info.used_constant_buffer_types, inst.GetOpcode());
460 AddConstantBufferDescriptor(info, index.U32(), 1); 509 u32& size{info.constant_buffer_used_sizes[index.U32()]};
461 u32 element_size{}; 510 if (offset.IsImmediate()) {
462 switch (inst.GetOpcode()) { 511 size = Common::AlignUp(std::max(size, offset.U32() + element_size), 16u);
463 case IR::Opcode::GetCbufU8: 512 } else {
464 case IR::Opcode::GetCbufS8: 513 size = 0x10'000;
465 info.used_constant_buffer_types |= IR::Type::U8; 514 }
466 element_size = 1;
467 break;
468 case IR::Opcode::GetCbufU16:
469 case IR::Opcode::GetCbufS16:
470 info.used_constant_buffer_types |= IR::Type::U16;
471 element_size = 2;
472 break;
473 case IR::Opcode::GetCbufU32:
474 info.used_constant_buffer_types |= IR::Type::U32;
475 element_size = 4;
476 break;
477 case IR::Opcode::GetCbufF32:
478 info.used_constant_buffer_types |= IR::Type::F32;
479 element_size = 4;
480 break;
481 case IR::Opcode::GetCbufU32x2:
482 info.used_constant_buffer_types |= IR::Type::U32x2;
483 element_size = 8;
484 break;
485 default:
486 break;
487 }
488 u32& size{info.constant_buffer_used_sizes[index.U32()]};
489 if (offset.IsImmediate()) {
490 size = Common::AlignUp(std::max(size, offset.U32() + element_size), 16u);
491 } else { 515 } else {
492 size = 0x10'000; 516 AddRegisterIndexedLdc(info);
517 GetElementSize(info.used_indirect_cbuf_types, inst.GetOpcode());
493 } 518 }
494 break; 519 break;
495 } 520 }
@@ -597,6 +622,15 @@ void VisitUsages(Info& info, IR::Inst& inst) {
597 break; 622 break;
598 case IR::Opcode::LoadStorage64: 623 case IR::Opcode::LoadStorage64:
599 case IR::Opcode::WriteStorage64: 624 case IR::Opcode::WriteStorage64:
625 case IR::Opcode::StorageAtomicIAdd32x2:
626 case IR::Opcode::StorageAtomicSMin32x2:
627 case IR::Opcode::StorageAtomicUMin32x2:
628 case IR::Opcode::StorageAtomicSMax32x2:
629 case IR::Opcode::StorageAtomicUMax32x2:
630 case IR::Opcode::StorageAtomicAnd32x2:
631 case IR::Opcode::StorageAtomicOr32x2:
632 case IR::Opcode::StorageAtomicXor32x2:
633 case IR::Opcode::StorageAtomicExchange32x2:
600 info.used_storage_buffer_types |= IR::Type::U32x2; 634 info.used_storage_buffer_types |= IR::Type::U32x2;
601 break; 635 break;
602 case IR::Opcode::LoadStorage128: 636 case IR::Opcode::LoadStorage128:
diff --git a/src/shader_recompiler/ir_opt/global_memory_to_storage_buffer_pass.cpp b/src/shader_recompiler/ir_opt/global_memory_to_storage_buffer_pass.cpp
index 4197b0095..ddf497e32 100644
--- a/src/shader_recompiler/ir_opt/global_memory_to_storage_buffer_pass.cpp
+++ b/src/shader_recompiler/ir_opt/global_memory_to_storage_buffer_pass.cpp
@@ -92,6 +92,15 @@ bool IsGlobalMemory(const IR::Inst& inst) {
92 case IR::Opcode::GlobalAtomicOr64: 92 case IR::Opcode::GlobalAtomicOr64:
93 case IR::Opcode::GlobalAtomicXor64: 93 case IR::Opcode::GlobalAtomicXor64:
94 case IR::Opcode::GlobalAtomicExchange64: 94 case IR::Opcode::GlobalAtomicExchange64:
95 case IR::Opcode::GlobalAtomicIAdd32x2:
96 case IR::Opcode::GlobalAtomicSMin32x2:
97 case IR::Opcode::GlobalAtomicUMin32x2:
98 case IR::Opcode::GlobalAtomicSMax32x2:
99 case IR::Opcode::GlobalAtomicUMax32x2:
100 case IR::Opcode::GlobalAtomicAnd32x2:
101 case IR::Opcode::GlobalAtomicOr32x2:
102 case IR::Opcode::GlobalAtomicXor32x2:
103 case IR::Opcode::GlobalAtomicExchange32x2:
95 case IR::Opcode::GlobalAtomicAddF32: 104 case IR::Opcode::GlobalAtomicAddF32:
96 case IR::Opcode::GlobalAtomicAddF16x2: 105 case IR::Opcode::GlobalAtomicAddF16x2:
97 case IR::Opcode::GlobalAtomicAddF32x2: 106 case IR::Opcode::GlobalAtomicAddF32x2:
@@ -135,6 +144,15 @@ bool IsGlobalMemoryWrite(const IR::Inst& inst) {
135 case IR::Opcode::GlobalAtomicOr64: 144 case IR::Opcode::GlobalAtomicOr64:
136 case IR::Opcode::GlobalAtomicXor64: 145 case IR::Opcode::GlobalAtomicXor64:
137 case IR::Opcode::GlobalAtomicExchange64: 146 case IR::Opcode::GlobalAtomicExchange64:
147 case IR::Opcode::GlobalAtomicIAdd32x2:
148 case IR::Opcode::GlobalAtomicSMin32x2:
149 case IR::Opcode::GlobalAtomicUMin32x2:
150 case IR::Opcode::GlobalAtomicSMax32x2:
151 case IR::Opcode::GlobalAtomicUMax32x2:
152 case IR::Opcode::GlobalAtomicAnd32x2:
153 case IR::Opcode::GlobalAtomicOr32x2:
154 case IR::Opcode::GlobalAtomicXor32x2:
155 case IR::Opcode::GlobalAtomicExchange32x2:
138 case IR::Opcode::GlobalAtomicAddF32: 156 case IR::Opcode::GlobalAtomicAddF32:
139 case IR::Opcode::GlobalAtomicAddF16x2: 157 case IR::Opcode::GlobalAtomicAddF16x2:
140 case IR::Opcode::GlobalAtomicAddF32x2: 158 case IR::Opcode::GlobalAtomicAddF32x2:
@@ -199,6 +217,8 @@ IR::Opcode GlobalToStorage(IR::Opcode opcode) {
199 return IR::Opcode::StorageAtomicOr32; 217 return IR::Opcode::StorageAtomicOr32;
200 case IR::Opcode::GlobalAtomicXor32: 218 case IR::Opcode::GlobalAtomicXor32:
201 return IR::Opcode::StorageAtomicXor32; 219 return IR::Opcode::StorageAtomicXor32;
220 case IR::Opcode::GlobalAtomicExchange32:
221 return IR::Opcode::StorageAtomicExchange32;
202 case IR::Opcode::GlobalAtomicIAdd64: 222 case IR::Opcode::GlobalAtomicIAdd64:
203 return IR::Opcode::StorageAtomicIAdd64; 223 return IR::Opcode::StorageAtomicIAdd64;
204 case IR::Opcode::GlobalAtomicSMin64: 224 case IR::Opcode::GlobalAtomicSMin64:
@@ -215,10 +235,26 @@ IR::Opcode GlobalToStorage(IR::Opcode opcode) {
215 return IR::Opcode::StorageAtomicOr64; 235 return IR::Opcode::StorageAtomicOr64;
216 case IR::Opcode::GlobalAtomicXor64: 236 case IR::Opcode::GlobalAtomicXor64:
217 return IR::Opcode::StorageAtomicXor64; 237 return IR::Opcode::StorageAtomicXor64;
218 case IR::Opcode::GlobalAtomicExchange32:
219 return IR::Opcode::StorageAtomicExchange32;
220 case IR::Opcode::GlobalAtomicExchange64: 238 case IR::Opcode::GlobalAtomicExchange64:
221 return IR::Opcode::StorageAtomicExchange64; 239 return IR::Opcode::StorageAtomicExchange64;
240 case IR::Opcode::GlobalAtomicIAdd32x2:
241 return IR::Opcode::StorageAtomicIAdd32x2;
242 case IR::Opcode::GlobalAtomicSMin32x2:
243 return IR::Opcode::StorageAtomicSMin32x2;
244 case IR::Opcode::GlobalAtomicUMin32x2:
245 return IR::Opcode::StorageAtomicUMin32x2;
246 case IR::Opcode::GlobalAtomicSMax32x2:
247 return IR::Opcode::StorageAtomicSMax32x2;
248 case IR::Opcode::GlobalAtomicUMax32x2:
249 return IR::Opcode::StorageAtomicUMax32x2;
250 case IR::Opcode::GlobalAtomicAnd32x2:
251 return IR::Opcode::StorageAtomicAnd32x2;
252 case IR::Opcode::GlobalAtomicOr32x2:
253 return IR::Opcode::StorageAtomicOr32x2;
254 case IR::Opcode::GlobalAtomicXor32x2:
255 return IR::Opcode::StorageAtomicXor32x2;
256 case IR::Opcode::GlobalAtomicExchange32x2:
257 return IR::Opcode::StorageAtomicExchange32x2;
222 case IR::Opcode::GlobalAtomicAddF32: 258 case IR::Opcode::GlobalAtomicAddF32:
223 return IR::Opcode::StorageAtomicAddF32; 259 return IR::Opcode::StorageAtomicAddF32;
224 case IR::Opcode::GlobalAtomicAddF16x2: 260 case IR::Opcode::GlobalAtomicAddF16x2:
@@ -298,7 +334,8 @@ std::optional<LowAddrInfo> TrackLowAddress(IR::Inst* inst) {
298/// Tries to track the storage buffer address used by a global memory instruction 334/// Tries to track the storage buffer address used by a global memory instruction
299std::optional<StorageBufferAddr> Track(const IR::Value& value, const Bias* bias) { 335std::optional<StorageBufferAddr> Track(const IR::Value& value, const Bias* bias) {
300 const auto pred{[bias](const IR::Inst* inst) -> std::optional<StorageBufferAddr> { 336 const auto pred{[bias](const IR::Inst* inst) -> std::optional<StorageBufferAddr> {
301 if (inst->GetOpcode() != IR::Opcode::GetCbufU32) { 337 if (inst->GetOpcode() != IR::Opcode::GetCbufU32 &&
338 inst->GetOpcode() != IR::Opcode::GetCbufU32x2) {
302 return std::nullopt; 339 return std::nullopt;
303 } 340 }
304 const IR::Value index{inst->Arg(0)}; 341 const IR::Value index{inst->Arg(0)};
@@ -454,6 +491,15 @@ void Replace(IR::Block& block, IR::Inst& inst, const IR::U32& storage_index,
454 case IR::Opcode::GlobalAtomicOr64: 491 case IR::Opcode::GlobalAtomicOr64:
455 case IR::Opcode::GlobalAtomicXor64: 492 case IR::Opcode::GlobalAtomicXor64:
456 case IR::Opcode::GlobalAtomicExchange64: 493 case IR::Opcode::GlobalAtomicExchange64:
494 case IR::Opcode::GlobalAtomicIAdd32x2:
495 case IR::Opcode::GlobalAtomicSMin32x2:
496 case IR::Opcode::GlobalAtomicUMin32x2:
497 case IR::Opcode::GlobalAtomicSMax32x2:
498 case IR::Opcode::GlobalAtomicUMax32x2:
499 case IR::Opcode::GlobalAtomicAnd32x2:
500 case IR::Opcode::GlobalAtomicOr32x2:
501 case IR::Opcode::GlobalAtomicXor32x2:
502 case IR::Opcode::GlobalAtomicExchange32x2:
457 case IR::Opcode::GlobalAtomicAddF32: 503 case IR::Opcode::GlobalAtomicAddF32:
458 case IR::Opcode::GlobalAtomicAddF16x2: 504 case IR::Opcode::GlobalAtomicAddF16x2:
459 case IR::Opcode::GlobalAtomicAddF32x2: 505 case IR::Opcode::GlobalAtomicAddF32x2:
diff --git a/src/shader_recompiler/ir_opt/lower_int64_to_int32.cpp b/src/shader_recompiler/ir_opt/lower_int64_to_int32.cpp
index e80d3d1d9..c2654cd9b 100644
--- a/src/shader_recompiler/ir_opt/lower_int64_to_int32.cpp
+++ b/src/shader_recompiler/ir_opt/lower_int64_to_int32.cpp
@@ -199,6 +199,26 @@ void Lower(IR::Block& block, IR::Inst& inst) {
199 return ShiftRightLogical64To32(block, inst); 199 return ShiftRightLogical64To32(block, inst);
200 case IR::Opcode::ShiftRightArithmetic64: 200 case IR::Opcode::ShiftRightArithmetic64:
201 return ShiftRightArithmetic64To32(block, inst); 201 return ShiftRightArithmetic64To32(block, inst);
202 case IR::Opcode::SharedAtomicExchange64:
203 return inst.ReplaceOpcode(IR::Opcode::SharedAtomicExchange32x2);
204 case IR::Opcode::GlobalAtomicIAdd64:
205 return inst.ReplaceOpcode(IR::Opcode::GlobalAtomicIAdd32x2);
206 case IR::Opcode::GlobalAtomicSMin64:
207 return inst.ReplaceOpcode(IR::Opcode::GlobalAtomicSMin32x2);
208 case IR::Opcode::GlobalAtomicUMin64:
209 return inst.ReplaceOpcode(IR::Opcode::GlobalAtomicUMin32x2);
210 case IR::Opcode::GlobalAtomicSMax64:
211 return inst.ReplaceOpcode(IR::Opcode::GlobalAtomicSMax32x2);
212 case IR::Opcode::GlobalAtomicUMax64:
213 return inst.ReplaceOpcode(IR::Opcode::GlobalAtomicUMax32x2);
214 case IR::Opcode::GlobalAtomicAnd64:
215 return inst.ReplaceOpcode(IR::Opcode::GlobalAtomicAnd32x2);
216 case IR::Opcode::GlobalAtomicOr64:
217 return inst.ReplaceOpcode(IR::Opcode::GlobalAtomicOr32x2);
218 case IR::Opcode::GlobalAtomicXor64:
219 return inst.ReplaceOpcode(IR::Opcode::GlobalAtomicXor32x2);
220 case IR::Opcode::GlobalAtomicExchange64:
221 return inst.ReplaceOpcode(IR::Opcode::GlobalAtomicExchange32x2);
202 default: 222 default:
203 break; 223 break;
204 } 224 }
diff --git a/src/shader_recompiler/ir_opt/rescaling_pass.cpp b/src/shader_recompiler/ir_opt/rescaling_pass.cpp
index c28500dd1..496d4667e 100644
--- a/src/shader_recompiler/ir_opt/rescaling_pass.cpp
+++ b/src/shader_recompiler/ir_opt/rescaling_pass.cpp
@@ -183,6 +183,31 @@ void ScaleIntegerComposite(IR::IREmitter& ir, IR::Inst& inst, const IR::U1& is_s
183 } 183 }
184} 184}
185 185
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/engines/maxwell_dma.cpp b/src/video_core/engines/maxwell_dma.cpp
index 67388d980..1fc1358bc 100644
--- a/src/video_core/engines/maxwell_dma.cpp
+++ b/src/video_core/engines/maxwell_dma.cpp
@@ -53,7 +53,6 @@ void MaxwellDMA::Launch() {
53 53
54 // TODO(Subv): Perform more research and implement all features of this engine. 54 // TODO(Subv): Perform more research and implement all features of this engine.
55 const LaunchDMA& launch = regs.launch_dma; 55 const LaunchDMA& launch = regs.launch_dma;
56 ASSERT(launch.semaphore_type == LaunchDMA::SemaphoreType::NONE);
57 ASSERT(launch.interrupt_type == LaunchDMA::InterruptType::NONE); 56 ASSERT(launch.interrupt_type == LaunchDMA::InterruptType::NONE);
58 ASSERT(launch.data_transfer_type == LaunchDMA::DataTransferType::NON_PIPELINED); 57 ASSERT(launch.data_transfer_type == LaunchDMA::DataTransferType::NON_PIPELINED);
59 ASSERT(regs.dst_params.origin.x == 0); 58 ASSERT(regs.dst_params.origin.x == 0);
@@ -79,6 +78,7 @@ void MaxwellDMA::Launch() {
79 CopyPitchToBlockLinear(); 78 CopyPitchToBlockLinear();
80 } 79 }
81 } 80 }
81 ReleaseSemaphore();
82} 82}
83 83
84void MaxwellDMA::CopyPitchToPitch() { 84void MaxwellDMA::CopyPitchToPitch() {
@@ -244,4 +244,22 @@ void MaxwellDMA::FastCopyBlockLinearToPitch() {
244 memory_manager.WriteBlock(regs.offset_out, write_buffer.data(), dst_size); 244 memory_manager.WriteBlock(regs.offset_out, write_buffer.data(), dst_size);
245} 245}
246 246
247void MaxwellDMA::ReleaseSemaphore() {
248 const auto type = regs.launch_dma.semaphore_type;
249 const GPUVAddr address = regs.semaphore.address;
250 switch (type) {
251 case LaunchDMA::SemaphoreType::NONE:
252 break;
253 case LaunchDMA::SemaphoreType::RELEASE_ONE_WORD_SEMAPHORE:
254 memory_manager.Write<u32>(address, regs.semaphore.payload);
255 break;
256 case LaunchDMA::SemaphoreType::RELEASE_FOUR_WORD_SEMAPHORE:
257 memory_manager.Write<u64>(address, static_cast<u64>(regs.semaphore.payload));
258 memory_manager.Write<u64>(address + 8, system.GPU().GetTicks());
259 break;
260 default:
261 UNREACHABLE_MSG("Unknown semaphore type: {}", static_cast<u32>(type.Value()));
262 }
263}
264
247} // namespace Tegra::Engines 265} // namespace Tegra::Engines
diff --git a/src/video_core/engines/maxwell_dma.h b/src/video_core/engines/maxwell_dma.h
index a04514425..2692cac8a 100644
--- a/src/video_core/engines/maxwell_dma.h
+++ b/src/video_core/engines/maxwell_dma.h
@@ -224,6 +224,8 @@ private:
224 224
225 void FastCopyBlockLinearToPitch(); 225 void FastCopyBlockLinearToPitch();
226 226
227 void ReleaseSemaphore();
228
227 Core::System& system; 229 Core::System& system;
228 230
229 MemoryManager& memory_manager; 231 MemoryManager& memory_manager;
diff --git a/src/video_core/fence_manager.h b/src/video_core/fence_manager.h
index 34dc6c596..f80d62c80 100644
--- a/src/video_core/fence_manager.h
+++ b/src/video_core/fence_manager.h
@@ -8,8 +8,6 @@
8#include <queue> 8#include <queue>
9 9
10#include "common/common_types.h" 10#include "common/common_types.h"
11#include "common/settings.h"
12#include "core/core.h"
13#include "video_core/delayed_destruction_ring.h" 11#include "video_core/delayed_destruction_ring.h"
14#include "video_core/gpu.h" 12#include "video_core/gpu.h"
15#include "video_core/memory_manager.h" 13#include "video_core/memory_manager.h"
diff --git a/src/video_core/host_shaders/CMakeLists.txt b/src/video_core/host_shaders/CMakeLists.txt
index fd3e41434..af05d47d1 100644
--- a/src/video_core/host_shaders/CMakeLists.txt
+++ b/src/video_core/host_shaders/CMakeLists.txt
@@ -14,6 +14,7 @@ set(SHADER_FILES
14 convert_d24s8_to_abgr8.frag 14 convert_d24s8_to_abgr8.frag
15 convert_depth_to_float.frag 15 convert_depth_to_float.frag
16 convert_float_to_depth.frag 16 convert_float_to_depth.frag
17 convert_s8d24_to_abgr8.frag
17 full_screen_triangle.vert 18 full_screen_triangle.vert
18 fxaa.frag 19 fxaa.frag
19 fxaa.vert 20 fxaa.vert
diff --git a/src/video_core/host_shaders/convert_s8d24_to_abgr8.frag b/src/video_core/host_shaders/convert_s8d24_to_abgr8.frag
new file mode 100644
index 000000000..c8a1683b8
--- /dev/null
+++ b/src/video_core/host_shaders/convert_s8d24_to_abgr8.frag
@@ -0,0 +1,23 @@
1// Copyright 2022 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#version 450
6
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_base.h b/src/video_core/renderer_base.h
index bb204454e..c5f974080 100644
--- a/src/video_core/renderer_base.h
+++ b/src/video_core/renderer_base.h
@@ -5,9 +5,10 @@
5#pragma once 5#pragma once
6 6
7#include <atomic> 7#include <atomic>
8#include <functional>
8#include <memory> 9#include <memory>
9#include <optional>
10 10
11#include "common/common_funcs.h"
11#include "common/common_types.h" 12#include "common/common_types.h"
12#include "core/frontend/emu_window.h" 13#include "core/frontend/emu_window.h"
13#include "video_core/gpu.h" 14#include "video_core/gpu.h"
@@ -28,8 +29,11 @@ struct RendererSettings {
28 Layout::FramebufferLayout screenshot_framebuffer_layout; 29 Layout::FramebufferLayout screenshot_framebuffer_layout;
29}; 30};
30 31
31class RendererBase : NonCopyable { 32class RendererBase {
32public: 33public:
34 YUZU_NON_COPYABLE(RendererBase);
35 YUZU_NON_MOVEABLE(RendererBase);
36
33 explicit RendererBase(Core::Frontend::EmuWindow& window, 37 explicit RendererBase(Core::Frontend::EmuWindow& window,
34 std::unique_ptr<Core::Frontend::GraphicsContext> context); 38 std::unique_ptr<Core::Frontend::GraphicsContext> context);
35 virtual ~RendererBase(); 39 virtual ~RendererBase();
diff --git a/src/video_core/renderer_opengl/gl_fence_manager.cpp b/src/video_core/renderer_opengl/gl_fence_manager.cpp
index 151290101..293ad7d59 100644
--- a/src/video_core/renderer_opengl/gl_fence_manager.cpp
+++ b/src/video_core/renderer_opengl/gl_fence_manager.cpp
@@ -31,9 +31,8 @@ bool GLInnerFence::IsSignaled() const {
31 return true; 31 return true;
32 } 32 }
33 ASSERT(sync_object.handle != 0); 33 ASSERT(sync_object.handle != 0);
34 GLsizei length;
35 GLint sync_status; 34 GLint sync_status;
36 glGetSynciv(sync_object.handle, GL_SYNC_STATUS, sizeof(GLint), &length, &sync_status); 35 glGetSynciv(sync_object.handle, GL_SYNC_STATUS, 1, nullptr, &sync_status);
37 return sync_status == GL_SIGNALED; 36 return sync_status == GL_SIGNALED;
38} 37}
39 38
diff --git a/src/video_core/renderer_opengl/gl_graphics_pipeline.cpp b/src/video_core/renderer_opengl/gl_graphics_pipeline.cpp
index f8495896c..9e6732abd 100644
--- a/src/video_core/renderer_opengl/gl_graphics_pipeline.cpp
+++ b/src/video_core/renderer_opengl/gl_graphics_pipeline.cpp
@@ -243,10 +243,6 @@ GraphicsPipeline::GraphicsPipeline(
243 case Settings::ShaderBackend::GLASM: 243 case Settings::ShaderBackend::GLASM:
244 if (!sources[stage].empty()) { 244 if (!sources[stage].empty()) {
245 assembly_programs[stage] = CompileProgram(sources[stage], AssemblyStage(stage)); 245 assembly_programs[stage] = CompileProgram(sources[stage], AssemblyStage(stage));
246 if (in_parallel) {
247 // Make sure program is built before continuing when building in parallel
248 glGetString(GL_PROGRAM_ERROR_STRING_NV);
249 }
250 } 246 }
251 break; 247 break;
252 case Settings::ShaderBackend::SPIRV: 248 case Settings::ShaderBackend::SPIRV:
@@ -256,20 +252,18 @@ GraphicsPipeline::GraphicsPipeline(
256 break; 252 break;
257 } 253 }
258 } 254 }
259 if (in_parallel && backend != Settings::ShaderBackend::GLASM) { 255 if (in_parallel) {
260 // Make sure programs have built if we are building shaders in parallel 256 std::lock_guard lock{built_mutex};
261 for (OGLProgram& program : source_programs) { 257 built_fence.Create();
262 if (program.handle != 0) { 258 // Flush this context to ensure compilation commands and fence are in the GPU pipe.
263 GLint status{}; 259 glFlush();
264 glGetProgramiv(program.handle, GL_LINK_STATUS, &status); 260 built_condvar.notify_one();
265 } 261 } else {
266 } 262 is_built = true;
267 } 263 }
268 if (shader_notify) { 264 if (shader_notify) {
269 shader_notify->MarkShaderComplete(); 265 shader_notify->MarkShaderComplete();
270 } 266 }
271 is_built = true;
272 built_condvar.notify_one();
273 }}; 267 }};
274 if (thread_worker) { 268 if (thread_worker) {
275 thread_worker->QueueWork(std::move(func)); 269 thread_worker->QueueWork(std::move(func));
@@ -440,7 +434,7 @@ void GraphicsPipeline::ConfigureImpl(bool is_indexed) {
440 buffer_cache.UpdateGraphicsBuffers(is_indexed); 434 buffer_cache.UpdateGraphicsBuffers(is_indexed);
441 buffer_cache.BindHostGeometryBuffers(is_indexed); 435 buffer_cache.BindHostGeometryBuffers(is_indexed);
442 436
443 if (!is_built.load(std::memory_order::relaxed)) { 437 if (!IsBuilt()) {
444 WaitForBuild(); 438 WaitForBuild();
445 } 439 }
446 const bool use_assembly{assembly_programs[0].handle != 0}; 440 const bool use_assembly{assembly_programs[0].handle != 0};
@@ -585,8 +579,26 @@ void GraphicsPipeline::GenerateTransformFeedbackState() {
585} 579}
586 580
587void GraphicsPipeline::WaitForBuild() { 581void GraphicsPipeline::WaitForBuild() {
588 std::unique_lock lock{built_mutex}; 582 if (built_fence.handle == 0) {
589 built_condvar.wait(lock, [this] { return is_built.load(std::memory_order::relaxed); }); 583 std::unique_lock lock{built_mutex};
584 built_condvar.wait(lock, [this] { return built_fence.handle != 0; });
585 }
586 ASSERT(glClientWaitSync(built_fence.handle, 0, GL_TIMEOUT_IGNORED) != GL_WAIT_FAILED);
587 is_built = true;
588}
589
590bool GraphicsPipeline::IsBuilt() noexcept {
591 if (is_built) {
592 return true;
593 }
594 if (built_fence.handle == 0) {
595 return false;
596 }
597 // Timeout of zero means this is non-blocking
598 const auto sync_status = glClientWaitSync(built_fence.handle, 0, 0);
599 ASSERT(sync_status != GL_WAIT_FAILED);
600 is_built = sync_status != GL_TIMEOUT_EXPIRED;
601 return is_built;
590} 602}
591 603
592} // namespace OpenGL 604} // namespace OpenGL
diff --git a/src/video_core/renderer_opengl/gl_graphics_pipeline.h b/src/video_core/renderer_opengl/gl_graphics_pipeline.h
index 4e28d9a42..311d49f3f 100644
--- a/src/video_core/renderer_opengl/gl_graphics_pipeline.h
+++ b/src/video_core/renderer_opengl/gl_graphics_pipeline.h
@@ -100,9 +100,7 @@ public:
100 return writes_global_memory; 100 return writes_global_memory;
101 } 101 }
102 102
103 [[nodiscard]] bool IsBuilt() const noexcept { 103 [[nodiscard]] bool IsBuilt() noexcept;
104 return is_built.load(std::memory_order::relaxed);
105 }
106 104
107 template <typename Spec> 105 template <typename Spec>
108 static auto MakeConfigureSpecFunc() { 106 static auto MakeConfigureSpecFunc() {
@@ -154,7 +152,8 @@ private:
154 152
155 std::mutex built_mutex; 153 std::mutex built_mutex;
156 std::condition_variable built_condvar; 154 std::condition_variable built_condvar;
157 std::atomic_bool is_built{false}; 155 OGLSync built_fence{};
156 bool is_built{false};
158}; 157};
159 158
160} // namespace OpenGL 159} // namespace OpenGL
diff --git a/src/video_core/renderer_opengl/gl_resource_manager.h b/src/video_core/renderer_opengl/gl_resource_manager.h
index b2d5bfd3b..84e07f8bd 100644
--- a/src/video_core/renderer_opengl/gl_resource_manager.h
+++ b/src/video_core/renderer_opengl/gl_resource_manager.h
@@ -7,12 +7,14 @@
7#include <string_view> 7#include <string_view>
8#include <utility> 8#include <utility>
9#include <glad/glad.h> 9#include <glad/glad.h>
10#include "common/common_types.h" 10#include "common/common_funcs.h"
11 11
12namespace OpenGL { 12namespace OpenGL {
13 13
14class OGLRenderbuffer : private NonCopyable { 14class OGLRenderbuffer final {
15public: 15public:
16 YUZU_NON_COPYABLE(OGLRenderbuffer);
17
16 OGLRenderbuffer() = default; 18 OGLRenderbuffer() = default;
17 19
18 OGLRenderbuffer(OGLRenderbuffer&& o) noexcept : handle(std::exchange(o.handle, 0)) {} 20 OGLRenderbuffer(OGLRenderbuffer&& o) noexcept : handle(std::exchange(o.handle, 0)) {}
@@ -36,8 +38,10 @@ public:
36 GLuint handle = 0; 38 GLuint handle = 0;
37}; 39};
38 40
39class OGLTexture : private NonCopyable { 41class OGLTexture final {
40public: 42public:
43 YUZU_NON_COPYABLE(OGLTexture);
44
41 OGLTexture() = default; 45 OGLTexture() = default;
42 46
43 OGLTexture(OGLTexture&& o) noexcept : handle(std::exchange(o.handle, 0)) {} 47 OGLTexture(OGLTexture&& o) noexcept : handle(std::exchange(o.handle, 0)) {}
@@ -61,8 +65,10 @@ public:
61 GLuint handle = 0; 65 GLuint handle = 0;
62}; 66};
63 67
64class OGLTextureView : private NonCopyable { 68class OGLTextureView final {
65public: 69public:
70 YUZU_NON_COPYABLE(OGLTextureView);
71
66 OGLTextureView() = default; 72 OGLTextureView() = default;
67 73
68 OGLTextureView(OGLTextureView&& o) noexcept : handle(std::exchange(o.handle, 0)) {} 74 OGLTextureView(OGLTextureView&& o) noexcept : handle(std::exchange(o.handle, 0)) {}
@@ -86,8 +92,10 @@ public:
86 GLuint handle = 0; 92 GLuint handle = 0;
87}; 93};
88 94
89class OGLSampler : private NonCopyable { 95class OGLSampler final {
90public: 96public:
97 YUZU_NON_COPYABLE(OGLSampler);
98
91 OGLSampler() = default; 99 OGLSampler() = default;
92 100
93 OGLSampler(OGLSampler&& o) noexcept : handle(std::exchange(o.handle, 0)) {} 101 OGLSampler(OGLSampler&& o) noexcept : handle(std::exchange(o.handle, 0)) {}
@@ -111,8 +119,10 @@ public:
111 GLuint handle = 0; 119 GLuint handle = 0;
112}; 120};
113 121
114class OGLShader : private NonCopyable { 122class OGLShader final {
115public: 123public:
124 YUZU_NON_COPYABLE(OGLShader);
125
116 OGLShader() = default; 126 OGLShader() = default;
117 127
118 OGLShader(OGLShader&& o) noexcept : handle(std::exchange(o.handle, 0)) {} 128 OGLShader(OGLShader&& o) noexcept : handle(std::exchange(o.handle, 0)) {}
@@ -132,8 +142,10 @@ public:
132 GLuint handle = 0; 142 GLuint handle = 0;
133}; 143};
134 144
135class OGLProgram : private NonCopyable { 145class OGLProgram final {
136public: 146public:
147 YUZU_NON_COPYABLE(OGLProgram);
148
137 OGLProgram() = default; 149 OGLProgram() = default;
138 150
139 OGLProgram(OGLProgram&& o) noexcept : handle(std::exchange(o.handle, 0)) {} 151 OGLProgram(OGLProgram&& o) noexcept : handle(std::exchange(o.handle, 0)) {}
@@ -154,8 +166,10 @@ public:
154 GLuint handle = 0; 166 GLuint handle = 0;
155}; 167};
156 168
157class OGLAssemblyProgram : private NonCopyable { 169class OGLAssemblyProgram final {
158public: 170public:
171 YUZU_NON_COPYABLE(OGLAssemblyProgram);
172
159 OGLAssemblyProgram() = default; 173 OGLAssemblyProgram() = default;
160 174
161 OGLAssemblyProgram(OGLAssemblyProgram&& o) noexcept : handle(std::exchange(o.handle, 0)) {} 175 OGLAssemblyProgram(OGLAssemblyProgram&& o) noexcept : handle(std::exchange(o.handle, 0)) {}
@@ -176,8 +190,10 @@ public:
176 GLuint handle = 0; 190 GLuint handle = 0;
177}; 191};
178 192
179class OGLPipeline : private NonCopyable { 193class OGLPipeline final {
180public: 194public:
195 YUZU_NON_COPYABLE(OGLPipeline);
196
181 OGLPipeline() = default; 197 OGLPipeline() = default;
182 OGLPipeline(OGLPipeline&& o) noexcept : handle{std::exchange<GLuint>(o.handle, 0)} {} 198 OGLPipeline(OGLPipeline&& o) noexcept : handle{std::exchange<GLuint>(o.handle, 0)} {}
183 199
@@ -198,8 +214,10 @@ public:
198 GLuint handle = 0; 214 GLuint handle = 0;
199}; 215};
200 216
201class OGLBuffer : private NonCopyable { 217class OGLBuffer final {
202public: 218public:
219 YUZU_NON_COPYABLE(OGLBuffer);
220
203 OGLBuffer() = default; 221 OGLBuffer() = default;
204 222
205 OGLBuffer(OGLBuffer&& o) noexcept : handle(std::exchange(o.handle, 0)) {} 223 OGLBuffer(OGLBuffer&& o) noexcept : handle(std::exchange(o.handle, 0)) {}
@@ -223,8 +241,10 @@ public:
223 GLuint handle = 0; 241 GLuint handle = 0;
224}; 242};
225 243
226class OGLSync : private NonCopyable { 244class OGLSync final {
227public: 245public:
246 YUZU_NON_COPYABLE(OGLSync);
247
228 OGLSync() = default; 248 OGLSync() = default;
229 249
230 OGLSync(OGLSync&& o) noexcept : handle(std::exchange(o.handle, nullptr)) {} 250 OGLSync(OGLSync&& o) noexcept : handle(std::exchange(o.handle, nullptr)) {}
@@ -247,8 +267,10 @@ public:
247 GLsync handle = 0; 267 GLsync handle = 0;
248}; 268};
249 269
250class OGLFramebuffer : private NonCopyable { 270class OGLFramebuffer final {
251public: 271public:
272 YUZU_NON_COPYABLE(OGLFramebuffer);
273
252 OGLFramebuffer() = default; 274 OGLFramebuffer() = default;
253 275
254 OGLFramebuffer(OGLFramebuffer&& o) noexcept : handle(std::exchange(o.handle, 0)) {} 276 OGLFramebuffer(OGLFramebuffer&& o) noexcept : handle(std::exchange(o.handle, 0)) {}
@@ -272,8 +294,10 @@ public:
272 GLuint handle = 0; 294 GLuint handle = 0;
273}; 295};
274 296
275class OGLQuery : private NonCopyable { 297class OGLQuery final {
276public: 298public:
299 YUZU_NON_COPYABLE(OGLQuery);
300
277 OGLQuery() = default; 301 OGLQuery() = default;
278 302
279 OGLQuery(OGLQuery&& o) noexcept : handle(std::exchange(o.handle, 0)) {} 303 OGLQuery(OGLQuery&& o) noexcept : handle(std::exchange(o.handle, 0)) {}
diff --git a/src/video_core/renderer_opengl/gl_state_tracker.h b/src/video_core/renderer_opengl/gl_state_tracker.h
index 5864c7c07..550ed6d36 100644
--- a/src/video_core/renderer_opengl/gl_state_tracker.h
+++ b/src/video_core/renderer_opengl/gl_state_tracker.h
@@ -9,7 +9,6 @@
9#include <glad/glad.h> 9#include <glad/glad.h>
10 10
11#include "common/common_types.h" 11#include "common/common_types.h"
12#include "core/core.h"
13#include "video_core/dirty_flags.h" 12#include "video_core/dirty_flags.h"
14#include "video_core/engines/maxwell_3d.h" 13#include "video_core/engines/maxwell_3d.h"
15 14
diff --git a/src/video_core/renderer_opengl/maxwell_to_gl.h b/src/video_core/renderer_opengl/maxwell_to_gl.h
index daba42ed9..db5bf1d30 100644
--- a/src/video_core/renderer_opengl/maxwell_to_gl.h
+++ b/src/video_core/renderer_opengl/maxwell_to_gl.h
@@ -184,6 +184,8 @@ inline GLenum VertexFormat(Maxwell::VertexAttribute attrib) {
184 case Maxwell::VertexAttribute::Size::Size_32_32_32: 184 case Maxwell::VertexAttribute::Size::Size_32_32_32:
185 case Maxwell::VertexAttribute::Size::Size_32_32_32_32: 185 case Maxwell::VertexAttribute::Size::Size_32_32_32_32:
186 return GL_FLOAT; 186 return GL_FLOAT;
187 case Maxwell::VertexAttribute::Size::Size_11_11_10:
188 return GL_UNSIGNED_INT_10F_11F_11F_REV;
187 default: 189 default:
188 break; 190 break;
189 } 191 }
diff --git a/src/video_core/renderer_vulkan/blit_image.cpp b/src/video_core/renderer_vulkan/blit_image.cpp
index 2c3914459..ec03cca38 100644
--- a/src/video_core/renderer_vulkan/blit_image.cpp
+++ b/src/video_core/renderer_vulkan/blit_image.cpp
@@ -9,6 +9,7 @@
9#include "video_core/host_shaders/convert_d24s8_to_abgr8_frag_spv.h" 9#include "video_core/host_shaders/convert_d24s8_to_abgr8_frag_spv.h"
10#include "video_core/host_shaders/convert_depth_to_float_frag_spv.h" 10#include "video_core/host_shaders/convert_depth_to_float_frag_spv.h"
11#include "video_core/host_shaders/convert_float_to_depth_frag_spv.h" 11#include "video_core/host_shaders/convert_float_to_depth_frag_spv.h"
12#include "video_core/host_shaders/convert_s8d24_to_abgr8_frag_spv.h"
12#include "video_core/host_shaders/full_screen_triangle_vert_spv.h" 13#include "video_core/host_shaders/full_screen_triangle_vert_spv.h"
13#include "video_core/host_shaders/vulkan_blit_color_float_frag_spv.h" 14#include "video_core/host_shaders/vulkan_blit_color_float_frag_spv.h"
14#include "video_core/host_shaders/vulkan_blit_depth_stencil_frag_spv.h" 15#include "video_core/host_shaders/vulkan_blit_depth_stencil_frag_spv.h"
@@ -370,6 +371,7 @@ BlitImageHelper::BlitImageHelper(const Device& device_, VKScheduler& scheduler_,
370 convert_float_to_depth_frag(BuildShader(device, CONVERT_FLOAT_TO_DEPTH_FRAG_SPV)), 371 convert_float_to_depth_frag(BuildShader(device, CONVERT_FLOAT_TO_DEPTH_FRAG_SPV)),
371 convert_abgr8_to_d24s8_frag(BuildShader(device, CONVERT_ABGR8_TO_D24S8_FRAG_SPV)), 372 convert_abgr8_to_d24s8_frag(BuildShader(device, CONVERT_ABGR8_TO_D24S8_FRAG_SPV)),
372 convert_d24s8_to_abgr8_frag(BuildShader(device, CONVERT_D24S8_TO_ABGR8_FRAG_SPV)), 373 convert_d24s8_to_abgr8_frag(BuildShader(device, CONVERT_D24S8_TO_ABGR8_FRAG_SPV)),
374 convert_s8d24_to_abgr8_frag(BuildShader(device, CONVERT_S8D24_TO_ABGR8_FRAG_SPV)),
373 linear_sampler(device.GetLogical().CreateSampler(SAMPLER_CREATE_INFO<VK_FILTER_LINEAR>)), 375 linear_sampler(device.GetLogical().CreateSampler(SAMPLER_CREATE_INFO<VK_FILTER_LINEAR>)),
374 nearest_sampler(device.GetLogical().CreateSampler(SAMPLER_CREATE_INFO<VK_FILTER_NEAREST>)) { 376 nearest_sampler(device.GetLogical().CreateSampler(SAMPLER_CREATE_INFO<VK_FILTER_NEAREST>)) {
375 if (device.IsExtShaderStencilExportSupported()) { 377 if (device.IsExtShaderStencilExportSupported()) {
@@ -474,6 +476,13 @@ void BlitImageHelper::ConvertD24S8ToABGR8(const Framebuffer* dst_framebuffer,
474 ConvertDepthStencil(*convert_d24s8_to_abgr8_pipeline, dst_framebuffer, src_image_view); 476 ConvertDepthStencil(*convert_d24s8_to_abgr8_pipeline, dst_framebuffer, src_image_view);
475} 477}
476 478
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/maxwell_to_vk.cpp b/src/video_core/renderer_vulkan/maxwell_to_vk.cpp
index 751e4792b..1c136c410 100644
--- a/src/video_core/renderer_vulkan/maxwell_to_vk.cpp
+++ b/src/video_core/renderer_vulkan/maxwell_to_vk.cpp
@@ -495,6 +495,8 @@ VkFormat VertexFormat(Maxwell::VertexAttribute::Type type, Maxwell::VertexAttrib
495 return VK_FORMAT_R32G32B32_SFLOAT; 495 return VK_FORMAT_R32G32B32_SFLOAT;
496 case Maxwell::VertexAttribute::Size::Size_32_32_32_32: 496 case Maxwell::VertexAttribute::Size::Size_32_32_32_32:
497 return VK_FORMAT_R32G32B32A32_SFLOAT; 497 return VK_FORMAT_R32G32B32A32_SFLOAT;
498 case Maxwell::VertexAttribute::Size::Size_11_11_10:
499 return VK_FORMAT_B10G11R11_UFLOAT_PACK32;
498 default: 500 default:
499 break; 501 break;
500 } 502 }
diff --git a/src/video_core/renderer_vulkan/vk_blit_screen.cpp b/src/video_core/renderer_vulkan/vk_blit_screen.cpp
index c71a1f44d..621a6a071 100644
--- a/src/video_core/renderer_vulkan/vk_blit_screen.cpp
+++ b/src/video_core/renderer_vulkan/vk_blit_screen.cpp
@@ -100,6 +100,8 @@ VkFormat GetFormat(const Tegra::FramebufferConfig& framebuffer) {
100 return VK_FORMAT_A8B8G8R8_UNORM_PACK32; 100 return VK_FORMAT_A8B8G8R8_UNORM_PACK32;
101 case Tegra::FramebufferConfig::PixelFormat::RGB565_UNORM: 101 case Tegra::FramebufferConfig::PixelFormat::RGB565_UNORM:
102 return VK_FORMAT_R5G6B5_UNORM_PACK16; 102 return VK_FORMAT_R5G6B5_UNORM_PACK16;
103 case Tegra::FramebufferConfig::PixelFormat::B8G8R8A8_UNORM:
104 return VK_FORMAT_B8G8R8A8_UNORM;
103 default: 105 default:
104 UNIMPLEMENTED_MSG("Unknown framebuffer pixel format: {}", 106 UNIMPLEMENTED_MSG("Unknown framebuffer pixel format: {}",
105 static_cast<u32>(framebuffer.pixel_format)); 107 static_cast<u32>(framebuffer.pixel_format));
diff --git a/src/video_core/renderer_vulkan/vk_compute_pass.cpp b/src/video_core/renderer_vulkan/vk_compute_pass.cpp
index 3e96c0f60..4d73427b4 100644
--- a/src/video_core/renderer_vulkan/vk_compute_pass.cpp
+++ b/src/video_core/renderer_vulkan/vk_compute_pass.cpp
@@ -2,6 +2,7 @@
2// Licensed under GPLv2 or any later version 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <array>
5#include <cstring> 6#include <cstring>
6#include <memory> 7#include <memory>
7#include <optional> 8#include <optional>
@@ -292,7 +293,7 @@ std::pair<VkBuffer, VkDeviceSize> QuadIndexedPass::Assemble(
292 .srcAccessMask = VK_ACCESS_SHADER_WRITE_BIT, 293 .srcAccessMask = VK_ACCESS_SHADER_WRITE_BIT,
293 .dstAccessMask = VK_ACCESS_INDEX_READ_BIT, 294 .dstAccessMask = VK_ACCESS_INDEX_READ_BIT,
294 }; 295 };
295 const std::array push_constants{base_vertex, index_shift}; 296 const std::array<u32, 2> push_constants{base_vertex, index_shift};
296 const VkDescriptorSet set = descriptor_allocator.Commit(); 297 const VkDescriptorSet set = descriptor_allocator.Commit();
297 device.GetLogical().UpdateDescriptorSet(set, *descriptor_template, descriptor_data); 298 device.GetLogical().UpdateDescriptorSet(set, *descriptor_template, descriptor_data);
298 cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_COMPUTE, *pipeline); 299 cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_COMPUTE, *pipeline);
diff --git a/src/video_core/renderer_vulkan/vk_scheduler.cpp b/src/video_core/renderer_vulkan/vk_scheduler.cpp
index 3bfdf41ba..7d9d4f7ba 100644
--- a/src/video_core/renderer_vulkan/vk_scheduler.cpp
+++ b/src/video_core/renderer_vulkan/vk_scheduler.cpp
@@ -140,12 +140,12 @@ bool VKScheduler::UpdateRescaling(bool is_rescaling) {
140void VKScheduler::WorkerThread(std::stop_token stop_token) { 140void VKScheduler::WorkerThread(std::stop_token stop_token) {
141 Common::SetCurrentThreadName("yuzu:VulkanWorker"); 141 Common::SetCurrentThreadName("yuzu:VulkanWorker");
142 do { 142 do {
143 if (work_queue.empty()) {
144 wait_cv.notify_all();
145 }
146 std::unique_ptr<CommandChunk> work; 143 std::unique_ptr<CommandChunk> work;
147 { 144 {
148 std::unique_lock lock{work_mutex}; 145 std::unique_lock lock{work_mutex};
146 if (work_queue.empty()) {
147 wait_cv.notify_all();
148 }
149 work_cv.wait(lock, stop_token, [this] { return !work_queue.empty(); }); 149 work_cv.wait(lock, stop_token, [this] { return !work_queue.empty(); });
150 if (stop_token.stop_requested()) { 150 if (stop_token.stop_requested()) {
151 continue; 151 continue;
diff --git a/src/video_core/renderer_vulkan/vk_scheduler.h b/src/video_core/renderer_vulkan/vk_scheduler.h
index 1b06c9296..e69aa136b 100644
--- a/src/video_core/renderer_vulkan/vk_scheduler.h
+++ b/src/video_core/renderer_vulkan/vk_scheduler.h
@@ -146,6 +146,7 @@ private:
146 using FuncType = TypedCommand<T>; 146 using FuncType = TypedCommand<T>;
147 static_assert(sizeof(FuncType) < sizeof(data), "Lambda is too large"); 147 static_assert(sizeof(FuncType) < sizeof(data), "Lambda is too large");
148 148
149 recorded_counts++;
149 command_offset = Common::AlignUp(command_offset, alignof(FuncType)); 150 command_offset = Common::AlignUp(command_offset, alignof(FuncType));
150 if (command_offset > sizeof(data) - sizeof(FuncType)) { 151 if (command_offset > sizeof(data) - sizeof(FuncType)) {
151 return false; 152 return false;
@@ -167,7 +168,7 @@ private:
167 } 168 }
168 169
169 bool Empty() const { 170 bool Empty() const {
170 return command_offset == 0; 171 return recorded_counts == 0;
171 } 172 }
172 173
173 bool HasSubmit() const { 174 bool HasSubmit() const {
@@ -178,6 +179,7 @@ private:
178 Command* first = nullptr; 179 Command* first = nullptr;
179 Command* last = nullptr; 180 Command* last = nullptr;
180 181
182 size_t recorded_counts = 0;
181 size_t command_offset = 0; 183 size_t command_offset = 0;
182 bool submit = false; 184 bool submit = false;
183 alignas(std::max_align_t) std::array<u8, 0x8000> data{}; 185 alignas(std::max_align_t) std::array<u8, 0x8000> data{};
diff --git a/src/video_core/renderer_vulkan/vk_state_tracker.h b/src/video_core/renderer_vulkan/vk_state_tracker.h
index 40a149832..8240c83e1 100644
--- a/src/video_core/renderer_vulkan/vk_state_tracker.h
+++ b/src/video_core/renderer_vulkan/vk_state_tracker.h
@@ -8,7 +8,6 @@
8#include <limits> 8#include <limits>
9 9
10#include "common/common_types.h" 10#include "common/common_types.h"
11#include "core/core.h"
12#include "video_core/dirty_flags.h" 11#include "video_core/dirty_flags.h"
13#include "video_core/engines/maxwell_3d.h" 12#include "video_core/engines/maxwell_3d.h"
14 13
diff --git a/src/video_core/renderer_vulkan/vk_texture_cache.cpp b/src/video_core/renderer_vulkan/vk_texture_cache.cpp
index 0ba56ff1e..8101eb42c 100644
--- a/src/video_core/renderer_vulkan/vk_texture_cache.cpp
+++ b/src/video_core/renderer_vulkan/vk_texture_cache.cpp
@@ -554,10 +554,12 @@ void CopyBufferToImage(vk::CommandBuffer cmdbuf, VkBuffer src_buffer, VkImage im
554 }; 554 };
555} 555}
556 556
557[[nodiscard]] bool IsFormatFlipped(PixelFormat format) { 557[[nodiscard]] bool IsFormatFlipped(PixelFormat format, bool emulate_bgr565) {
558 switch (format) { 558 switch (format) {
559 case PixelFormat::A1B5G5R5_UNORM: 559 case PixelFormat::A1B5G5R5_UNORM:
560 return true; 560 return true;
561 case PixelFormat::B5G6R5_UNORM:
562 return emulate_bgr565;
561 default: 563 default:
562 return false; 564 return false;
563 } 565 }
@@ -1068,6 +1070,9 @@ void TextureCacheRuntime::ConvertImage(Framebuffer* dst, ImageView& dst_view, Im
1068 if (src_view.format == PixelFormat::S8_UINT_D24_UNORM) { 1070 if (src_view.format == PixelFormat::S8_UINT_D24_UNORM) {
1069 return blit_image_helper.ConvertD24S8ToABGR8(dst, src_view); 1071 return blit_image_helper.ConvertD24S8ToABGR8(dst, src_view);
1070 } 1072 }
1073 if (src_view.format == PixelFormat::D24_UNORM_S8_UINT) {
1074 return blit_image_helper.ConvertS8D24ToABGR8(dst, src_view);
1075 }
1071 break; 1076 break;
1072 case PixelFormat::R32_FLOAT: 1077 case PixelFormat::R32_FLOAT:
1073 if (src_view.format == PixelFormat::D32_FLOAT) { 1078 if (src_view.format == PixelFormat::D32_FLOAT) {
@@ -1488,7 +1493,7 @@ ImageView::ImageView(TextureCacheRuntime& runtime, const VideoCommon::ImageViewI
1488 }; 1493 };
1489 if (!info.IsRenderTarget()) { 1494 if (!info.IsRenderTarget()) {
1490 swizzle = info.Swizzle(); 1495 swizzle = info.Swizzle();
1491 if (IsFormatFlipped(format)) { 1496 if (IsFormatFlipped(format, device->MustEmulateBGR565())) {
1492 std::ranges::transform(swizzle, swizzle.begin(), SwapBlueRed); 1497 std::ranges::transform(swizzle, swizzle.begin(), SwapBlueRed);
1493 } 1498 }
1494 if ((aspect_mask & (VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT)) != 0) { 1499 if ((aspect_mask & (VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT)) != 0) {
diff --git a/src/video_core/video_core.cpp b/src/video_core/video_core.cpp
index 329bf4def..2f2594585 100644
--- a/src/video_core/video_core.cpp
+++ b/src/video_core/video_core.cpp
@@ -50,6 +50,7 @@ std::unique_ptr<Tegra::GPU> CreateGPU(Core::Frontend::EmuWindow& emu_window, Cor
50 gpu->BindRenderer(std::move(renderer)); 50 gpu->BindRenderer(std::move(renderer));
51 return gpu; 51 return gpu;
52 } catch (const std::runtime_error& exception) { 52 } catch (const std::runtime_error& exception) {
53 scope.Cancel();
53 LOG_ERROR(HW_GPU, "Failed to initialize GPU: {}", exception.what()); 54 LOG_ERROR(HW_GPU, "Failed to initialize GPU: {}", exception.what());
54 return nullptr; 55 return nullptr;
55 } 56 }
diff --git a/src/video_core/vulkan_common/vulkan_device.cpp b/src/video_core/vulkan_common/vulkan_device.cpp
index 153702c0b..effde73c9 100644
--- a/src/video_core/vulkan_common/vulkan_device.cpp
+++ b/src/video_core/vulkan_common/vulkan_device.cpp
@@ -39,6 +39,11 @@ constexpr std::array DEPTH16_UNORM_STENCIL8_UINT{
39 VK_FORMAT_D32_SFLOAT_S8_UINT, 39 VK_FORMAT_D32_SFLOAT_S8_UINT,
40 VK_FORMAT_UNDEFINED, 40 VK_FORMAT_UNDEFINED,
41}; 41};
42
43constexpr std::array B5G6R5_UNORM_PACK16{
44 VK_FORMAT_R5G6B5_UNORM_PACK16,
45 VK_FORMAT_UNDEFINED,
46};
42} // namespace Alternatives 47} // namespace Alternatives
43 48
44enum class NvidiaArchitecture { 49enum class NvidiaArchitecture {
@@ -87,6 +92,8 @@ constexpr const VkFormat* GetFormatAlternatives(VkFormat format) {
87 return Alternatives::DEPTH24_UNORM_STENCIL8_UINT.data(); 92 return Alternatives::DEPTH24_UNORM_STENCIL8_UINT.data();
88 case VK_FORMAT_D16_UNORM_S8_UINT: 93 case VK_FORMAT_D16_UNORM_S8_UINT:
89 return Alternatives::DEPTH16_UNORM_STENCIL8_UINT.data(); 94 return Alternatives::DEPTH16_UNORM_STENCIL8_UINT.data();
95 case VK_FORMAT_B5G6R5_UNORM_PACK16:
96 return Alternatives::B5G6R5_UNORM_PACK16.data();
90 default: 97 default:
91 return nullptr; 98 return nullptr;
92 } 99 }
@@ -224,9 +231,14 @@ std::vector<std::string> GetSupportedExtensions(vk::PhysicalDevice physical) {
224 return supported_extensions; 231 return supported_extensions;
225} 232}
226 233
234bool IsExtensionSupported(std::span<const std::string> supported_extensions,
235 std::string_view extension) {
236 return std::ranges::find(supported_extensions, extension) != supported_extensions.end();
237}
238
227NvidiaArchitecture GetNvidiaArchitecture(vk::PhysicalDevice physical, 239NvidiaArchitecture GetNvidiaArchitecture(vk::PhysicalDevice physical,
228 std::span<const std::string> exts) { 240 std::span<const std::string> exts) {
229 if (std::ranges::find(exts, VK_KHR_FRAGMENT_SHADING_RATE_EXTENSION_NAME) != exts.end()) { 241 if (IsExtensionSupported(exts, VK_KHR_FRAGMENT_SHADING_RATE_EXTENSION_NAME)) {
230 VkPhysicalDeviceFragmentShadingRatePropertiesKHR shading_rate_props{}; 242 VkPhysicalDeviceFragmentShadingRatePropertiesKHR shading_rate_props{};
231 shading_rate_props.sType = 243 shading_rate_props.sType =
232 VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FRAGMENT_SHADING_RATE_PROPERTIES_KHR; 244 VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FRAGMENT_SHADING_RATE_PROPERTIES_KHR;
@@ -239,7 +251,7 @@ NvidiaArchitecture GetNvidiaArchitecture(vk::PhysicalDevice physical,
239 return NvidiaArchitecture::AmpereOrNewer; 251 return NvidiaArchitecture::AmpereOrNewer;
240 } 252 }
241 } 253 }
242 if (std::ranges::find(exts, VK_NV_SHADING_RATE_IMAGE_EXTENSION_NAME) != exts.end()) { 254 if (IsExtensionSupported(exts, VK_NV_SHADING_RATE_IMAGE_EXTENSION_NAME)) {
243 return NvidiaArchitecture::Turing; 255 return NvidiaArchitecture::Turing;
244 } 256 }
245 return NvidiaArchitecture::VoltaOrOlder; 257 return NvidiaArchitecture::VoltaOrOlder;
@@ -604,7 +616,8 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR
604 break; 616 break;
605 } 617 }
606 } 618 }
607 if (ext_extended_dynamic_state && driver_id == VK_DRIVER_ID_MESA_RADV) { 619 const bool is_radv = driver_id == VK_DRIVER_ID_MESA_RADV;
620 if (ext_extended_dynamic_state && is_radv) {
608 // Mask driver version variant 621 // Mask driver version variant
609 const u32 version = (properties.driverVersion << 3) >> 3; 622 const u32 version = (properties.driverVersion << 3) >> 3;
610 if (version < VK_MAKE_API_VERSION(0, 21, 2, 0)) { 623 if (version < VK_MAKE_API_VERSION(0, 21, 2, 0)) {
@@ -613,6 +626,17 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR
613 ext_extended_dynamic_state = false; 626 ext_extended_dynamic_state = false;
614 } 627 }
615 } 628 }
629 if (ext_vertex_input_dynamic_state && is_radv) {
630 // TODO(ameerj): Blacklist only offending driver versions
631 // TODO(ameerj): Confirm if RDNA1 is affected
632 const bool is_rdna2 =
633 IsExtensionSupported(supported_extensions, VK_KHR_FRAGMENT_SHADING_RATE_EXTENSION_NAME);
634 if (is_rdna2) {
635 LOG_WARNING(Render_Vulkan,
636 "RADV has broken VK_EXT_vertex_input_dynamic_state on RDNA2 hardware");
637 ext_vertex_input_dynamic_state = false;
638 }
639 }
616 sets_per_pool = 64; 640 sets_per_pool = 64;
617 641
618 const bool is_amd = 642 const bool is_amd =
@@ -628,7 +652,7 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR
628 has_broken_cube_compatibility = true; 652 has_broken_cube_compatibility = true;
629 } 653 }
630 } 654 }
631 const bool is_amd_or_radv = is_amd || driver_id == VK_DRIVER_ID_MESA_RADV; 655 const bool is_amd_or_radv = is_amd || is_radv;
632 if (ext_sampler_filter_minmax && is_amd_or_radv) { 656 if (ext_sampler_filter_minmax && is_amd_or_radv) {
633 // Disable ext_sampler_filter_minmax on AMD GCN4 and lower as it is broken. 657 // Disable ext_sampler_filter_minmax on AMD GCN4 and lower as it is broken.
634 if (!is_float16_supported) { 658 if (!is_float16_supported) {
@@ -639,6 +663,7 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR
639 } 663 }
640 664
641 const bool is_intel_windows = driver_id == VK_DRIVER_ID_INTEL_PROPRIETARY_WINDOWS; 665 const bool is_intel_windows = driver_id == VK_DRIVER_ID_INTEL_PROPRIETARY_WINDOWS;
666 const bool is_intel_anv = driver_id == VK_DRIVER_ID_INTEL_OPEN_SOURCE_MESA;
642 if (ext_vertex_input_dynamic_state && is_intel_windows) { 667 if (ext_vertex_input_dynamic_state && is_intel_windows) {
643 LOG_WARNING(Render_Vulkan, "Blacklisting Intel for VK_EXT_vertex_input_dynamic_state"); 668 LOG_WARNING(Render_Vulkan, "Blacklisting Intel for VK_EXT_vertex_input_dynamic_state");
644 ext_vertex_input_dynamic_state = false; 669 ext_vertex_input_dynamic_state = false;
@@ -652,6 +677,10 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR
652 LOG_WARNING(Render_Vulkan, "Intel proprietary drivers do not support MSAA image blits"); 677 LOG_WARNING(Render_Vulkan, "Intel proprietary drivers do not support MSAA image blits");
653 cant_blit_msaa = true; 678 cant_blit_msaa = true;
654 } 679 }
680 if (is_intel_anv) {
681 LOG_WARNING(Render_Vulkan, "ANV driver does not support native BGR format");
682 must_emulate_bgr565 = true;
683 }
655 684
656 supports_d24_depth = 685 supports_d24_depth =
657 IsFormatSupported(VK_FORMAT_D24_UNORM_S8_UINT, 686 IsFormatSupported(VK_FORMAT_D24_UNORM_S8_UINT,
diff --git a/src/video_core/vulkan_common/vulkan_device.h b/src/video_core/vulkan_common/vulkan_device.h
index 37d140ebd..34b1add16 100644
--- a/src/video_core/vulkan_common/vulkan_device.h
+++ b/src/video_core/vulkan_common/vulkan_device.h
@@ -354,6 +354,10 @@ public:
354 return cant_blit_msaa; 354 return cant_blit_msaa;
355 } 355 }
356 356
357 bool MustEmulateBGR565() const {
358 return must_emulate_bgr565;
359 }
360
357private: 361private:
358 /// Checks if the physical device is suitable. 362 /// Checks if the physical device is suitable.
359 void CheckSuitability(bool requires_swapchain) const; 363 void CheckSuitability(bool requires_swapchain) const;
@@ -448,6 +452,7 @@ private:
448 bool has_nsight_graphics{}; ///< Has Nsight Graphics attached 452 bool has_nsight_graphics{}; ///< Has Nsight Graphics attached
449 bool supports_d24_depth{}; ///< Supports D24 depth buffers. 453 bool supports_d24_depth{}; ///< Supports D24 depth buffers.
450 bool cant_blit_msaa{}; ///< Does not support MSAA<->MSAA blitting. 454 bool cant_blit_msaa{}; ///< Does not support MSAA<->MSAA blitting.
455 bool must_emulate_bgr565{}; ///< Emulates BGR565 by swizzling RGB565 format.
451 456
452 // Telemetry parameters 457 // Telemetry parameters
453 std::string vendor_name; ///< Device's driver name. 458 std::string vendor_name; ///< Device's driver name.
diff --git a/src/yuzu/applets/qt_controller.cpp b/src/yuzu/applets/qt_controller.cpp
index 4239c17f5..4104928d1 100644
--- a/src/yuzu/applets/qt_controller.cpp
+++ b/src/yuzu/applets/qt_controller.cpp
@@ -257,7 +257,7 @@ void QtControllerSelectorDialog::LoadConfiguration() {
257} 257}
258 258
259void QtControllerSelectorDialog::CallConfigureVibrationDialog() { 259void QtControllerSelectorDialog::CallConfigureVibrationDialog() {
260 ConfigureVibration dialog(this); 260 ConfigureVibration dialog(this, system.HIDCore());
261 261
262 dialog.setWindowFlags(Qt::Dialog | Qt::CustomizeWindowHint | Qt::WindowTitleHint | 262 dialog.setWindowFlags(Qt::Dialog | Qt::CustomizeWindowHint | Qt::WindowTitleHint |
263 Qt::WindowSystemMenuHint); 263 Qt::WindowSystemMenuHint);
diff --git a/src/yuzu/applets/qt_profile_select.cpp b/src/yuzu/applets/qt_profile_select.cpp
index 5b32da923..4cd8f7784 100644
--- a/src/yuzu/applets/qt_profile_select.cpp
+++ b/src/yuzu/applets/qt_profile_select.cpp
@@ -23,13 +23,13 @@ QString FormatUserEntryText(const QString& username, Common::UUID uuid) {
23 return QtProfileSelectionDialog::tr( 23 return QtProfileSelectionDialog::tr(
24 "%1\n%2", "%1 is the profile username, %2 is the formatted UUID (e.g. " 24 "%1\n%2", "%1 is the profile username, %2 is the formatted UUID (e.g. "
25 "00112233-4455-6677-8899-AABBCCDDEEFF))") 25 "00112233-4455-6677-8899-AABBCCDDEEFF))")
26 .arg(username, QString::fromStdString(uuid.FormatSwitch())); 26 .arg(username, QString::fromStdString(uuid.FormattedString()));
27} 27}
28 28
29QString GetImagePath(Common::UUID uuid) { 29QString GetImagePath(Common::UUID uuid) {
30 const auto path = 30 const auto path =
31 Common::FS::GetYuzuPath(Common::FS::YuzuPath::NANDDir) / 31 Common::FS::GetYuzuPath(Common::FS::YuzuPath::NANDDir) /
32 fmt::format("system/save/8000000000000010/su/avators/{}.jpg", uuid.FormatSwitch()); 32 fmt::format("system/save/8000000000000010/su/avators/{}.jpg", uuid.FormattedString());
33 return QString::fromStdString(Common::FS::PathToUTF8String(path)); 33 return QString::fromStdString(Common::FS::PathToUTF8String(path));
34} 34}
35 35
diff --git a/src/yuzu/configuration/config.cpp b/src/yuzu/configuration/config.cpp
index 2c8c10c50..4b943c6ba 100644
--- a/src/yuzu/configuration/config.cpp
+++ b/src/yuzu/configuration/config.cpp
@@ -65,23 +65,25 @@ const std::array<int, 2> Config::default_stick_mod = {
65// This must be in alphabetical order according to action name as it must have the same order as 65// This must be in alphabetical order according to action name as it must have the same order as
66// UISetting::values.shortcuts, which is alphabetically ordered. 66// UISetting::values.shortcuts, which is alphabetically ordered.
67// clang-format off 67// clang-format off
68const std::array<UISettings::Shortcut, 20> Config::default_hotkeys{{ 68const std::array<UISettings::Shortcut, 22> Config::default_hotkeys{{
69 {QStringLiteral("Audio Mute/Unmute"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+M"), QStringLiteral("Home+Dpad_Right"), Qt::WindowShortcut}}, 69 {QStringLiteral("Audio Mute/Unmute"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+M"), QStringLiteral("Home+Dpad_Right"), Qt::WindowShortcut}},
70 {QStringLiteral("Audio Volume Down"), QStringLiteral("Main Window"), {QStringLiteral("-"), QStringLiteral("Home+Dpad_Down"), Qt::ApplicationShortcut}}, 70 {QStringLiteral("Audio Volume Down"), QStringLiteral("Main Window"), {QStringLiteral("-"), QStringLiteral("Home+Dpad_Down"), Qt::ApplicationShortcut}},
71 {QStringLiteral("Audio Volume Up"), QStringLiteral("Main Window"), {QStringLiteral("+"), QStringLiteral("Home+Dpad_Up"), Qt::ApplicationShortcut}}, 71 {QStringLiteral("Audio Volume Up"), QStringLiteral("Main Window"), {QStringLiteral("+"), QStringLiteral("Home+Dpad_Up"), Qt::ApplicationShortcut}},
72 {QStringLiteral("Capture Screenshot"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+P"), QStringLiteral("Screenshot"), Qt::WidgetWithChildrenShortcut}}, 72 {QStringLiteral("Capture Screenshot"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+P"), QStringLiteral("Screenshot"), Qt::WidgetWithChildrenShortcut}},
73 {QStringLiteral("Change Adapting Filter"), QStringLiteral("Main Window"), {QStringLiteral("F8"), QStringLiteral("Home+L"), Qt::ApplicationShortcut}},
73 {QStringLiteral("Change Docked Mode"), QStringLiteral("Main Window"), {QStringLiteral("F10"), QStringLiteral("Home+X"), Qt::ApplicationShortcut}}, 74 {QStringLiteral("Change Docked Mode"), QStringLiteral("Main Window"), {QStringLiteral("F10"), QStringLiteral("Home+X"), Qt::ApplicationShortcut}},
75 {QStringLiteral("Change GPU Accuracy"), QStringLiteral("Main Window"), {QStringLiteral("F9"), QStringLiteral("Home+R"), Qt::ApplicationShortcut}},
74 {QStringLiteral("Continue/Pause Emulation"), QStringLiteral("Main Window"), {QStringLiteral("F4"), QStringLiteral("Home+Plus"), Qt::WindowShortcut}}, 76 {QStringLiteral("Continue/Pause Emulation"), QStringLiteral("Main Window"), {QStringLiteral("F4"), QStringLiteral("Home+Plus"), Qt::WindowShortcut}},
75 {QStringLiteral("Exit Fullscreen"), QStringLiteral("Main Window"), {QStringLiteral("Esc"), QStringLiteral(""), Qt::WindowShortcut}}, 77 {QStringLiteral("Exit Fullscreen"), QStringLiteral("Main Window"), {QStringLiteral("Esc"), QStringLiteral(""), Qt::WindowShortcut}},
76 {QStringLiteral("Exit yuzu"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+Q"), QStringLiteral("Home+Minus"), Qt::WindowShortcut}}, 78 {QStringLiteral("Exit yuzu"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+Q"), QStringLiteral("Home+Minus"), Qt::WindowShortcut}},
77 {QStringLiteral("Fullscreen"), QStringLiteral("Main Window"), {QStringLiteral("F11"), QStringLiteral("Home+B"), Qt::WindowShortcut}}, 79 {QStringLiteral("Fullscreen"), QStringLiteral("Main Window"), {QStringLiteral("F11"), QStringLiteral("Home+B"), Qt::WindowShortcut}},
78 {QStringLiteral("Load Amiibo"), QStringLiteral("Main Window"), {QStringLiteral("F2"), QStringLiteral("Home+A"), Qt::WidgetWithChildrenShortcut}},
79 {QStringLiteral("Load File"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+O"), QStringLiteral(""), Qt::WidgetWithChildrenShortcut}}, 80 {QStringLiteral("Load File"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+O"), QStringLiteral(""), Qt::WidgetWithChildrenShortcut}},
81 {QStringLiteral("Load/Remove Amiibo"), QStringLiteral("Main Window"), {QStringLiteral("F2"), QStringLiteral("Home+A"), Qt::WidgetWithChildrenShortcut}},
80 {QStringLiteral("Restart Emulation"), QStringLiteral("Main Window"), {QStringLiteral("F6"), QStringLiteral(""), Qt::WindowShortcut}}, 82 {QStringLiteral("Restart Emulation"), QStringLiteral("Main Window"), {QStringLiteral("F6"), QStringLiteral(""), Qt::WindowShortcut}},
81 {QStringLiteral("Stop Emulation"), QStringLiteral("Main Window"), {QStringLiteral("F5"), QStringLiteral(""), Qt::WindowShortcut}}, 83 {QStringLiteral("Stop Emulation"), QStringLiteral("Main Window"), {QStringLiteral("F5"), QStringLiteral(""), Qt::WindowShortcut}},
82 {QStringLiteral("TAS Start/Stop"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+F5"), QStringLiteral(""), Qt::ApplicationShortcut}},
83 {QStringLiteral("TAS Reset"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+F6"), QStringLiteral(""), Qt::ApplicationShortcut}},
84 {QStringLiteral("TAS Record"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+F7"), QStringLiteral(""), Qt::ApplicationShortcut}}, 84 {QStringLiteral("TAS Record"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+F7"), QStringLiteral(""), Qt::ApplicationShortcut}},
85 {QStringLiteral("TAS Reset"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+F6"), QStringLiteral(""), Qt::ApplicationShortcut}},
86 {QStringLiteral("TAS Start/Stop"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+F5"), QStringLiteral(""), Qt::ApplicationShortcut}},
85 {QStringLiteral("Toggle Filter Bar"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+F"), QStringLiteral(""), Qt::WindowShortcut}}, 87 {QStringLiteral("Toggle Filter Bar"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+F"), QStringLiteral(""), Qt::WindowShortcut}},
86 {QStringLiteral("Toggle Framerate Limit"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+U"), QStringLiteral("Home+Y"), Qt::ApplicationShortcut}}, 88 {QStringLiteral("Toggle Framerate Limit"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+U"), QStringLiteral("Home+Y"), Qt::ApplicationShortcut}},
87 {QStringLiteral("Toggle Mouse Panning"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+F9"), QStringLiteral(""), Qt::ApplicationShortcut}}, 89 {QStringLiteral("Toggle Mouse Panning"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+F9"), QStringLiteral(""), Qt::ApplicationShortcut}},
@@ -443,6 +445,7 @@ void Config::ReadCoreValues() {
443 qt_config->beginGroup(QStringLiteral("Core")); 445 qt_config->beginGroup(QStringLiteral("Core"));
444 446
445 ReadGlobalSetting(Settings::values.use_multi_core); 447 ReadGlobalSetting(Settings::values.use_multi_core);
448 ReadGlobalSetting(Settings::values.use_extended_memory_layout);
446 449
447 qt_config->endGroup(); 450 qt_config->endGroup();
448} 451}
@@ -606,6 +609,7 @@ void Config::ReadCpuValues() {
606 ReadGlobalSetting(Settings::values.cpuopt_unsafe_ignore_standard_fpcr); 609 ReadGlobalSetting(Settings::values.cpuopt_unsafe_ignore_standard_fpcr);
607 ReadGlobalSetting(Settings::values.cpuopt_unsafe_inaccurate_nan); 610 ReadGlobalSetting(Settings::values.cpuopt_unsafe_inaccurate_nan);
608 ReadGlobalSetting(Settings::values.cpuopt_unsafe_fastmem_check); 611 ReadGlobalSetting(Settings::values.cpuopt_unsafe_fastmem_check);
612 ReadGlobalSetting(Settings::values.cpuopt_unsafe_ignore_global_monitor);
609 613
610 if (global) { 614 if (global) {
611 ReadBasicSetting(Settings::values.cpu_debug_mode); 615 ReadBasicSetting(Settings::values.cpu_debug_mode);
@@ -618,6 +622,8 @@ void Config::ReadCpuValues() {
618 ReadBasicSetting(Settings::values.cpuopt_misc_ir); 622 ReadBasicSetting(Settings::values.cpuopt_misc_ir);
619 ReadBasicSetting(Settings::values.cpuopt_reduce_misalign_checks); 623 ReadBasicSetting(Settings::values.cpuopt_reduce_misalign_checks);
620 ReadBasicSetting(Settings::values.cpuopt_fastmem); 624 ReadBasicSetting(Settings::values.cpuopt_fastmem);
625 ReadBasicSetting(Settings::values.cpuopt_fastmem_exclusives);
626 ReadBasicSetting(Settings::values.cpuopt_recompile_exclusives);
621 } 627 }
622 628
623 qt_config->endGroup(); 629 qt_config->endGroup();
@@ -767,6 +773,7 @@ void Config::ReadUIValues() {
767 ReadBasicSetting(UISettings::values.callout_flags); 773 ReadBasicSetting(UISettings::values.callout_flags);
768 ReadBasicSetting(UISettings::values.show_console); 774 ReadBasicSetting(UISettings::values.show_console);
769 ReadBasicSetting(UISettings::values.pause_when_in_background); 775 ReadBasicSetting(UISettings::values.pause_when_in_background);
776 ReadBasicSetting(UISettings::values.mute_when_in_background);
770 ReadBasicSetting(UISettings::values.hide_mouse); 777 ReadBasicSetting(UISettings::values.hide_mouse);
771 778
772 qt_config->endGroup(); 779 qt_config->endGroup();
@@ -1016,6 +1023,7 @@ void Config::SaveCoreValues() {
1016 qt_config->beginGroup(QStringLiteral("Core")); 1023 qt_config->beginGroup(QStringLiteral("Core"));
1017 1024
1018 WriteGlobalSetting(Settings::values.use_multi_core); 1025 WriteGlobalSetting(Settings::values.use_multi_core);
1026 WriteGlobalSetting(Settings::values.use_extended_memory_layout);
1019 1027
1020 qt_config->endGroup(); 1028 qt_config->endGroup();
1021} 1029}
@@ -1134,6 +1142,7 @@ void Config::SaveCpuValues() {
1134 WriteGlobalSetting(Settings::values.cpuopt_unsafe_ignore_standard_fpcr); 1142 WriteGlobalSetting(Settings::values.cpuopt_unsafe_ignore_standard_fpcr);
1135 WriteGlobalSetting(Settings::values.cpuopt_unsafe_inaccurate_nan); 1143 WriteGlobalSetting(Settings::values.cpuopt_unsafe_inaccurate_nan);
1136 WriteGlobalSetting(Settings::values.cpuopt_unsafe_fastmem_check); 1144 WriteGlobalSetting(Settings::values.cpuopt_unsafe_fastmem_check);
1145 WriteGlobalSetting(Settings::values.cpuopt_unsafe_ignore_global_monitor);
1137 1146
1138 if (global) { 1147 if (global) {
1139 WriteBasicSetting(Settings::values.cpu_debug_mode); 1148 WriteBasicSetting(Settings::values.cpu_debug_mode);
@@ -1146,6 +1155,8 @@ void Config::SaveCpuValues() {
1146 WriteBasicSetting(Settings::values.cpuopt_misc_ir); 1155 WriteBasicSetting(Settings::values.cpuopt_misc_ir);
1147 WriteBasicSetting(Settings::values.cpuopt_reduce_misalign_checks); 1156 WriteBasicSetting(Settings::values.cpuopt_reduce_misalign_checks);
1148 WriteBasicSetting(Settings::values.cpuopt_fastmem); 1157 WriteBasicSetting(Settings::values.cpuopt_fastmem);
1158 WriteBasicSetting(Settings::values.cpuopt_fastmem_exclusives);
1159 WriteBasicSetting(Settings::values.cpuopt_recompile_exclusives);
1149 } 1160 }
1150 1161
1151 qt_config->endGroup(); 1162 qt_config->endGroup();
@@ -1295,6 +1306,7 @@ void Config::SaveUIValues() {
1295 WriteBasicSetting(UISettings::values.callout_flags); 1306 WriteBasicSetting(UISettings::values.callout_flags);
1296 WriteBasicSetting(UISettings::values.show_console); 1307 WriteBasicSetting(UISettings::values.show_console);
1297 WriteBasicSetting(UISettings::values.pause_when_in_background); 1308 WriteBasicSetting(UISettings::values.pause_when_in_background);
1309 WriteBasicSetting(UISettings::values.mute_when_in_background);
1298 WriteBasicSetting(UISettings::values.hide_mouse); 1310 WriteBasicSetting(UISettings::values.hide_mouse);
1299 1311
1300 qt_config->endGroup(); 1312 qt_config->endGroup();
diff --git a/src/yuzu/configuration/config.h b/src/yuzu/configuration/config.h
index 60b20a62f..ae3e36a11 100644
--- a/src/yuzu/configuration/config.h
+++ b/src/yuzu/configuration/config.h
@@ -46,7 +46,7 @@ public:
46 default_mouse_buttons; 46 default_mouse_buttons;
47 static const std::array<int, Settings::NativeKeyboard::NumKeyboardKeys> default_keyboard_keys; 47 static const std::array<int, Settings::NativeKeyboard::NumKeyboardKeys> default_keyboard_keys;
48 static const std::array<int, Settings::NativeKeyboard::NumKeyboardMods> default_keyboard_mods; 48 static const std::array<int, Settings::NativeKeyboard::NumKeyboardMods> default_keyboard_mods;
49 static const std::array<UISettings::Shortcut, 20> default_hotkeys; 49 static const std::array<UISettings::Shortcut, 22> default_hotkeys;
50 50
51 static constexpr UISettings::Theme default_theme{ 51 static constexpr UISettings::Theme default_theme{
52#ifdef _WIN32 52#ifdef _WIN32
diff --git a/src/yuzu/configuration/configure_cpu.cpp b/src/yuzu/configuration/configure_cpu.cpp
index f66cab5d4..bf74ccc7c 100644
--- a/src/yuzu/configuration/configure_cpu.cpp
+++ b/src/yuzu/configuration/configure_cpu.cpp
@@ -36,6 +36,7 @@ void ConfigureCpu::SetConfiguration() {
36 ui->cpuopt_unsafe_ignore_standard_fpcr->setEnabled(runtime_lock); 36 ui->cpuopt_unsafe_ignore_standard_fpcr->setEnabled(runtime_lock);
37 ui->cpuopt_unsafe_inaccurate_nan->setEnabled(runtime_lock); 37 ui->cpuopt_unsafe_inaccurate_nan->setEnabled(runtime_lock);
38 ui->cpuopt_unsafe_fastmem_check->setEnabled(runtime_lock); 38 ui->cpuopt_unsafe_fastmem_check->setEnabled(runtime_lock);
39 ui->cpuopt_unsafe_ignore_global_monitor->setEnabled(runtime_lock);
39 40
40 ui->cpuopt_unsafe_unfuse_fma->setChecked(Settings::values.cpuopt_unsafe_unfuse_fma.GetValue()); 41 ui->cpuopt_unsafe_unfuse_fma->setChecked(Settings::values.cpuopt_unsafe_unfuse_fma.GetValue());
41 ui->cpuopt_unsafe_reduce_fp_error->setChecked( 42 ui->cpuopt_unsafe_reduce_fp_error->setChecked(
@@ -46,6 +47,8 @@ void ConfigureCpu::SetConfiguration() {
46 Settings::values.cpuopt_unsafe_inaccurate_nan.GetValue()); 47 Settings::values.cpuopt_unsafe_inaccurate_nan.GetValue());
47 ui->cpuopt_unsafe_fastmem_check->setChecked( 48 ui->cpuopt_unsafe_fastmem_check->setChecked(
48 Settings::values.cpuopt_unsafe_fastmem_check.GetValue()); 49 Settings::values.cpuopt_unsafe_fastmem_check.GetValue());
50 ui->cpuopt_unsafe_ignore_global_monitor->setChecked(
51 Settings::values.cpuopt_unsafe_ignore_global_monitor.GetValue());
49 52
50 if (Settings::IsConfiguringGlobal()) { 53 if (Settings::IsConfiguringGlobal()) {
51 ui->accuracy->setCurrentIndex(static_cast<int>(Settings::values.cpu_accuracy.GetValue())); 54 ui->accuracy->setCurrentIndex(static_cast<int>(Settings::values.cpu_accuracy.GetValue()));
@@ -82,6 +85,9 @@ void ConfigureCpu::ApplyConfiguration() {
82 ConfigurationShared::ApplyPerGameSetting(&Settings::values.cpuopt_unsafe_fastmem_check, 85 ConfigurationShared::ApplyPerGameSetting(&Settings::values.cpuopt_unsafe_fastmem_check,
83 ui->cpuopt_unsafe_fastmem_check, 86 ui->cpuopt_unsafe_fastmem_check,
84 cpuopt_unsafe_fastmem_check); 87 cpuopt_unsafe_fastmem_check);
88 ConfigurationShared::ApplyPerGameSetting(&Settings::values.cpuopt_unsafe_ignore_global_monitor,
89 ui->cpuopt_unsafe_ignore_global_monitor,
90 cpuopt_unsafe_ignore_global_monitor);
85} 91}
86 92
87void ConfigureCpu::changeEvent(QEvent* event) { 93void ConfigureCpu::changeEvent(QEvent* event) {
@@ -120,4 +126,7 @@ void ConfigureCpu::SetupPerGameUI() {
120 ConfigurationShared::SetColoredTristate(ui->cpuopt_unsafe_fastmem_check, 126 ConfigurationShared::SetColoredTristate(ui->cpuopt_unsafe_fastmem_check,
121 Settings::values.cpuopt_unsafe_fastmem_check, 127 Settings::values.cpuopt_unsafe_fastmem_check,
122 cpuopt_unsafe_fastmem_check); 128 cpuopt_unsafe_fastmem_check);
129 ConfigurationShared::SetColoredTristate(ui->cpuopt_unsafe_ignore_global_monitor,
130 Settings::values.cpuopt_unsafe_ignore_global_monitor,
131 cpuopt_unsafe_ignore_global_monitor);
123} 132}
diff --git a/src/yuzu/configuration/configure_cpu.h b/src/yuzu/configuration/configure_cpu.h
index ed9af0e9f..733e38be4 100644
--- a/src/yuzu/configuration/configure_cpu.h
+++ b/src/yuzu/configuration/configure_cpu.h
@@ -45,6 +45,7 @@ private:
45 ConfigurationShared::CheckState cpuopt_unsafe_ignore_standard_fpcr; 45 ConfigurationShared::CheckState cpuopt_unsafe_ignore_standard_fpcr;
46 ConfigurationShared::CheckState cpuopt_unsafe_inaccurate_nan; 46 ConfigurationShared::CheckState cpuopt_unsafe_inaccurate_nan;
47 ConfigurationShared::CheckState cpuopt_unsafe_fastmem_check; 47 ConfigurationShared::CheckState cpuopt_unsafe_fastmem_check;
48 ConfigurationShared::CheckState cpuopt_unsafe_ignore_global_monitor;
48 49
49 const Core::System& system; 50 const Core::System& system;
50}; 51};
diff --git a/src/yuzu/configuration/configure_cpu.ui b/src/yuzu/configuration/configure_cpu.ui
index d8064db24..5d80a8c91 100644
--- a/src/yuzu/configuration/configure_cpu.ui
+++ b/src/yuzu/configuration/configure_cpu.ui
@@ -150,6 +150,18 @@
150 </property> 150 </property>
151 </widget> 151 </widget>
152 </item> 152 </item>
153 <item>
154 <widget class="QCheckBox" name="cpuopt_unsafe_ignore_global_monitor">
155 <property name="toolTip">
156 <string>
157 &lt;div&gt;This option improves speed by relying only on the semantics of cmpxchg to ensure safety of exclusive access instructions. Please note this may result in deadlocks and other race conditions.&lt;/div&gt;
158 </string>
159 </property>
160 <property name="text">
161 <string>Ignore global monitor</string>
162 </property>
163 </widget>
164 </item>
153 </layout> 165 </layout>
154 </widget> 166 </widget>
155 </item> 167 </item>
diff --git a/src/yuzu/configuration/configure_cpu_debug.cpp b/src/yuzu/configuration/configure_cpu_debug.cpp
index 05a90963d..616a0be75 100644
--- a/src/yuzu/configuration/configure_cpu_debug.cpp
+++ b/src/yuzu/configuration/configure_cpu_debug.cpp
@@ -44,6 +44,12 @@ void ConfigureCpuDebug::SetConfiguration() {
44 Settings::values.cpuopt_reduce_misalign_checks.GetValue()); 44 Settings::values.cpuopt_reduce_misalign_checks.GetValue());
45 ui->cpuopt_fastmem->setEnabled(runtime_lock); 45 ui->cpuopt_fastmem->setEnabled(runtime_lock);
46 ui->cpuopt_fastmem->setChecked(Settings::values.cpuopt_fastmem.GetValue()); 46 ui->cpuopt_fastmem->setChecked(Settings::values.cpuopt_fastmem.GetValue());
47 ui->cpuopt_fastmem_exclusives->setEnabled(runtime_lock);
48 ui->cpuopt_fastmem_exclusives->setChecked(
49 Settings::values.cpuopt_fastmem_exclusives.GetValue());
50 ui->cpuopt_recompile_exclusives->setEnabled(runtime_lock);
51 ui->cpuopt_recompile_exclusives->setChecked(
52 Settings::values.cpuopt_recompile_exclusives.GetValue());
47} 53}
48 54
49void ConfigureCpuDebug::ApplyConfiguration() { 55void ConfigureCpuDebug::ApplyConfiguration() {
@@ -56,6 +62,8 @@ void ConfigureCpuDebug::ApplyConfiguration() {
56 Settings::values.cpuopt_misc_ir = ui->cpuopt_misc_ir->isChecked(); 62 Settings::values.cpuopt_misc_ir = ui->cpuopt_misc_ir->isChecked();
57 Settings::values.cpuopt_reduce_misalign_checks = ui->cpuopt_reduce_misalign_checks->isChecked(); 63 Settings::values.cpuopt_reduce_misalign_checks = ui->cpuopt_reduce_misalign_checks->isChecked();
58 Settings::values.cpuopt_fastmem = ui->cpuopt_fastmem->isChecked(); 64 Settings::values.cpuopt_fastmem = ui->cpuopt_fastmem->isChecked();
65 Settings::values.cpuopt_fastmem_exclusives = ui->cpuopt_fastmem_exclusives->isChecked();
66 Settings::values.cpuopt_recompile_exclusives = ui->cpuopt_recompile_exclusives->isChecked();
59} 67}
60 68
61void ConfigureCpuDebug::changeEvent(QEvent* event) { 69void ConfigureCpuDebug::changeEvent(QEvent* event) {
diff --git a/src/yuzu/configuration/configure_cpu_debug.ui b/src/yuzu/configuration/configure_cpu_debug.ui
index 6e635bb2f..2bc268810 100644
--- a/src/yuzu/configuration/configure_cpu_debug.ui
+++ b/src/yuzu/configuration/configure_cpu_debug.ui
@@ -144,7 +144,34 @@
144 </string> 144 </string>
145 </property> 145 </property>
146 <property name="text"> 146 <property name="text">
147 <string>Enable Host MMU Emulation</string> 147 <string>Enable Host MMU Emulation (general memory instructions)</string>
148 </property>
149 </widget>
150 </item>
151 <item>
152 <widget class="QCheckBox" name="cpuopt_fastmem_exclusives">
153 <property name="toolTip">
154 <string>
155 &lt;div style=&quot;white-space: nowrap&quot;&gt;This optimization speeds up exclusive memory accesses by the guest program.&lt;/div&gt;
156 &lt;div style=&quot;white-space: nowrap&quot;&gt;Enabling it causes guest exclusive memory reads/writes to be done directly into memory and make use of Host's MMU.&lt;/div&gt;
157 &lt;div style=&quot;white-space: nowrap&quot;&gt;Disabling this forces all exclusive memory accesses to use Software MMU Emulation.&lt;/div&gt;
158 </string>
159 </property>
160 <property name="text">
161 <string>Enable Host MMU Emulation (exclusive memory instructions)</string>
162 </property>
163 </widget>
164 </item>
165 <item>
166 <widget class="QCheckBox" name="cpuopt_recompile_exclusives">
167 <property name="toolTip">
168 <string>
169 &lt;div style=&quot;white-space: nowrap&quot;&gt;This optimization speeds up exclusive memory accesses by the guest program.&lt;/div&gt;
170 &lt;div style=&quot;white-space: nowrap&quot;&gt;Enabling it reduces the overhead of fastmem failure of exclusive memory accesses.&lt;/div&gt;
171 </string>
172 </property>
173 <property name="text">
174 <string>Enable recompilation of exclusive memory instructions</string>
148 </property> 175 </property>
149 </widget> 176 </widget>
150 </item> 177 </item>
diff --git a/src/yuzu/configuration/configure_general.cpp b/src/yuzu/configuration/configure_general.cpp
index 566879317..08d5444ec 100644
--- a/src/yuzu/configuration/configure_general.cpp
+++ b/src/yuzu/configuration/configure_general.cpp
@@ -42,10 +42,14 @@ void ConfigureGeneral::SetConfiguration() {
42 42
43 ui->use_multi_core->setEnabled(runtime_lock); 43 ui->use_multi_core->setEnabled(runtime_lock);
44 ui->use_multi_core->setChecked(Settings::values.use_multi_core.GetValue()); 44 ui->use_multi_core->setChecked(Settings::values.use_multi_core.GetValue());
45 ui->use_extended_memory_layout->setEnabled(runtime_lock);
46 ui->use_extended_memory_layout->setChecked(
47 Settings::values.use_extended_memory_layout.GetValue());
45 48
46 ui->toggle_check_exit->setChecked(UISettings::values.confirm_before_closing.GetValue()); 49 ui->toggle_check_exit->setChecked(UISettings::values.confirm_before_closing.GetValue());
47 ui->toggle_user_on_boot->setChecked(UISettings::values.select_user_on_boot.GetValue()); 50 ui->toggle_user_on_boot->setChecked(UISettings::values.select_user_on_boot.GetValue());
48 ui->toggle_background_pause->setChecked(UISettings::values.pause_when_in_background.GetValue()); 51 ui->toggle_background_pause->setChecked(UISettings::values.pause_when_in_background.GetValue());
52 ui->toggle_background_mute->setChecked(UISettings::values.mute_when_in_background.GetValue());
49 ui->toggle_hide_mouse->setChecked(UISettings::values.hide_mouse.GetValue()); 53 ui->toggle_hide_mouse->setChecked(UISettings::values.hide_mouse.GetValue());
50 54
51 ui->toggle_speed_limit->setChecked(Settings::values.use_speed_limit.GetValue()); 55 ui->toggle_speed_limit->setChecked(Settings::values.use_speed_limit.GetValue());
@@ -90,11 +94,15 @@ void ConfigureGeneral::ResetDefaults() {
90void ConfigureGeneral::ApplyConfiguration() { 94void ConfigureGeneral::ApplyConfiguration() {
91 ConfigurationShared::ApplyPerGameSetting(&Settings::values.use_multi_core, ui->use_multi_core, 95 ConfigurationShared::ApplyPerGameSetting(&Settings::values.use_multi_core, ui->use_multi_core,
92 use_multi_core); 96 use_multi_core);
97 ConfigurationShared::ApplyPerGameSetting(&Settings::values.use_extended_memory_layout,
98 ui->use_extended_memory_layout,
99 use_extended_memory_layout);
93 100
94 if (Settings::IsConfiguringGlobal()) { 101 if (Settings::IsConfiguringGlobal()) {
95 UISettings::values.confirm_before_closing = ui->toggle_check_exit->isChecked(); 102 UISettings::values.confirm_before_closing = ui->toggle_check_exit->isChecked();
96 UISettings::values.select_user_on_boot = ui->toggle_user_on_boot->isChecked(); 103 UISettings::values.select_user_on_boot = ui->toggle_user_on_boot->isChecked();
97 UISettings::values.pause_when_in_background = ui->toggle_background_pause->isChecked(); 104 UISettings::values.pause_when_in_background = ui->toggle_background_pause->isChecked();
105 UISettings::values.mute_when_in_background = ui->toggle_background_mute->isChecked();
98 UISettings::values.hide_mouse = ui->toggle_hide_mouse->isChecked(); 106 UISettings::values.hide_mouse = ui->toggle_hide_mouse->isChecked();
99 107
100 Settings::values.fps_cap.SetValue(ui->fps_cap->value()); 108 Settings::values.fps_cap.SetValue(ui->fps_cap->value());
@@ -158,6 +166,9 @@ void ConfigureGeneral::SetupPerGameUI() {
158 Settings::values.use_speed_limit, use_speed_limit); 166 Settings::values.use_speed_limit, use_speed_limit);
159 ConfigurationShared::SetColoredTristate(ui->use_multi_core, Settings::values.use_multi_core, 167 ConfigurationShared::SetColoredTristate(ui->use_multi_core, Settings::values.use_multi_core,
160 use_multi_core); 168 use_multi_core);
169 ConfigurationShared::SetColoredTristate(ui->use_extended_memory_layout,
170 Settings::values.use_extended_memory_layout,
171 use_extended_memory_layout);
161 172
162 connect(ui->toggle_speed_limit, &QCheckBox::clicked, ui->speed_limit, [this]() { 173 connect(ui->toggle_speed_limit, &QCheckBox::clicked, ui->speed_limit, [this]() {
163 ui->speed_limit->setEnabled(ui->toggle_speed_limit->isChecked() && 174 ui->speed_limit->setEnabled(ui->toggle_speed_limit->isChecked() &&
diff --git a/src/yuzu/configuration/configure_general.h b/src/yuzu/configuration/configure_general.h
index 85c1dd4a8..b6f3bb5ed 100644
--- a/src/yuzu/configuration/configure_general.h
+++ b/src/yuzu/configuration/configure_general.h
@@ -48,6 +48,7 @@ private:
48 48
49 ConfigurationShared::CheckState use_speed_limit; 49 ConfigurationShared::CheckState use_speed_limit;
50 ConfigurationShared::CheckState use_multi_core; 50 ConfigurationShared::CheckState use_multi_core;
51 ConfigurationShared::CheckState use_extended_memory_layout;
51 52
52 const Core::System& system; 53 const Core::System& system;
53}; 54};
diff --git a/src/yuzu/configuration/configure_general.ui b/src/yuzu/configuration/configure_general.ui
index 112dc72b3..c6ef2ab70 100644
--- a/src/yuzu/configuration/configure_general.ui
+++ b/src/yuzu/configuration/configure_general.ui
@@ -143,6 +143,13 @@
143 </widget> 143 </widget>
144 </item> 144 </item>
145 <item> 145 <item>
146 <widget class="QCheckBox" name="use_extended_memory_layout">
147 <property name="text">
148 <string>Extended memory layout (6GB DRAM)</string>
149 </property>
150 </widget>
151 </item>
152 <item>
146 <widget class="QCheckBox" name="toggle_check_exit"> 153 <widget class="QCheckBox" name="toggle_check_exit">
147 <property name="text"> 154 <property name="text">
148 <string>Confirm exit while emulation is running</string> 155 <string>Confirm exit while emulation is running</string>
@@ -164,6 +171,13 @@
164 </widget> 171 </widget>
165 </item> 172 </item>
166 <item> 173 <item>
174 <widget class="QCheckBox" name="toggle_background_mute">
175 <property name="text">
176 <string>Mute audio when in background</string>
177 </property>
178 </widget>
179 </item>
180 <item>
167 <widget class="QCheckBox" name="toggle_hide_mouse"> 181 <widget class="QCheckBox" name="toggle_hide_mouse">
168 <property name="text"> 182 <property name="text">
169 <string>Hide mouse on inactivity</string> 183 <string>Hide mouse on inactivity</string>
diff --git a/src/yuzu/configuration/configure_input.cpp b/src/yuzu/configuration/configure_input.cpp
index d53179dbb..7c5776189 100644
--- a/src/yuzu/configuration/configure_input.cpp
+++ b/src/yuzu/configuration/configure_input.cpp
@@ -164,7 +164,7 @@ void ConfigureInput::Initialize(InputCommon::InputSubsystem* input_subsystem,
164 }); 164 });
165 165
166 connect(ui->vibrationButton, &QPushButton::clicked, 166 connect(ui->vibrationButton, &QPushButton::clicked,
167 [this] { CallConfigureDialog<ConfigureVibration>(*this); }); 167 [this, &hid_core] { CallConfigureDialog<ConfigureVibration>(*this, hid_core); });
168 168
169 connect(ui->motionButton, &QPushButton::clicked, [this, input_subsystem] { 169 connect(ui->motionButton, &QPushButton::clicked, [this, input_subsystem] {
170 CallConfigureDialog<ConfigureMotionTouch>(*this, input_subsystem); 170 CallConfigureDialog<ConfigureMotionTouch>(*this, input_subsystem);
diff --git a/src/yuzu/configuration/configure_input_player.cpp b/src/yuzu/configuration/configure_input_player.cpp
index cc0534907..0aa4ac3e4 100644
--- a/src/yuzu/configuration/configure_input_player.cpp
+++ b/src/yuzu/configuration/configure_input_player.cpp
@@ -488,6 +488,32 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_i
488 emulated_controller->SetStickParam(analog_id, {}); 488 emulated_controller->SetStickParam(analog_id, {});
489 analog_map_buttons[analog_id][sub_button_id]->setText(tr("[not set]")); 489 analog_map_buttons[analog_id][sub_button_id]->setText(tr("[not set]"));
490 }); 490 });
491 context_menu.addAction(tr("Center axis"), [&] {
492 const auto stick_value =
493 emulated_controller->GetSticksValues()[analog_id];
494 const float offset_x = stick_value.x.properties.offset;
495 const float offset_y = stick_value.y.properties.offset;
496 float raw_value_x = stick_value.x.raw_value;
497 float raw_value_y = stick_value.y.raw_value;
498 // See Core::HID::SanitizeStick() to obtain the original raw axis value
499 if (std::abs(offset_x) < 0.5f) {
500 if (raw_value_x > 0) {
501 raw_value_x *= 1 + offset_x;
502 } else {
503 raw_value_x *= 1 - offset_x;
504 }
505 }
506 if (std::abs(offset_x) < 0.5f) {
507 if (raw_value_y > 0) {
508 raw_value_y *= 1 + offset_y;
509 } else {
510 raw_value_y *= 1 - offset_y;
511 }
512 }
513 param.Set("offset_x", -raw_value_x + offset_x);
514 param.Set("offset_y", -raw_value_y + offset_y);
515 emulated_controller->SetStickParam(analog_id, param);
516 });
491 context_menu.addAction(tr("Invert axis"), [&] { 517 context_menu.addAction(tr("Invert axis"), [&] {
492 if (sub_button_id == 2 || sub_button_id == 3) { 518 if (sub_button_id == 2 || sub_button_id == 3) {
493 const bool invert_value = param.Get("invert_x", "+") == "-"; 519 const bool invert_value = param.Get("invert_x", "+") == "-";
@@ -1306,6 +1332,9 @@ void ConfigureInputPlayer::HandleClick(
1306 QPushButton* button, std::size_t button_id, 1332 QPushButton* button, std::size_t button_id,
1307 std::function<void(const Common::ParamPackage&)> new_input_setter, 1333 std::function<void(const Common::ParamPackage&)> new_input_setter,
1308 InputCommon::Polling::InputType type) { 1334 InputCommon::Polling::InputType type) {
1335 if (timeout_timer->isActive()) {
1336 return;
1337 }
1309 if (button == ui->buttonMotionLeft || button == ui->buttonMotionRight) { 1338 if (button == ui->buttonMotionLeft || button == ui->buttonMotionRight) {
1310 button->setText(tr("Shake!")); 1339 button->setText(tr("Shake!"));
1311 } else { 1340 } else {
diff --git a/src/yuzu/configuration/configure_input_player_widget.cpp b/src/yuzu/configuration/configure_input_player_widget.cpp
index 6630321cb..fb168b2ca 100644
--- a/src/yuzu/configuration/configure_input_player_widget.cpp
+++ b/src/yuzu/configuration/configure_input_player_widget.cpp
@@ -70,7 +70,6 @@ void PlayerControlPreview::UpdateColors() {
70 colors.slider_arrow = QColor(14, 15, 18); 70 colors.slider_arrow = QColor(14, 15, 18);
71 colors.font2 = QColor(255, 255, 255); 71 colors.font2 = QColor(255, 255, 255);
72 colors.indicator = QColor(170, 238, 255); 72 colors.indicator = QColor(170, 238, 255);
73 colors.indicator2 = QColor(100, 255, 100);
74 colors.deadzone = QColor(204, 136, 136); 73 colors.deadzone = QColor(204, 136, 136);
75 colors.slider_button = colors.button; 74 colors.slider_button = colors.button;
76 } 75 }
@@ -88,7 +87,6 @@ void PlayerControlPreview::UpdateColors() {
88 colors.slider_arrow = QColor(65, 68, 73); 87 colors.slider_arrow = QColor(65, 68, 73);
89 colors.font2 = QColor(0, 0, 0); 88 colors.font2 = QColor(0, 0, 0);
90 colors.indicator = QColor(0, 0, 200); 89 colors.indicator = QColor(0, 0, 200);
91 colors.indicator2 = QColor(0, 150, 0);
92 colors.deadzone = QColor(170, 0, 0); 90 colors.deadzone = QColor(170, 0, 0);
93 colors.slider_button = QColor(153, 149, 149); 91 colors.slider_button = QColor(153, 149, 149);
94 } 92 }
@@ -101,6 +99,8 @@ void PlayerControlPreview::UpdateColors() {
101 colors.font = QColor(255, 255, 255); 99 colors.font = QColor(255, 255, 255);
102 colors.led_on = QColor(255, 255, 0); 100 colors.led_on = QColor(255, 255, 0);
103 colors.led_off = QColor(170, 238, 255); 101 colors.led_off = QColor(170, 238, 255);
102 colors.indicator2 = QColor(59, 165, 93);
103 colors.charging = QColor(250, 168, 26);
104 104
105 colors.left = colors.primary; 105 colors.left = colors.primary;
106 colors.right = colors.primary; 106 colors.right = colors.primary;
@@ -357,7 +357,7 @@ void PlayerControlPreview::DrawLeftController(QPainter& p, const QPointF center)
357 DrawCircle(p, center + QPoint(26, 71), 5); 357 DrawCircle(p, center + QPoint(26, 71), 5);
358 358
359 // Draw battery 359 // Draw battery
360 DrawBattery(p, center + QPoint(-170, -140), 360 DrawBattery(p, center + QPoint(-160, -140),
361 battery_values[Core::HID::EmulatedDeviceIndex::LeftIndex]); 361 battery_values[Core::HID::EmulatedDeviceIndex::LeftIndex]);
362} 362}
363 363
@@ -484,7 +484,7 @@ void PlayerControlPreview::DrawRightController(QPainter& p, const QPointF center
484 DrawSymbol(p, center + QPoint(-26, 66), Symbol::House, 5); 484 DrawSymbol(p, center + QPoint(-26, 66), Symbol::House, 5);
485 485
486 // Draw battery 486 // Draw battery
487 DrawBattery(p, center + QPoint(110, -140), 487 DrawBattery(p, center + QPoint(120, -140),
488 battery_values[Core::HID::EmulatedDeviceIndex::RightIndex]); 488 battery_values[Core::HID::EmulatedDeviceIndex::RightIndex]);
489} 489}
490 490
@@ -621,9 +621,9 @@ void PlayerControlPreview::DrawDualController(QPainter& p, const QPointF center)
621 DrawSymbol(p, center + QPoint(50, 60), Symbol::House, 4.2f); 621 DrawSymbol(p, center + QPoint(50, 60), Symbol::House, 4.2f);
622 622
623 // Draw battery 623 // Draw battery
624 DrawBattery(p, center + QPoint(-100, -160), 624 DrawBattery(p, center + QPoint(-200, -10),
625 battery_values[Core::HID::EmulatedDeviceIndex::LeftIndex]); 625 battery_values[Core::HID::EmulatedDeviceIndex::LeftIndex]);
626 DrawBattery(p, center + QPoint(40, -160), 626 DrawBattery(p, center + QPoint(160, -10),
627 battery_values[Core::HID::EmulatedDeviceIndex::RightIndex]); 627 battery_values[Core::HID::EmulatedDeviceIndex::RightIndex]);
628} 628}
629 629
@@ -694,12 +694,12 @@ void PlayerControlPreview::DrawHandheldController(QPainter& p, const QPointF cen
694 694
695 // ZL and ZR buttons 695 // ZL and ZR buttons
696 p.setPen(colors.outline); 696 p.setPen(colors.outline);
697 DrawTriggerButton(p, center + QPoint(-210, -130), Direction::Left, button_values[ZL]); 697 DrawTriggerButton(p, center + QPoint(-210, -120), Direction::Left, button_values[ZL]);
698 DrawTriggerButton(p, center + QPoint(210, -130), Direction::Right, button_values[ZR]); 698 DrawTriggerButton(p, center + QPoint(210, -120), Direction::Right, button_values[ZR]);
699 p.setPen(colors.transparent); 699 p.setPen(colors.transparent);
700 p.setBrush(colors.font); 700 p.setBrush(colors.font);
701 DrawSymbol(p, center + QPoint(-210, -130), Symbol::ZL, 1.5f); 701 DrawSymbol(p, center + QPoint(-210, -120), Symbol::ZL, 1.5f);
702 DrawSymbol(p, center + QPoint(210, -130), Symbol::ZR, 1.5f); 702 DrawSymbol(p, center + QPoint(210, -120), Symbol::ZR, 1.5f);
703 703
704 // Minus and Plus button 704 // Minus and Plus button
705 p.setPen(colors.outline); 705 p.setPen(colors.outline);
@@ -725,9 +725,9 @@ void PlayerControlPreview::DrawHandheldController(QPainter& p, const QPointF cen
725 DrawSymbol(p, center + QPoint(161, 37), Symbol::House, 2.75f); 725 DrawSymbol(p, center + QPoint(161, 37), Symbol::House, 2.75f);
726 726
727 // Draw battery 727 // Draw battery
728 DrawBattery(p, center + QPoint(-200, 110), 728 DrawBattery(p, center + QPoint(-188, 95),
729 battery_values[Core::HID::EmulatedDeviceIndex::LeftIndex]); 729 battery_values[Core::HID::EmulatedDeviceIndex::LeftIndex]);
730 DrawBattery(p, center + QPoint(130, 110), 730 DrawBattery(p, center + QPoint(150, 95),
731 battery_values[Core::HID::EmulatedDeviceIndex::RightIndex]); 731 battery_values[Core::HID::EmulatedDeviceIndex::RightIndex]);
732} 732}
733 733
@@ -781,12 +781,12 @@ void PlayerControlPreview::DrawProController(QPainter& p, const QPointF center)
781 781
782 // ZL and ZR buttons 782 // ZL and ZR buttons
783 p.setPen(colors.outline); 783 p.setPen(colors.outline);
784 DrawTriggerButton(p, center + QPoint(-210, -130), Direction::Left, button_values[ZL]); 784 DrawTriggerButton(p, center + QPoint(-210, -120), Direction::Left, button_values[ZL]);
785 DrawTriggerButton(p, center + QPoint(210, -130), Direction::Right, button_values[ZR]); 785 DrawTriggerButton(p, center + QPoint(210, -120), Direction::Right, button_values[ZR]);
786 p.setPen(colors.transparent); 786 p.setPen(colors.transparent);
787 p.setBrush(colors.font); 787 p.setBrush(colors.font);
788 DrawSymbol(p, center + QPoint(-210, -130), Symbol::ZL, 1.5f); 788 DrawSymbol(p, center + QPoint(-210, -120), Symbol::ZL, 1.5f);
789 DrawSymbol(p, center + QPoint(210, -130), Symbol::ZR, 1.5f); 789 DrawSymbol(p, center + QPoint(210, -120), Symbol::ZR, 1.5f);
790 790
791 // Minus and Plus buttons 791 // Minus and Plus buttons
792 p.setPen(colors.outline); 792 p.setPen(colors.outline);
@@ -818,7 +818,7 @@ void PlayerControlPreview::DrawProController(QPainter& p, const QPointF center)
818 DrawSymbol(p, center + QPoint(29, -56), Symbol::House, 3.9f); 818 DrawSymbol(p, center + QPoint(29, -56), Symbol::House, 3.9f);
819 819
820 // Draw battery 820 // Draw battery
821 DrawBattery(p, center + QPoint(-30, -160), 821 DrawBattery(p, center + QPoint(-20, -160),
822 battery_values[Core::HID::EmulatedDeviceIndex::LeftIndex]); 822 battery_values[Core::HID::EmulatedDeviceIndex::LeftIndex]);
823} 823}
824 824
@@ -875,7 +875,7 @@ void PlayerControlPreview::DrawGCController(QPainter& p, const QPointF center) {
875 DrawCircleButton(p, center + QPoint(0, -44), button_values[Plus], 8); 875 DrawCircleButton(p, center + QPoint(0, -44), button_values[Plus], 8);
876 876
877 // Draw battery 877 // Draw battery
878 DrawBattery(p, center + QPoint(-30, -165), 878 DrawBattery(p, center + QPoint(-20, 110),
879 battery_values[Core::HID::EmulatedDeviceIndex::LeftIndex]); 879 battery_values[Core::HID::EmulatedDeviceIndex::LeftIndex]);
880} 880}
881 881
@@ -1030,6 +1030,10 @@ constexpr std::array<float, 30 * 2> symbol_c = {
1030 -2.37f, 5.64f, -0.65f, 6.44f, 1.25f, 6.47f, 3.06f, 5.89f, 4.63f, 4.92f, 4.63f, 6.83f, 1030 -2.37f, 5.64f, -0.65f, 6.44f, 1.25f, 6.47f, 3.06f, 5.89f, 4.63f, 4.92f, 4.63f, 6.83f,
1031}; 1031};
1032 1032
1033constexpr std::array<float, 6 * 2> symbol_charging = {
1034 6.5f, -1.0f, 1.0f, -1.0f, 1.0f, -3.0f, -6.5f, 1.0f, -1.0f, 1.0f, -1.0f, 3.0f,
1035};
1036
1033constexpr std::array<float, 12 * 2> house = { 1037constexpr std::array<float, 12 * 2> house = {
1034 -1.3f, 0.0f, -0.93f, 0.0f, -0.93f, 1.15f, 0.93f, 1.15f, 0.93f, 0.0f, 1.3f, 0.0f, 1038 -1.3f, 0.0f, -0.93f, 0.0f, -0.93f, 1.15f, 0.93f, 1.15f, 0.93f, 0.0f, 1.3f, 0.0f,
1035 0.0f, -1.2f, -1.3f, 0.0f, -0.43f, 0.0f, -0.43f, .73f, 0.43f, .73f, 0.43f, 0.0f, 1039 0.0f, -1.2f, -1.3f, 0.0f, -0.43f, 0.0f, -0.43f, .73f, 0.43f, .73f, 0.43f, 0.0f,
@@ -2674,36 +2678,43 @@ void PlayerControlPreview::DrawBattery(QPainter& p, QPointF center,
2674 if (battery == Common::Input::BatteryLevel::None) { 2678 if (battery == Common::Input::BatteryLevel::None) {
2675 return; 2679 return;
2676 } 2680 }
2677 p.setPen(colors.outline); 2681 // Draw outline
2682 p.setPen(QPen(colors.button, 5));
2683 p.setBrush(colors.transparent);
2684 p.drawRoundedRect(center.x(), center.y(), 34, 16, 2, 2);
2685
2686 p.setPen(QPen(colors.button, 3));
2687 p.drawRect(center.x() + 35, center.y() + 4.5f, 4, 7);
2688
2689 // Draw Battery shape
2690 p.setPen(QPen(colors.indicator2, 3));
2678 p.setBrush(colors.transparent); 2691 p.setBrush(colors.transparent);
2679 p.drawRect(center.x(), center.y(), 56, 20); 2692 p.drawRoundedRect(center.x(), center.y(), 34, 16, 2, 2);
2680 p.drawRect(center.x() + 56, center.y() + 6, 3, 8); 2693
2681 p.setBrush(colors.deadzone); 2694 p.setPen(QPen(colors.indicator2, 1));
2695 p.setBrush(colors.indicator2);
2696 p.drawRect(center.x() + 35, center.y() + 4.5f, 4, 7);
2682 switch (battery) { 2697 switch (battery) {
2683 case Common::Input::BatteryLevel::Charging: 2698 case Common::Input::BatteryLevel::Charging:
2684 p.setBrush(colors.indicator2); 2699 p.drawRect(center.x(), center.y(), 34, 16);
2685 p.drawText(center + QPoint(2, 14), tr("Charging")); 2700 p.setPen(colors.slider);
2701 p.setBrush(colors.charging);
2702 DrawSymbol(p, center + QPointF(17.0f, 8.0f), Symbol::Charging, 2.1f);
2686 break; 2703 break;
2687 case Common::Input::BatteryLevel::Full: 2704 case Common::Input::BatteryLevel::Full:
2688 p.drawRect(center.x() + 42, center.y(), 14, 20); 2705 p.drawRect(center.x(), center.y(), 34, 16);
2689 p.drawRect(center.x() + 28, center.y(), 14, 20);
2690 p.drawRect(center.x() + 14, center.y(), 14, 20);
2691 p.drawRect(center.x(), center.y(), 14, 20);
2692 break; 2706 break;
2693 case Common::Input::BatteryLevel::Medium: 2707 case Common::Input::BatteryLevel::Medium:
2694 p.drawRect(center.x() + 28, center.y(), 14, 20); 2708 p.drawRect(center.x(), center.y(), 25, 16);
2695 p.drawRect(center.x() + 14, center.y(), 14, 20);
2696 p.drawRect(center.x(), center.y(), 14, 20);
2697 break; 2709 break;
2698 case Common::Input::BatteryLevel::Low: 2710 case Common::Input::BatteryLevel::Low:
2699 p.drawRect(center.x() + 14, center.y(), 14, 20); 2711 p.drawRect(center.x(), center.y(), 17, 16);
2700 p.drawRect(center.x(), center.y(), 14, 20);
2701 break; 2712 break;
2702 case Common::Input::BatteryLevel::Critical: 2713 case Common::Input::BatteryLevel::Critical:
2703 p.drawRect(center.x(), center.y(), 14, 20); 2714 p.drawRect(center.x(), center.y(), 6, 16);
2704 break; 2715 break;
2705 case Common::Input::BatteryLevel::Empty: 2716 case Common::Input::BatteryLevel::Empty:
2706 p.drawRect(center.x(), center.y(), 5, 20); 2717 p.drawRect(center.x(), center.y(), 3, 16);
2707 break; 2718 break;
2708 default: 2719 default:
2709 break; 2720 break;
@@ -2724,6 +2735,7 @@ void PlayerControlPreview::DrawSymbol(QPainter& p, const QPointF center, Symbol
2724 std::array<QPointF, symbol_sl.size() / 2> sl_icon; 2735 std::array<QPointF, symbol_sl.size() / 2> sl_icon;
2725 std::array<QPointF, symbol_zr.size() / 2> zr_icon; 2736 std::array<QPointF, symbol_zr.size() / 2> zr_icon;
2726 std::array<QPointF, symbol_sr.size() / 2> sr_icon; 2737 std::array<QPointF, symbol_sr.size() / 2> sr_icon;
2738 std::array<QPointF, symbol_charging.size() / 2> charging_icon;
2727 switch (symbol) { 2739 switch (symbol) {
2728 case Symbol::House: 2740 case Symbol::House:
2729 for (std::size_t point = 0; point < house.size() / 2; ++point) { 2741 for (std::size_t point = 0; point < house.size() / 2; ++point) {
@@ -2809,6 +2821,13 @@ void PlayerControlPreview::DrawSymbol(QPainter& p, const QPointF center, Symbol
2809 } 2821 }
2810 p.drawPolygon(sr_icon.data(), static_cast<int>(sr_icon.size())); 2822 p.drawPolygon(sr_icon.data(), static_cast<int>(sr_icon.size()));
2811 break; 2823 break;
2824 case Symbol::Charging:
2825 for (std::size_t point = 0; point < symbol_charging.size() / 2; ++point) {
2826 charging_icon[point] = center + QPointF(symbol_charging[point * 2] * icon_size,
2827 symbol_charging[point * 2 + 1] * icon_size);
2828 }
2829 p.drawPolygon(charging_icon.data(), static_cast<int>(charging_icon.size()));
2830 break;
2812 } 2831 }
2813} 2832}
2814 2833
diff --git a/src/yuzu/configuration/configure_input_player_widget.h b/src/yuzu/configuration/configure_input_player_widget.h
index 4cd5c3be0..3582ef77a 100644
--- a/src/yuzu/configuration/configure_input_player_widget.h
+++ b/src/yuzu/configuration/configure_input_player_widget.h
@@ -72,6 +72,7 @@ private:
72 ZL, 72 ZL,
73 ZR, 73 ZR,
74 SR, 74 SR,
75 Charging,
75 }; 76 };
76 77
77 struct ColorMapping { 78 struct ColorMapping {
@@ -94,6 +95,7 @@ private:
94 QColor slider_button{}; 95 QColor slider_button{};
95 QColor slider_arrow{}; 96 QColor slider_arrow{};
96 QColor deadzone{}; 97 QColor deadzone{};
98 QColor charging{};
97 }; 99 };
98 100
99 void UpdateColors(); 101 void UpdateColors();
diff --git a/src/yuzu/configuration/configure_profile_manager.cpp b/src/yuzu/configuration/configure_profile_manager.cpp
index 78b6374c0..d9f6dee4e 100644
--- a/src/yuzu/configuration/configure_profile_manager.cpp
+++ b/src/yuzu/configuration/configure_profile_manager.cpp
@@ -33,10 +33,10 @@ constexpr std::array<u8, 107> backup_jpeg{
33 0x01, 0x01, 0x00, 0x00, 0x3f, 0x00, 0xd2, 0xcf, 0x20, 0xff, 0xd9, 33 0x01, 0x01, 0x00, 0x00, 0x3f, 0x00, 0xd2, 0xcf, 0x20, 0xff, 0xd9,
34}; 34};
35 35
36QString GetImagePath(Common::UUID uuid) { 36QString GetImagePath(const Common::UUID& uuid) {
37 const auto path = 37 const auto path =
38 Common::FS::GetYuzuPath(Common::FS::YuzuPath::NANDDir) / 38 Common::FS::GetYuzuPath(Common::FS::YuzuPath::NANDDir) /
39 fmt::format("system/save/8000000000000010/su/avators/{}.jpg", uuid.FormatSwitch()); 39 fmt::format("system/save/8000000000000010/su/avators/{}.jpg", uuid.FormattedString());
40 return QString::fromStdString(Common::FS::PathToUTF8String(path)); 40 return QString::fromStdString(Common::FS::PathToUTF8String(path));
41} 41}
42 42
@@ -55,10 +55,10 @@ QString FormatUserEntryText(const QString& username, Common::UUID uuid) {
55 return ConfigureProfileManager::tr("%1\n%2", 55 return ConfigureProfileManager::tr("%1\n%2",
56 "%1 is the profile username, %2 is the formatted UUID (e.g. " 56 "%1 is the profile username, %2 is the formatted UUID (e.g. "
57 "00112233-4455-6677-8899-AABBCCDDEEFF))") 57 "00112233-4455-6677-8899-AABBCCDDEEFF))")
58 .arg(username, QString::fromStdString(uuid.FormatSwitch())); 58 .arg(username, QString::fromStdString(uuid.FormattedString()));
59} 59}
60 60
61QPixmap GetIcon(Common::UUID uuid) { 61QPixmap GetIcon(const Common::UUID& uuid) {
62 QPixmap icon{GetImagePath(uuid)}; 62 QPixmap icon{GetImagePath(uuid)};
63 63
64 if (!icon) { 64 if (!icon) {
@@ -200,7 +200,7 @@ void ConfigureProfileManager::AddUser() {
200 return; 200 return;
201 } 201 }
202 202
203 const auto uuid = Common::UUID::Generate(); 203 const auto uuid = Common::UUID::MakeRandom();
204 profile_manager->CreateNewUser(uuid, username.toStdString()); 204 profile_manager->CreateNewUser(uuid, username.toStdString());
205 205
206 item_model->appendRow(new QStandardItem{GetIcon(uuid), FormatUserEntryText(username, uuid)}); 206 item_model->appendRow(new QStandardItem{GetIcon(uuid), FormatUserEntryText(username, uuid)});
diff --git a/src/yuzu/configuration/configure_touch_from_button.cpp b/src/yuzu/configuration/configure_touch_from_button.cpp
index bde0a08c4..211a00217 100644
--- a/src/yuzu/configuration/configure_touch_from_button.cpp
+++ b/src/yuzu/configuration/configure_touch_from_button.cpp
@@ -227,6 +227,9 @@ void ConfigureTouchFromButton::RenameMapping() {
227} 227}
228 228
229void ConfigureTouchFromButton::GetButtonInput(const int row_index, const bool is_new) { 229void ConfigureTouchFromButton::GetButtonInput(const int row_index, const bool is_new) {
230 if (timeout_timer->isActive()) {
231 return;
232 }
230 binding_list_model->item(row_index, 0)->setText(tr("[press key]")); 233 binding_list_model->item(row_index, 0)->setText(tr("[press key]"));
231 234
232 input_setter = [this, row_index, is_new](const Common::ParamPackage& params, 235 input_setter = [this, row_index, is_new](const Common::ParamPackage& params,
diff --git a/src/yuzu/configuration/configure_vibration.cpp b/src/yuzu/configuration/configure_vibration.cpp
index adce04b27..779b6401c 100644
--- a/src/yuzu/configuration/configure_vibration.cpp
+++ b/src/yuzu/configuration/configure_vibration.cpp
@@ -9,11 +9,14 @@
9 9
10#include "common/param_package.h" 10#include "common/param_package.h"
11#include "common/settings.h" 11#include "common/settings.h"
12#include "core/hid/emulated_controller.h"
13#include "core/hid/hid_core.h"
14#include "core/hid/hid_types.h"
12#include "ui_configure_vibration.h" 15#include "ui_configure_vibration.h"
13#include "yuzu/configuration/configure_vibration.h" 16#include "yuzu/configuration/configure_vibration.h"
14 17
15ConfigureVibration::ConfigureVibration(QWidget* parent) 18ConfigureVibration::ConfigureVibration(QWidget* parent, Core::HID::HIDCore& hid_core_)
16 : QDialog(parent), ui(std::make_unique<Ui::ConfigureVibration>()) { 19 : QDialog(parent), ui(std::make_unique<Ui::ConfigureVibration>()), hid_core{hid_core_} {
17 ui->setupUi(this); 20 ui->setupUi(this);
18 21
19 vibration_groupboxes = { 22 vibration_groupboxes = {
@@ -31,6 +34,13 @@ ConfigureVibration::ConfigureVibration(QWidget* parent)
31 const auto& players = Settings::values.players.GetValue(); 34 const auto& players = Settings::values.players.GetValue();
32 35
33 for (std::size_t i = 0; i < NUM_PLAYERS; ++i) { 36 for (std::size_t i = 0; i < NUM_PLAYERS; ++i) {
37 auto controller = hid_core.GetEmulatedControllerByIndex(i);
38 Core::HID::ControllerUpdateCallback engine_callback{
39 .on_change = [this,
40 i](Core::HID::ControllerTriggerType type) { VibrateController(type, i); },
41 .is_npad_service = false,
42 };
43 controller_callback_key[i] = controller->SetCallback(engine_callback);
34 vibration_groupboxes[i]->setChecked(players[i].vibration_enabled); 44 vibration_groupboxes[i]->setChecked(players[i].vibration_enabled);
35 vibration_spinboxes[i]->setValue(players[i].vibration_strength); 45 vibration_spinboxes[i]->setValue(players[i].vibration_strength);
36 } 46 }
@@ -45,7 +55,14 @@ ConfigureVibration::ConfigureVibration(QWidget* parent)
45 RetranslateUI(); 55 RetranslateUI();
46} 56}
47 57
48ConfigureVibration::~ConfigureVibration() = default; 58ConfigureVibration::~ConfigureVibration() {
59 StopVibrations();
60
61 for (std::size_t i = 0; i < NUM_PLAYERS; ++i) {
62 auto controller = hid_core.GetEmulatedControllerByIndex(i);
63 controller->DeleteCallback(controller_callback_key[i]);
64 }
65};
49 66
50void ConfigureVibration::ApplyConfiguration() { 67void ConfigureVibration::ApplyConfiguration() {
51 auto& players = Settings::values.players.GetValue(); 68 auto& players = Settings::values.players.GetValue();
@@ -70,3 +87,54 @@ void ConfigureVibration::changeEvent(QEvent* event) {
70void ConfigureVibration::RetranslateUI() { 87void ConfigureVibration::RetranslateUI() {
71 ui->retranslateUi(this); 88 ui->retranslateUi(this);
72} 89}
90
91void ConfigureVibration::VibrateController(Core::HID::ControllerTriggerType type,
92 std::size_t player_index) {
93 if (type != Core::HID::ControllerTriggerType::Button) {
94 return;
95 }
96
97 auto& player = Settings::values.players.GetValue()[player_index];
98 auto controller = hid_core.GetEmulatedControllerByIndex(player_index);
99 const int vibration_strenght = vibration_spinboxes[player_index]->value();
100 const auto& buttons = controller->GetButtonsValues();
101
102 bool button_is_pressed = false;
103 for (std::size_t i = 0; i < buttons.size(); ++i) {
104 if (buttons[i].value) {
105 button_is_pressed = true;
106 break;
107 }
108 }
109
110 if (!button_is_pressed) {
111 StopVibrations();
112 return;
113 }
114
115 const int old_vibration_enabled = player.vibration_enabled;
116 const bool old_vibration_strenght = player.vibration_strength;
117 player.vibration_enabled = true;
118 player.vibration_strength = vibration_strenght;
119
120 const Core::HID::VibrationValue vibration{
121 .low_amplitude = 1.0f,
122 .low_frequency = 160.0f,
123 .high_amplitude = 1.0f,
124 .high_frequency = 320.0f,
125 };
126 controller->SetVibration(0, vibration);
127 controller->SetVibration(1, vibration);
128
129 // Restore previous values
130 player.vibration_enabled = old_vibration_enabled;
131 player.vibration_strength = old_vibration_strenght;
132}
133
134void ConfigureVibration::StopVibrations() {
135 for (std::size_t i = 0; i < NUM_PLAYERS; ++i) {
136 auto controller = hid_core.GetEmulatedControllerByIndex(i);
137 controller->SetVibration(0, Core::HID::DEFAULT_VIBRATION_VALUE);
138 controller->SetVibration(1, Core::HID::DEFAULT_VIBRATION_VALUE);
139 }
140}
diff --git a/src/yuzu/configuration/configure_vibration.h b/src/yuzu/configuration/configure_vibration.h
index 37bbc2653..50b8195fa 100644
--- a/src/yuzu/configuration/configure_vibration.h
+++ b/src/yuzu/configuration/configure_vibration.h
@@ -15,11 +15,16 @@ namespace Ui {
15class ConfigureVibration; 15class ConfigureVibration;
16} 16}
17 17
18namespace Core::HID {
19enum class ControllerTriggerType;
20class HIDCore;
21} // namespace Core::HID
22
18class ConfigureVibration : public QDialog { 23class ConfigureVibration : public QDialog {
19 Q_OBJECT 24 Q_OBJECT
20 25
21public: 26public:
22 explicit ConfigureVibration(QWidget* parent); 27 explicit ConfigureVibration(QWidget* parent, Core::HID::HIDCore& hid_core_);
23 ~ConfigureVibration() override; 28 ~ConfigureVibration() override;
24 29
25 void ApplyConfiguration(); 30 void ApplyConfiguration();
@@ -27,14 +32,21 @@ public:
27private: 32private:
28 void changeEvent(QEvent* event) override; 33 void changeEvent(QEvent* event) override;
29 void RetranslateUI(); 34 void RetranslateUI();
35 void VibrateController(Core::HID::ControllerTriggerType type, std::size_t player_index);
36 void StopVibrations();
30 37
31 std::unique_ptr<Ui::ConfigureVibration> ui; 38 std::unique_ptr<Ui::ConfigureVibration> ui;
32 39
33 static constexpr std::size_t NUM_PLAYERS = 8; 40 static constexpr std::size_t NUM_PLAYERS = 8;
34 41
35 // Groupboxes encapsulating the vibration strength spinbox. 42 /// Groupboxes encapsulating the vibration strength spinbox.
36 std::array<QGroupBox*, NUM_PLAYERS> vibration_groupboxes; 43 std::array<QGroupBox*, NUM_PLAYERS> vibration_groupboxes;
37 44
38 // Spinboxes representing the vibration strength percentage. 45 /// Spinboxes representing the vibration strength percentage.
39 std::array<QSpinBox*, NUM_PLAYERS> vibration_spinboxes; 46 std::array<QSpinBox*, NUM_PLAYERS> vibration_spinboxes;
47
48 /// Callback index to stop the controllers events
49 std::array<int, NUM_PLAYERS> controller_callback_key;
50
51 Core::HID::HIDCore& hid_core;
40}; 52};
diff --git a/src/yuzu/configuration/configure_vibration.ui b/src/yuzu/configuration/configure_vibration.ui
index efdf317a9..447a18eb1 100644
--- a/src/yuzu/configuration/configure_vibration.ui
+++ b/src/yuzu/configuration/configure_vibration.ui
@@ -17,6 +17,13 @@
17 <string notr="true"/> 17 <string notr="true"/>
18 </property> 18 </property>
19 <layout class="QVBoxLayout"> 19 <layout class="QVBoxLayout">
20 <item row="0" column="0" colspan="4">
21 <widget class="QLabel" name="label_1">
22 <property name="text">
23 <string>Press any controller button to vibrate the controller.</string>
24 </property>
25 </widget>
26 </item>
20 <item> 27 <item>
21 <widget class="QGroupBox" name="vibrationStrengthGroup"> 28 <widget class="QGroupBox" name="vibrationStrengthGroup">
22 <property name="title"> 29 <property name="title">
diff --git a/src/yuzu/debugger/console.cpp b/src/yuzu/debugger/console.cpp
index f89ea8ea7..4b508b466 100644
--- a/src/yuzu/debugger/console.cpp
+++ b/src/yuzu/debugger/console.cpp
@@ -30,6 +30,7 @@ void ToggleConsole() {
30 freopen_s(&temp, "CONIN$", "r", stdin); 30 freopen_s(&temp, "CONIN$", "r", stdin);
31 freopen_s(&temp, "CONOUT$", "w", stdout); 31 freopen_s(&temp, "CONOUT$", "w", stdout);
32 freopen_s(&temp, "CONOUT$", "w", stderr); 32 freopen_s(&temp, "CONOUT$", "w", stderr);
33 SetConsoleOutputCP(65001);
33 SetColorConsoleBackendEnabled(true); 34 SetColorConsoleBackendEnabled(true);
34 } 35 }
35 } else { 36 } else {
diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp
index d9e689d14..06774768d 100644
--- a/src/yuzu/main.cpp
+++ b/src/yuzu/main.cpp
@@ -25,6 +25,7 @@
25#include "core/file_sys/vfs_real.h" 25#include "core/file_sys/vfs_real.h"
26#include "core/frontend/applets/controller.h" 26#include "core/frontend/applets/controller.h"
27#include "core/frontend/applets/general_frontend.h" 27#include "core/frontend/applets/general_frontend.h"
28#include "core/frontend/applets/mii.h"
28#include "core/frontend/applets/software_keyboard.h" 29#include "core/frontend/applets/software_keyboard.h"
29#include "core/hid/emulated_controller.h" 30#include "core/hid/emulated_controller.h"
30#include "core/hid/hid_core.h" 31#include "core/hid/hid_core.h"
@@ -249,9 +250,9 @@ GMainWindow::GMainWindow()
249#ifdef ARCHITECTURE_x86_64 250#ifdef ARCHITECTURE_x86_64
250 const auto& caps = Common::GetCPUCaps(); 251 const auto& caps = Common::GetCPUCaps();
251 std::string cpu_string = caps.cpu_string; 252 std::string cpu_string = caps.cpu_string;
252 if (caps.avx || caps.avx2 || caps.avx512) { 253 if (caps.avx || caps.avx2 || caps.avx512f) {
253 cpu_string += " | AVX"; 254 cpu_string += " | AVX";
254 if (caps.avx512) { 255 if (caps.avx512f) {
255 cpu_string += "512"; 256 cpu_string += "512";
256 } else if (caps.avx2) { 257 } else if (caps.avx2) {
257 cpu_string += '2'; 258 cpu_string += '2';
@@ -806,21 +807,8 @@ void GMainWindow::InitializeWidgets() {
806 filter_status_button = new QPushButton(); 807 filter_status_button = new QPushButton();
807 filter_status_button->setObjectName(QStringLiteral("TogglableStatusBarButton")); 808 filter_status_button->setObjectName(QStringLiteral("TogglableStatusBarButton"));
808 filter_status_button->setFocusPolicy(Qt::NoFocus); 809 filter_status_button->setFocusPolicy(Qt::NoFocus);
809 connect(filter_status_button, &QPushButton::clicked, [&] { 810 connect(filter_status_button, &QPushButton::clicked, this,
810 auto filter = Settings::values.scaling_filter.GetValue(); 811 &GMainWindow::OnToggleAdaptingFilter);
811 if (filter == Settings::ScalingFilter::LastFilter) {
812 filter = Settings::ScalingFilter::NearestNeighbor;
813 } else {
814 filter = static_cast<Settings::ScalingFilter>(static_cast<u32>(filter) + 1);
815 }
816 if (Settings::values.renderer_backend.GetValue() == Settings::RendererBackend::OpenGL &&
817 filter == Settings::ScalingFilter::Fsr) {
818 filter = Settings::ScalingFilter::NearestNeighbor;
819 }
820 Settings::values.scaling_filter.SetValue(filter);
821 filter_status_button->setChecked(true);
822 UpdateFilterText();
823 });
824 auto filter = Settings::values.scaling_filter.GetValue(); 812 auto filter = Settings::values.scaling_filter.GetValue();
825 if (Settings::values.renderer_backend.GetValue() == Settings::RendererBackend::OpenGL && 813 if (Settings::values.renderer_backend.GetValue() == Settings::RendererBackend::OpenGL &&
826 filter == Settings::ScalingFilter::Fsr) { 814 filter == Settings::ScalingFilter::Fsr) {
@@ -835,25 +823,7 @@ void GMainWindow::InitializeWidgets() {
835 dock_status_button = new QPushButton(); 823 dock_status_button = new QPushButton();
836 dock_status_button->setObjectName(QStringLiteral("TogglableStatusBarButton")); 824 dock_status_button->setObjectName(QStringLiteral("TogglableStatusBarButton"));
837 dock_status_button->setFocusPolicy(Qt::NoFocus); 825 dock_status_button->setFocusPolicy(Qt::NoFocus);
838 connect(dock_status_button, &QPushButton::clicked, [&] { 826 connect(dock_status_button, &QPushButton::clicked, this, &GMainWindow::OnToggleDockedMode);
839 const bool is_docked = Settings::values.use_docked_mode.GetValue();
840 auto* player_1 = system->HIDCore().GetEmulatedController(Core::HID::NpadIdType::Player1);
841 auto* handheld = system->HIDCore().GetEmulatedController(Core::HID::NpadIdType::Handheld);
842
843 if (!is_docked && handheld->IsConnected()) {
844 QMessageBox::warning(this, tr("Invalid config detected"),
845 tr("Handheld controller can't be used on docked mode. Pro "
846 "controller will be selected."));
847 handheld->Disconnect();
848 player_1->SetNpadStyleIndex(Core::HID::NpadStyleIndex::ProController);
849 player_1->Connect();
850 controller_dialog->refreshConfiguration();
851 }
852
853 Settings::values.use_docked_mode.SetValue(!is_docked);
854 dock_status_button->setChecked(!is_docked);
855 OnDockedModeChanged(is_docked, !is_docked, *system);
856 });
857 dock_status_button->setText(tr("DOCK")); 827 dock_status_button->setText(tr("DOCK"));
858 dock_status_button->setCheckable(true); 828 dock_status_button->setCheckable(true);
859 dock_status_button->setChecked(Settings::values.use_docked_mode.GetValue()); 829 dock_status_button->setChecked(Settings::values.use_docked_mode.GetValue());
@@ -863,22 +833,7 @@ void GMainWindow::InitializeWidgets() {
863 gpu_accuracy_button->setObjectName(QStringLiteral("GPUStatusBarButton")); 833 gpu_accuracy_button->setObjectName(QStringLiteral("GPUStatusBarButton"));
864 gpu_accuracy_button->setCheckable(true); 834 gpu_accuracy_button->setCheckable(true);
865 gpu_accuracy_button->setFocusPolicy(Qt::NoFocus); 835 gpu_accuracy_button->setFocusPolicy(Qt::NoFocus);
866 connect(gpu_accuracy_button, &QPushButton::clicked, [this] { 836 connect(gpu_accuracy_button, &QPushButton::clicked, this, &GMainWindow::OnToggleGpuAccuracy);
867 switch (Settings::values.gpu_accuracy.GetValue()) {
868 case Settings::GPUAccuracy::High: {
869 Settings::values.gpu_accuracy.SetValue(Settings::GPUAccuracy::Normal);
870 break;
871 }
872 case Settings::GPUAccuracy::Normal:
873 case Settings::GPUAccuracy::Extreme:
874 default: {
875 Settings::values.gpu_accuracy.SetValue(Settings::GPUAccuracy::High);
876 }
877 }
878
879 system->ApplySettings();
880 UpdateGPUAccuracyButton();
881 });
882 UpdateGPUAccuracyButton(); 837 UpdateGPUAccuracyButton();
883 statusBar()->insertPermanentWidget(0, gpu_accuracy_button); 838 statusBar()->insertPermanentWidget(0, gpu_accuracy_button);
884 839
@@ -965,6 +920,7 @@ void GMainWindow::LinkActionShortcut(QAction* action, const QString& action_name
965 static const QString main_window = QStringLiteral("Main Window"); 920 static const QString main_window = QStringLiteral("Main Window");
966 action->setShortcut(hotkey_registry.GetKeySequence(main_window, action_name)); 921 action->setShortcut(hotkey_registry.GetKeySequence(main_window, action_name));
967 action->setShortcutContext(hotkey_registry.GetShortcutContext(main_window, action_name)); 922 action->setShortcutContext(hotkey_registry.GetShortcutContext(main_window, action_name));
923 action->setAutoRepeat(false);
968 924
969 this->addAction(action); 925 this->addAction(action);
970 926
@@ -979,7 +935,7 @@ void GMainWindow::InitializeHotkeys() {
979 hotkey_registry.LoadHotkeys(); 935 hotkey_registry.LoadHotkeys();
980 936
981 LinkActionShortcut(ui->action_Load_File, QStringLiteral("Load File")); 937 LinkActionShortcut(ui->action_Load_File, QStringLiteral("Load File"));
982 LinkActionShortcut(ui->action_Load_Amiibo, QStringLiteral("Load Amiibo")); 938 LinkActionShortcut(ui->action_Load_Amiibo, QStringLiteral("Load/Remove Amiibo"));
983 LinkActionShortcut(ui->action_Exit, QStringLiteral("Exit yuzu")); 939 LinkActionShortcut(ui->action_Exit, QStringLiteral("Exit yuzu"));
984 LinkActionShortcut(ui->action_Restart, QStringLiteral("Restart Emulation")); 940 LinkActionShortcut(ui->action_Restart, QStringLiteral("Restart Emulation"));
985 LinkActionShortcut(ui->action_Pause, QStringLiteral("Continue/Pause Emulation")); 941 LinkActionShortcut(ui->action_Pause, QStringLiteral("Continue/Pause Emulation"));
@@ -1008,12 +964,10 @@ void GMainWindow::InitializeHotkeys() {
1008 ToggleFullscreen(); 964 ToggleFullscreen();
1009 } 965 }
1010 }); 966 });
1011 connect_shortcut(QStringLiteral("Change Docked Mode"), [&] { 967 connect_shortcut(QStringLiteral("Change Adapting Filter"),
1012 Settings::values.use_docked_mode.SetValue(!Settings::values.use_docked_mode.GetValue()); 968 &GMainWindow::OnToggleAdaptingFilter);
1013 OnDockedModeChanged(!Settings::values.use_docked_mode.GetValue(), 969 connect_shortcut(QStringLiteral("Change Docked Mode"), &GMainWindow::OnToggleDockedMode);
1014 Settings::values.use_docked_mode.GetValue(), *system); 970 connect_shortcut(QStringLiteral("Change GPU Accuracy"), &GMainWindow::OnToggleGpuAccuracy);
1015 dock_status_button->setChecked(Settings::values.use_docked_mode.GetValue());
1016 });
1017 connect_shortcut(QStringLiteral("Audio Mute/Unmute"), 971 connect_shortcut(QStringLiteral("Audio Mute/Unmute"),
1018 [] { Settings::values.audio_muted = !Settings::values.audio_muted; }); 972 [] { Settings::values.audio_muted = !Settings::values.audio_muted; });
1019 connect_shortcut(QStringLiteral("Audio Volume Down"), [] { 973 connect_shortcut(QStringLiteral("Audio Volume Down"), [] {
@@ -1051,8 +1005,10 @@ void GMainWindow::SetDefaultUIGeometry() {
1051} 1005}
1052 1006
1053void GMainWindow::RestoreUIState() { 1007void GMainWindow::RestoreUIState() {
1008 setWindowFlags(windowFlags() & ~Qt::FramelessWindowHint);
1054 restoreGeometry(UISettings::values.geometry); 1009 restoreGeometry(UISettings::values.geometry);
1055 restoreState(UISettings::values.state); 1010 restoreState(UISettings::values.state);
1011 render_window->setWindowFlags(render_window->windowFlags() & ~Qt::FramelessWindowHint);
1056 render_window->restoreGeometry(UISettings::values.renderwindow_geometry); 1012 render_window->restoreGeometry(UISettings::values.renderwindow_geometry);
1057#if MICROPROFILE_ENABLED 1013#if MICROPROFILE_ENABLED
1058 microProfileDialog->restoreGeometry(UISettings::values.microprofile_geometry); 1014 microProfileDialog->restoreGeometry(UISettings::values.microprofile_geometry);
@@ -1079,14 +1035,14 @@ void GMainWindow::RestoreUIState() {
1079} 1035}
1080 1036
1081void GMainWindow::OnAppFocusStateChanged(Qt::ApplicationState state) { 1037void GMainWindow::OnAppFocusStateChanged(Qt::ApplicationState state) {
1082 if (!UISettings::values.pause_when_in_background) {
1083 return;
1084 }
1085 if (state != Qt::ApplicationHidden && state != Qt::ApplicationInactive && 1038 if (state != Qt::ApplicationHidden && state != Qt::ApplicationInactive &&
1086 state != Qt::ApplicationActive) { 1039 state != Qt::ApplicationActive) {
1087 LOG_DEBUG(Frontend, "ApplicationState unusual flag: {} ", state); 1040 LOG_DEBUG(Frontend, "ApplicationState unusual flag: {} ", state);
1088 } 1041 }
1089 if (emulation_running) { 1042 if (!emulation_running) {
1043 return;
1044 }
1045 if (UISettings::values.pause_when_in_background) {
1090 if (emu_thread->IsRunning() && 1046 if (emu_thread->IsRunning() &&
1091 (state & (Qt::ApplicationHidden | Qt::ApplicationInactive))) { 1047 (state & (Qt::ApplicationHidden | Qt::ApplicationInactive))) {
1092 auto_paused = true; 1048 auto_paused = true;
@@ -1096,6 +1052,16 @@ void GMainWindow::OnAppFocusStateChanged(Qt::ApplicationState state) {
1096 OnStartGame(); 1052 OnStartGame();
1097 } 1053 }
1098 } 1054 }
1055 if (UISettings::values.mute_when_in_background) {
1056 if (!Settings::values.audio_muted &&
1057 (state & (Qt::ApplicationHidden | Qt::ApplicationInactive))) {
1058 Settings::values.audio_muted = true;
1059 auto_muted = true;
1060 } else if (auto_muted && state == Qt::ApplicationActive) {
1061 Settings::values.audio_muted = false;
1062 auto_muted = false;
1063 }
1064 }
1099} 1065}
1100 1066
1101void GMainWindow::ConnectWidgetEvents() { 1067void GMainWindow::ConnectWidgetEvents() {
@@ -1320,6 +1286,7 @@ bool GMainWindow::LoadROM(const QString& filename, u64 program_id, std::size_t p
1320 std::make_unique<QtControllerSelector>(*this), // Controller Selector 1286 std::make_unique<QtControllerSelector>(*this), // Controller Selector
1321 std::make_unique<QtErrorDisplay>(*this), // Error Display 1287 std::make_unique<QtErrorDisplay>(*this), // Error Display
1322 nullptr, // Parental Controls 1288 nullptr, // Parental Controls
1289 nullptr, // Mii editor
1323 nullptr, // Photo Viewer 1290 nullptr, // Photo Viewer
1324 std::make_unique<QtProfileSelector>(*this), // Profile Selector 1291 std::make_unique<QtProfileSelector>(*this), // Profile Selector
1325 std::make_unique<QtSoftwareKeyboard>(*this), // Software Keyboard 1292 std::make_unique<QtSoftwareKeyboard>(*this), // Software Keyboard
@@ -1687,7 +1654,7 @@ void GMainWindow::OnGameListOpenFolder(u64 program_id, GameListOpenTarget target
1687 1654
1688 const auto user_save_data_path = FileSys::SaveDataFactory::GetFullPath( 1655 const auto user_save_data_path = FileSys::SaveDataFactory::GetFullPath(
1689 *system, FileSys::SaveDataSpaceId::NandUser, FileSys::SaveDataType::SaveData, 1656 *system, FileSys::SaveDataSpaceId::NandUser, FileSys::SaveDataType::SaveData,
1690 program_id, user_id->uuid, 0); 1657 program_id, user_id->AsU128(), 0);
1691 1658
1692 path = Common::FS::ConcatPathSafe(nand_dir, user_save_data_path); 1659 path = Common::FS::ConcatPathSafe(nand_dir, user_save_data_path);
1693 } else { 1660 } else {
@@ -2865,6 +2832,59 @@ void GMainWindow::OnTasReset() {
2865 input_subsystem->GetTas()->Reset(); 2832 input_subsystem->GetTas()->Reset();
2866} 2833}
2867 2834
2835void GMainWindow::OnToggleDockedMode() {
2836 const bool is_docked = Settings::values.use_docked_mode.GetValue();
2837 auto* player_1 = system->HIDCore().GetEmulatedController(Core::HID::NpadIdType::Player1);
2838 auto* handheld = system->HIDCore().GetEmulatedController(Core::HID::NpadIdType::Handheld);
2839
2840 if (!is_docked && handheld->IsConnected()) {
2841 QMessageBox::warning(this, tr("Invalid config detected"),
2842 tr("Handheld controller can't be used on docked mode. Pro "
2843 "controller will be selected."));
2844 handheld->Disconnect();
2845 player_1->SetNpadStyleIndex(Core::HID::NpadStyleIndex::ProController);
2846 player_1->Connect();
2847 controller_dialog->refreshConfiguration();
2848 }
2849
2850 Settings::values.use_docked_mode.SetValue(!is_docked);
2851 dock_status_button->setChecked(!is_docked);
2852 OnDockedModeChanged(is_docked, !is_docked, *system);
2853}
2854
2855void GMainWindow::OnToggleGpuAccuracy() {
2856 switch (Settings::values.gpu_accuracy.GetValue()) {
2857 case Settings::GPUAccuracy::High: {
2858 Settings::values.gpu_accuracy.SetValue(Settings::GPUAccuracy::Normal);
2859 break;
2860 }
2861 case Settings::GPUAccuracy::Normal:
2862 case Settings::GPUAccuracy::Extreme:
2863 default: {
2864 Settings::values.gpu_accuracy.SetValue(Settings::GPUAccuracy::High);
2865 }
2866 }
2867
2868 system->ApplySettings();
2869 UpdateGPUAccuracyButton();
2870}
2871
2872void GMainWindow::OnToggleAdaptingFilter() {
2873 auto filter = Settings::values.scaling_filter.GetValue();
2874 if (filter == Settings::ScalingFilter::LastFilter) {
2875 filter = Settings::ScalingFilter::NearestNeighbor;
2876 } else {
2877 filter = static_cast<Settings::ScalingFilter>(static_cast<u32>(filter) + 1);
2878 }
2879 if (Settings::values.renderer_backend.GetValue() == Settings::RendererBackend::OpenGL &&
2880 filter == Settings::ScalingFilter::Fsr) {
2881 filter = Settings::ScalingFilter::NearestNeighbor;
2882 }
2883 Settings::values.scaling_filter.SetValue(filter);
2884 filter_status_button->setChecked(true);
2885 UpdateFilterText();
2886}
2887
2868void GMainWindow::OnConfigurePerGame() { 2888void GMainWindow::OnConfigurePerGame() {
2869 const u64 title_id = system->GetCurrentProcessProgramID(); 2889 const u64 title_id = system->GetCurrentProcessProgramID();
2870 OpenPerGameConfiguration(title_id, game_path.toStdString()); 2890 OpenPerGameConfiguration(title_id, game_path.toStdString());
@@ -2909,6 +2929,25 @@ void GMainWindow::OnLoadAmiibo() {
2909 return; 2929 return;
2910 } 2930 }
2911 2931
2932 Service::SM::ServiceManager& sm = system->ServiceManager();
2933 auto nfc = sm.GetService<Service::NFP::Module::Interface>("nfp:user");
2934 if (nfc == nullptr) {
2935 QMessageBox::warning(this, tr("Error"), tr("The current game is not looking for amiibos"));
2936 return;
2937 }
2938 const auto nfc_state = nfc->GetCurrentState();
2939 if (nfc_state == Service::NFP::DeviceState::TagFound ||
2940 nfc_state == Service::NFP::DeviceState::TagMounted) {
2941 nfc->CloseAmiibo();
2942 QMessageBox::warning(this, tr("Amiibo"), tr("The current amiibo has been removed"));
2943 return;
2944 }
2945
2946 if (nfc_state != Service::NFP::DeviceState::SearchingForTag) {
2947 QMessageBox::warning(this, tr("Error"), tr("The current game is not looking for amiibos"));
2948 return;
2949 }
2950
2912 is_amiibo_file_select_active = true; 2951 is_amiibo_file_select_active = true;
2913 const QString extensions{QStringLiteral("*.bin")}; 2952 const QString extensions{QStringLiteral("*.bin")};
2914 const QString file_filter = tr("Amiibo File (%1);; All Files (*.*)").arg(extensions); 2953 const QString file_filter = tr("Amiibo File (%1);; All Files (*.*)").arg(extensions);
@@ -2929,6 +2968,15 @@ void GMainWindow::LoadAmiibo(const QString& filename) {
2929 return; 2968 return;
2930 } 2969 }
2931 2970
2971 // Remove amiibo if one is connected
2972 const auto nfc_state = nfc->GetCurrentState();
2973 if (nfc_state == Service::NFP::DeviceState::TagFound ||
2974 nfc_state == Service::NFP::DeviceState::TagMounted) {
2975 nfc->CloseAmiibo();
2976 QMessageBox::warning(this, tr("Amiibo"), tr("The current amiibo has been removed"));
2977 return;
2978 }
2979
2932 QFile nfc_file{filename}; 2980 QFile nfc_file{filename};
2933 if (!nfc_file.open(QIODevice::ReadOnly)) { 2981 if (!nfc_file.open(QIODevice::ReadOnly)) {
2934 QMessageBox::warning(this, tr("Error opening Amiibo data file"), 2982 QMessageBox::warning(this, tr("Error opening Amiibo data file"),
diff --git a/src/yuzu/main.h b/src/yuzu/main.h
index ca4ab9af5..6a35b9e3d 100644
--- a/src/yuzu/main.h
+++ b/src/yuzu/main.h
@@ -284,6 +284,9 @@ private slots:
284 void OnTasStartStop(); 284 void OnTasStartStop();
285 void OnTasRecord(); 285 void OnTasRecord();
286 void OnTasReset(); 286 void OnTasReset();
287 void OnToggleDockedMode();
288 void OnToggleGpuAccuracy();
289 void OnToggleAdaptingFilter();
287 void OnConfigurePerGame(); 290 void OnConfigurePerGame();
288 void OnLoadAmiibo(); 291 void OnLoadAmiibo();
289 void OnOpenYuzuFolder(); 292 void OnOpenYuzuFolder();
@@ -369,6 +372,7 @@ private:
369 QString game_path; 372 QString game_path;
370 373
371 bool auto_paused = false; 374 bool auto_paused = false;
375 bool auto_muted = false;
372 QTimer mouse_hide_timer; 376 QTimer mouse_hide_timer;
373 377
374 // FS 378 // FS
diff --git a/src/yuzu/main.ui b/src/yuzu/main.ui
index 5719b2ee4..6ab95b9a5 100644
--- a/src/yuzu/main.ui
+++ b/src/yuzu/main.ui
@@ -266,7 +266,7 @@
266 <bool>false</bool> 266 <bool>false</bool>
267 </property> 267 </property>
268 <property name="text"> 268 <property name="text">
269 <string>Load &amp;Amiibo...</string> 269 <string>Load/Remove &amp;Amiibo...</string>
270 </property> 270 </property>
271 </action> 271 </action>
272 <action name="action_Report_Compatibility"> 272 <action name="action_Report_Compatibility">
diff --git a/src/yuzu/uisettings.h b/src/yuzu/uisettings.h
index f7298ddad..06e8b46da 100644
--- a/src/yuzu/uisettings.h
+++ b/src/yuzu/uisettings.h
@@ -73,6 +73,7 @@ struct Values {
73 Settings::BasicSetting<bool> confirm_before_closing{true, "confirmClose"}; 73 Settings::BasicSetting<bool> confirm_before_closing{true, "confirmClose"};
74 Settings::BasicSetting<bool> first_start{true, "firstStart"}; 74 Settings::BasicSetting<bool> first_start{true, "firstStart"};
75 Settings::BasicSetting<bool> pause_when_in_background{false, "pauseWhenInBackground"}; 75 Settings::BasicSetting<bool> pause_when_in_background{false, "pauseWhenInBackground"};
76 Settings::BasicSetting<bool> mute_when_in_background{false, "muteWhenInBackground"};
76 Settings::BasicSetting<bool> hide_mouse{true, "hideInactiveMouse"}; 77 Settings::BasicSetting<bool> hide_mouse{true, "hideInactiveMouse"};
77 78
78 Settings::BasicSetting<bool> select_user_on_boot{false, "select_user_on_boot"}; 79 Settings::BasicSetting<bool> select_user_on_boot{false, "select_user_on_boot"};
diff --git a/src/yuzu_cmd/config.cpp b/src/yuzu_cmd/config.cpp
index 8e9c7d211..131bc2201 100644
--- a/src/yuzu_cmd/config.cpp
+++ b/src/yuzu_cmd/config.cpp
@@ -3,6 +3,7 @@
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <memory> 5#include <memory>
6#include <optional>
6#include <sstream> 7#include <sstream>
7 8
8// Ignore -Wimplicit-fallthrough due to https://github.com/libsdl-org/SDL/issues/4307 9// Ignore -Wimplicit-fallthrough due to https://github.com/libsdl-org/SDL/issues/4307
@@ -29,11 +30,12 @@
29 30
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
@@ -66,6 +68,11 @@ static const std::array<int, Settings::NativeButton::NumButtons> default_buttons
66 SDL_SCANCODE_M, SDL_SCANCODE_N, SDL_SCANCODE_1, SDL_SCANCODE_2, SDL_SCANCODE_B, 68 SDL_SCANCODE_M, SDL_SCANCODE_N, SDL_SCANCODE_1, SDL_SCANCODE_2, SDL_SCANCODE_B,
67}; 69};
68 70
71static const std::array<int, Settings::NativeMotion::NumMotions> default_motions = {
72 SDL_SCANCODE_7,
73 SDL_SCANCODE_8,
74};
75
69static const std::array<std::array<int, 5>, Settings::NativeAnalog::NumAnalogs> default_analogs{{ 76static const std::array<std::array<int, 5>, Settings::NativeAnalog::NumAnalogs> default_analogs{{
70 { 77 {
71 SDL_SCANCODE_UP, 78 SDL_SCANCODE_UP,
@@ -102,27 +109,42 @@ void Config::ReadSetting(const std::string& group, Settings::BasicSetting<Type>&
102void Config::ReadValues() { 109void Config::ReadValues() {
103 // Controls 110 // Controls
104 for (std::size_t p = 0; p < Settings::values.players.GetValue().size(); ++p) { 111 for (std::size_t p = 0; p < Settings::values.players.GetValue().size(); ++p) {
112 auto& player = Settings::values.players.GetValue()[p];
113
105 const auto group = fmt::format("ControlsP{}", p); 114 const auto group = fmt::format("ControlsP{}", p);
106 for (int i = 0; i < Settings::NativeButton::NumButtons; ++i) { 115 for (int i = 0; i < Settings::NativeButton::NumButtons; ++i) {
107 std::string default_param = InputCommon::GenerateKeyboardParam(default_buttons[i]); 116 std::string default_param = InputCommon::GenerateKeyboardParam(default_buttons[i]);
108 Settings::values.players.GetValue()[p].buttons[i] = 117 player.buttons[i] =
109 sdl2_config->Get(group, Settings::NativeButton::mapping[i], default_param); 118 sdl2_config->Get(group, Settings::NativeButton::mapping[i], default_param);
110 if (Settings::values.players.GetValue()[p].buttons[i].empty()) 119 if (player.buttons[i].empty()) {
111 Settings::values.players.GetValue()[p].buttons[i] = default_param; 120 player.buttons[i] = default_param;
121 }
112 } 122 }
113 123
114 for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) { 124 for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) {
115 std::string default_param = InputCommon::GenerateAnalogParamFromKeys( 125 std::string default_param = InputCommon::GenerateAnalogParamFromKeys(
116 default_analogs[i][0], default_analogs[i][1], default_analogs[i][2], 126 default_analogs[i][0], default_analogs[i][1], default_analogs[i][2],
117 default_analogs[i][3], default_analogs[i][4], 0.5f); 127 default_analogs[i][3], default_analogs[i][4], 0.5f);
118 Settings::values.players.GetValue()[p].analogs[i] = 128 player.analogs[i] =
119 sdl2_config->Get(group, Settings::NativeAnalog::mapping[i], default_param); 129 sdl2_config->Get(group, Settings::NativeAnalog::mapping[i], default_param);
120 if (Settings::values.players.GetValue()[p].analogs[i].empty()) 130 if (player.analogs[i].empty()) {
121 Settings::values.players.GetValue()[p].analogs[i] = default_param; 131 player.analogs[i] = default_param;
132 }
133 }
134
135 for (int i = 0; i < Settings::NativeMotion::NumMotions; ++i) {
136 const std::string default_param =
137 InputCommon::GenerateKeyboardParam(default_motions[i]);
138 auto& player_motions = player.motions[i];
139
140 player_motions =
141 sdl2_config->Get(group, Settings::NativeMotion::mapping[i], default_param);
142 if (player_motions.empty()) {
143 player_motions = default_param;
144 }
122 } 145 }
123 146
124 Settings::values.players.GetValue()[p].connected = 147 player.connected = sdl2_config->GetBoolean(group, "connected", false);
125 sdl2_config->GetBoolean(group, "connected", false);
126 } 148 }
127 149
128 ReadSetting("ControlsGeneral", Settings::values.mouse_enabled); 150 ReadSetting("ControlsGeneral", Settings::values.mouse_enabled);
@@ -246,6 +268,7 @@ void Config::ReadValues() {
246 268
247 // Core 269 // Core
248 ReadSetting("Core", Settings::values.use_multi_core); 270 ReadSetting("Core", Settings::values.use_multi_core);
271 ReadSetting("Core", Settings::values.use_extended_memory_layout);
249 272
250 // Cpu 273 // Cpu
251 ReadSetting("Cpu", Settings::values.cpu_accuracy); 274 ReadSetting("Cpu", Settings::values.cpu_accuracy);
@@ -259,11 +282,14 @@ void Config::ReadValues() {
259 ReadSetting("Cpu", Settings::values.cpuopt_misc_ir); 282 ReadSetting("Cpu", Settings::values.cpuopt_misc_ir);
260 ReadSetting("Cpu", Settings::values.cpuopt_reduce_misalign_checks); 283 ReadSetting("Cpu", Settings::values.cpuopt_reduce_misalign_checks);
261 ReadSetting("Cpu", Settings::values.cpuopt_fastmem); 284 ReadSetting("Cpu", Settings::values.cpuopt_fastmem);
285 ReadSetting("Cpu", Settings::values.cpuopt_fastmem_exclusives);
286 ReadSetting("Cpu", Settings::values.cpuopt_recompile_exclusives);
262 ReadSetting("Cpu", Settings::values.cpuopt_unsafe_unfuse_fma); 287 ReadSetting("Cpu", Settings::values.cpuopt_unsafe_unfuse_fma);
263 ReadSetting("Cpu", Settings::values.cpuopt_unsafe_reduce_fp_error); 288 ReadSetting("Cpu", Settings::values.cpuopt_unsafe_reduce_fp_error);
264 ReadSetting("Cpu", Settings::values.cpuopt_unsafe_ignore_standard_fpcr); 289 ReadSetting("Cpu", Settings::values.cpuopt_unsafe_ignore_standard_fpcr);
265 ReadSetting("Cpu", Settings::values.cpuopt_unsafe_inaccurate_nan); 290 ReadSetting("Cpu", Settings::values.cpuopt_unsafe_inaccurate_nan);
266 ReadSetting("Cpu", Settings::values.cpuopt_unsafe_fastmem_check); 291 ReadSetting("Cpu", Settings::values.cpuopt_unsafe_fastmem_check);
292 ReadSetting("Cpu", Settings::values.cpuopt_unsafe_ignore_global_monitor);
267 293
268 // Renderer 294 // Renderer
269 ReadSetting("Renderer", Settings::values.renderer_backend); 295 ReadSetting("Renderer", Settings::values.renderer_backend);
diff --git a/src/yuzu_cmd/config.h b/src/yuzu_cmd/config.h
index 1ee932be2..f61ba23ec 100644
--- a/src/yuzu_cmd/config.h
+++ b/src/yuzu_cmd/config.h
@@ -6,6 +6,7 @@
6 6
7#include <filesystem> 7#include <filesystem>
8#include <memory> 8#include <memory>
9#include <optional>
9#include <string> 10#include <string>
10 11
11#include "common/settings.h" 12#include "common/settings.h"
@@ -13,14 +14,14 @@
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 6d613bf7a..34782c378 100644
--- a/src/yuzu_cmd/default_ini.h
+++ b/src/yuzu_cmd/default_ini.h
@@ -124,7 +124,11 @@ keyboard_enabled =
124[Core] 124[Core]
125# Whether to use multi-core for CPU emulation 125# Whether to use multi-core for CPU emulation
126# 0: Disabled, 1 (default): Enabled 126# 0: Disabled, 1 (default): Enabled
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.
@@ -174,6 +178,14 @@ cpuopt_reduce_misalign_checks =
174# 0: Disabled, 1 (default): Enabled 178# 0: Disabled, 1 (default): Enabled
175cpuopt_fastmem = 179cpuopt_fastmem =
176 180
181# Enable Host MMU Emulation for exclusive memory instructions (faster guest memory access)
182# 0: Disabled, 1 (default): Enabled
183cpuopt_fastmem_exclusives =
184
185# Enable fallback on failure of fastmem of exclusive memory instructions (faster guest memory access)
186# 0: Disabled, 1 (default): Enabled
187cpuopt_recompile_exclusives =
188
177# Enable unfuse FMA (improve performance on CPUs without FMA) 189# Enable unfuse FMA (improve performance on CPUs without FMA)
178# Only enabled if cpu_accuracy is set to Unsafe. Automatically chosen with cpu_accuracy = Auto-select. 190# Only enabled if cpu_accuracy is set to Unsafe. Automatically chosen with cpu_accuracy = Auto-select.
179# 0: Disabled, 1 (default): Enabled 191# 0: Disabled, 1 (default): Enabled
@@ -199,6 +211,11 @@ cpuopt_unsafe_inaccurate_nan =
199# 0: Disabled, 1 (default): Enabled 211# 0: Disabled, 1 (default): Enabled
200cpuopt_unsafe_fastmem_check = 212cpuopt_unsafe_fastmem_check =
201 213
214# Enable faster exclusive instructions
215# Only enabled if cpu_accuracy is set to Unsafe. Automatically chosen with cpu_accuracy = Auto-select.
216# 0: Disabled, 1 (default): Enabled
217cpuopt_unsafe_ignore_global_monitor =
218
202[Renderer] 219[Renderer]
203# Which backend API to use. 220# Which backend API to use.
204# 0 (default): OpenGL, 1: Vulkan 221# 0 (default): OpenGL, 1: Vulkan
diff --git a/src/yuzu_cmd/yuzu.cpp b/src/yuzu_cmd/yuzu.cpp
index b44ea0cc4..f6d563017 100644
--- a/src/yuzu_cmd/yuzu.cpp
+++ b/src/yuzu_cmd/yuzu.cpp
@@ -66,7 +66,8 @@ static void PrintHelp(const char* argv0) {
66 "-f, --fullscreen Start in fullscreen mode\n" 66 "-f, --fullscreen Start in fullscreen mode\n"
67 "-h, --help Display this help and exit\n" 67 "-h, --help Display this help and exit\n"
68 "-v, --version Output version information and exit\n" 68 "-v, --version Output version information and exit\n"
69 "-p, --program Pass following string as arguments to executable\n"; 69 "-p, --program Pass following string as arguments to executable\n"
70 "-c, --config Load the specified configuration file\n";
70} 71}
71 72
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