summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CMakeLists.txt14
-rw-r--r--dist/languages/uk.ts7321
-rw-r--r--externals/CMakeLists.txt11
m---------externals/dynarmic0
m---------externals/xbyak0
-rw-r--r--src/audio_core/CMakeLists.txt2
-rw-r--r--src/audio_core/in/audio_in_system.cpp2
-rw-r--r--src/audio_core/in/audio_in_system.h2
-rw-r--r--src/audio_core/out/audio_out_system.cpp4
-rw-r--r--src/audio_core/out/audio_out_system.h4
-rw-r--r--src/common/common_funcs.h4
-rw-r--r--src/common/concepts.h16
-rw-r--r--src/common/fs/file.h12
-rw-r--r--src/common/host_memory.cpp6
-rw-r--r--src/common/input.h5
-rw-r--r--src/common/settings.cpp1
-rw-r--r--src/core/CMakeLists.txt20
-rw-r--r--src/core/arm/dynarmic/arm_dynarmic_32.cpp7
-rw-r--r--src/core/arm/dynarmic/arm_dynarmic_64.cpp2
-rw-r--r--src/core/arm/dynarmic/arm_dynarmic_cp15.cpp22
-rw-r--r--src/core/arm/exclusive_monitor.cpp4
-rw-r--r--src/core/core.cpp9
-rw-r--r--src/core/file_sys/control_metadata.cpp43
-rw-r--r--src/core/file_sys/control_metadata.h6
-rw-r--r--src/core/hid/emulated_controller.cpp46
-rw-r--r--src/core/hid/emulated_controller.h5
-rw-r--r--src/core/hle/ipc_helpers.h19
-rw-r--r--src/core/hle/kernel/board/nintendo/nx/k_system_control.h4
-rw-r--r--src/core/hle/kernel/global_scheduler_context.cpp22
-rw-r--r--src/core/hle/kernel/global_scheduler_context.h8
-rw-r--r--src/core/hle/kernel/hle_ipc.cpp63
-rw-r--r--src/core/hle/kernel/hle_ipc.h37
-rw-r--r--src/core/hle/kernel/init/init_slab_setup.cpp75
-rw-r--r--src/core/hle/kernel/init/init_slab_setup.h2
-rw-r--r--src/core/hle/kernel/k_class_token.cpp3
-rw-r--r--src/core/hle/kernel/k_class_token.h6
-rw-r--r--src/core/hle/kernel/k_client_port.cpp7
-rw-r--r--src/core/hle/kernel/k_client_port.h3
-rw-r--r--src/core/hle/kernel/k_debug.h20
-rw-r--r--src/core/hle/kernel/k_dynamic_page_manager.h67
-rw-r--r--src/core/hle/kernel/k_dynamic_resource_manager.h3
-rw-r--r--src/core/hle/kernel/k_event.cpp2
-rw-r--r--src/core/hle/kernel/k_event_info.h64
-rw-r--r--src/core/hle/kernel/k_handle_table.cpp33
-rw-r--r--src/core/hle/kernel/k_handle_table.h106
-rw-r--r--src/core/hle/kernel/k_memory_block.h122
-rw-r--r--src/core/hle/kernel/k_memory_layout.cpp10
-rw-r--r--src/core/hle/kernel/k_memory_layout.h19
-rw-r--r--src/core/hle/kernel/k_memory_manager.cpp270
-rw-r--r--src/core/hle/kernel/k_memory_manager.h259
-rw-r--r--src/core/hle/kernel/k_memory_region_type.h123
-rw-r--r--src/core/hle/kernel/k_page_bitmap.h243
-rw-r--r--src/core/hle/kernel/k_page_buffer.h14
-rw-r--r--src/core/hle/kernel/k_page_group.h86
-rw-r--r--src/core/hle/kernel/k_page_heap.cpp86
-rw-r--r--src/core/hle/kernel/k_page_heap.h39
-rw-r--r--src/core/hle/kernel/k_page_table.cpp1129
-rw-r--r--src/core/hle/kernel/k_page_table.h142
-rw-r--r--src/core/hle/kernel/k_page_table_manager.h55
-rw-r--r--src/core/hle/kernel/k_page_table_slab_heap.h93
-rw-r--r--src/core/hle/kernel/k_port.cpp6
-rw-r--r--src/core/hle/kernel/k_process.cpp18
-rw-r--r--src/core/hle/kernel/k_resource_limit.cpp11
-rw-r--r--src/core/hle/kernel/k_resource_limit.h11
-rw-r--r--src/core/hle/kernel/k_scheduler.cpp35
-rw-r--r--src/core/hle/kernel/k_scheduler_lock.h3
-rw-r--r--src/core/hle/kernel/k_server_port.cpp6
-rw-r--r--src/core/hle/kernel/k_server_port.h19
-rw-r--r--src/core/hle/kernel/k_server_session.cpp187
-rw-r--r--src/core/hle/kernel/k_server_session.h43
-rw-r--r--src/core/hle/kernel/k_session.cpp7
-rw-r--r--src/core/hle/kernel/k_session.h3
-rw-r--r--src/core/hle/kernel/k_shared_memory.cpp6
-rw-r--r--src/core/hle/kernel/k_system_resource.cpp26
-rw-r--r--src/core/hle/kernel/k_system_resource.h137
-rw-r--r--src/core/hle/kernel/k_thread.cpp48
-rw-r--r--src/core/hle/kernel/k_thread.h10
-rw-r--r--src/core/hle/kernel/k_transfer_memory.cpp2
-rw-r--r--src/core/hle/kernel/kernel.cpp284
-rw-r--r--src/core/hle/kernel/kernel.h50
-rw-r--r--src/core/hle/kernel/physical_core.cpp4
-rw-r--r--src/core/hle/kernel/service_thread.cpp242
-rw-r--r--src/core/hle/kernel/service_thread.h6
-rw-r--r--src/core/hle/kernel/slab_helpers.h78
-rw-r--r--src/core/hle/kernel/svc.cpp126
-rw-r--r--src/core/hle/kernel/svc_results.h1
-rw-r--r--src/core/hle/kernel/svc_types.h508
-rw-r--r--src/core/hle/result.h13
-rw-r--r--src/core/hle/service/acc/acc.cpp34
-rw-r--r--src/core/hle/service/acc/acc.h1
-rw-r--r--src/core/hle/service/acc/acc_u0.cpp2
-rw-r--r--src/core/hle/service/acc/profile_manager.cpp25
-rw-r--r--src/core/hle/service/acc/profile_manager.h3
-rw-r--r--src/core/hle/service/am/am.cpp13
-rw-r--r--src/core/hle/service/am/am.h1
-rw-r--r--src/core/hle/service/audio/audin_u.cpp2
-rw-r--r--src/core/hle/service/audio/audout_u.cpp2
-rw-r--r--src/core/hle/service/hid/controllers/npad.cpp4
-rw-r--r--src/core/hle/service/kernel_helpers.cpp2
-rw-r--r--src/core/hle/service/nfp/amiibo_crypto.cpp8
-rw-r--r--src/core/hle/service/nfp/amiibo_crypto.h3
-rw-r--r--src/core/hle/service/nfp/nfp_device.cpp9
-rw-r--r--src/core/hle/service/nfp/nfp_device.h1
-rw-r--r--src/core/hle/service/nfp/nfp_types.h5
-rw-r--r--src/core/hle/service/nfp/nfp_user.cpp3
-rw-r--r--src/core/hle/service/nfp/nfp_user.h8
-rw-r--r--src/core/hle/service/nvdrv/core/nvmap.cpp5
-rw-r--r--src/core/hle/service/nvdrv/core/nvmap.h1
-rw-r--r--src/core/hle/service/nvdrv/devices/nvmap.cpp16
-rw-r--r--src/core/hle/service/nvflinger/buffer_queue_producer.cpp7
-rw-r--r--src/core/hle/service/nvflinger/nvflinger.cpp13
-rw-r--r--src/core/hle/service/nvflinger/nvflinger.h5
-rw-r--r--src/core/hle/service/service.cpp21
-rw-r--r--src/core/hle/service/service.h4
-rw-r--r--src/core/hle/service/sm/sm.cpp44
-rw-r--r--src/core/hle/service/sm/sm.h2
-rw-r--r--src/core/hle/service/sm/sm_controller.cpp41
-rw-r--r--src/core/hle/service/vi/display/vi_display.h6
-rw-r--r--src/core/hle/service/vi/vi.cpp8
-rw-r--r--src/core/internal_network/socket_proxy.cpp4
-rw-r--r--src/input_common/drivers/gc_adapter.cpp6
-rw-r--r--src/input_common/drivers/gc_adapter.h4
-rw-r--r--src/input_common/drivers/sdl_driver.cpp64
-rw-r--r--src/input_common/drivers/sdl_driver.h4
-rw-r--r--src/input_common/input_engine.h7
-rw-r--r--src/input_common/input_poller.cpp6
-rw-r--r--src/shader_recompiler/CMakeLists.txt1
-rw-r--r--src/shader_recompiler/backend/glasm/emit_glasm.cpp3
-rw-r--r--src/shader_recompiler/backend/glasm/emit_glasm_bitwise_conversion.cpp4
-rw-r--r--src/shader_recompiler/backend/glasm/emit_glasm_context_get_set.cpp16
-rw-r--r--src/shader_recompiler/backend/glasm/emit_glasm_instructions.h3
-rw-r--r--src/shader_recompiler/backend/glasm/glasm_emit_context.cpp4
-rw-r--r--src/shader_recompiler/backend/glsl/emit_glsl_bitwise_conversion.cpp4
-rw-r--r--src/shader_recompiler/backend/glsl/emit_glsl_context_get_set.cpp16
-rw-r--r--src/shader_recompiler/backend/glsl/emit_glsl_instructions.h3
-rw-r--r--src/shader_recompiler/backend/glsl/glsl_emit_context.cpp3
-rw-r--r--src/shader_recompiler/backend/spirv/emit_spirv.h4
-rw-r--r--src/shader_recompiler/backend/spirv/emit_spirv_bitwise_conversion.cpp4
-rw-r--r--src/shader_recompiler/backend/spirv/emit_spirv_context_get_set.cpp24
-rw-r--r--src/shader_recompiler/backend/spirv/emit_spirv_instructions.h5
-rw-r--r--src/shader_recompiler/backend/spirv/spirv_emit_context.cpp35
-rw-r--r--src/shader_recompiler/backend/spirv/spirv_emit_context.h5
-rw-r--r--src/shader_recompiler/environment.h4
-rw-r--r--src/shader_recompiler/frontend/ir/ir_emitter.cpp17
-rw-r--r--src/shader_recompiler/frontend/ir/ir_emitter.h4
-rw-r--r--src/shader_recompiler/frontend/ir/microinstruction.cpp5
-rw-r--r--src/shader_recompiler/frontend/ir/opcodes.h1
-rw-r--r--src/shader_recompiler/frontend/ir/opcodes.inc3
-rw-r--r--src/shader_recompiler/frontend/ir/patch.h4
-rw-r--r--src/shader_recompiler/frontend/ir/type.h31
-rw-r--r--src/shader_recompiler/frontend/ir/value.cpp3
-rw-r--r--src/shader_recompiler/frontend/ir/value.h16
-rw-r--r--src/shader_recompiler/frontend/maxwell/translate/impl/move_special_register.cpp3
-rw-r--r--src/shader_recompiler/frontend/maxwell/translate_program.cpp4
-rw-r--r--src/shader_recompiler/host_translate_info.h1
-rw-r--r--src/shader_recompiler/ir_opt/collect_shader_info_pass.cpp3
-rw-r--r--src/shader_recompiler/ir_opt/dead_code_elimination_pass.cpp98
-rw-r--r--src/shader_recompiler/ir_opt/passes.h7
-rw-r--r--src/shader_recompiler/ir_opt/position_pass.cpp77
-rw-r--r--src/shader_recompiler/ir_opt/texture_pass.cpp51
-rw-r--r--src/shader_recompiler/shader_info.h12
-rw-r--r--src/tests/video_core/buffer_base.cpp2
-rw-r--r--src/video_core/CMakeLists.txt15
-rw-r--r--src/video_core/engines/maxwell_3d.cpp261
-rw-r--r--src/video_core/engines/maxwell_3d.h52
-rw-r--r--src/video_core/macro/macro.cpp3
-rw-r--r--src/video_core/macro/macro_hle.cpp47
-rw-r--r--src/video_core/macro/macro_interpreter.cpp2
-rw-r--r--src/video_core/macro/macro_jit_x64.cpp2
-rw-r--r--src/video_core/memory_manager.cpp2
-rw-r--r--src/video_core/rasterizer_interface.h2
-rw-r--r--src/video_core/renderer_opengl/gl_buffer_cache.cpp15
-rw-r--r--src/video_core/renderer_opengl/gl_graphics_pipeline.cpp11
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer.cpp27
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer.h4
-rw-r--r--src/video_core/renderer_opengl/gl_shader_cache.cpp6
-rw-r--r--src/video_core/renderer_opengl/gl_state_tracker.cpp4
-rw-r--r--src/video_core/renderer_vulkan/pipeline_helper.h10
-rw-r--r--src/video_core/renderer_vulkan/vk_blit_screen.cpp6
-rw-r--r--src/video_core/renderer_vulkan/vk_blit_screen.h5
-rw-r--r--src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp22
-rw-r--r--src/video_core/renderer_vulkan/vk_graphics_pipeline.h4
-rw-r--r--src/video_core/renderer_vulkan/vk_pipeline_cache.cpp4
-rw-r--r--src/video_core/renderer_vulkan/vk_rasterizer.cpp69
-rw-r--r--src/video_core/renderer_vulkan/vk_rasterizer.h5
-rw-r--r--src/video_core/renderer_vulkan/vk_scheduler.h4
-rw-r--r--src/video_core/renderer_vulkan/vk_state_tracker.cpp2
-rw-r--r--src/video_core/renderer_vulkan/vk_texture_cache.cpp27
-rw-r--r--src/video_core/renderer_vulkan/vk_texture_cache.h11
-rw-r--r--src/video_core/shader_environment.cpp104
-rw-r--r--src/video_core/shader_environment.h21
-rw-r--r--src/video_core/texture_cache/util.cpp1
-rw-r--r--src/yuzu/CMakeLists.txt4
-rw-r--r--src/yuzu/bootmanager.cpp337
-rw-r--r--src/yuzu/configuration/config.cpp6
-rw-r--r--src/yuzu/configuration/configure_ui.cpp9
-rw-r--r--src/yuzu/configuration/configure_ui.ui23
-rw-r--r--src/yuzu/game_list.cpp4
-rw-r--r--src/yuzu/main.cpp74
-rw-r--r--src/yuzu/main.h13
-rw-r--r--src/yuzu/main.ui1
-rw-r--r--src/yuzu/uisettings.h7
202 files changed, 12657 insertions, 2135 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
index b625743ea..1d13dc74e 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -133,13 +133,13 @@ if (NOT ENABLE_GENERIC)
133 if (MSVC) 133 if (MSVC)
134 detect_architecture("_M_AMD64" x86_64) 134 detect_architecture("_M_AMD64" x86_64)
135 detect_architecture("_M_IX86" x86) 135 detect_architecture("_M_IX86" x86)
136 detect_architecture("_M_ARM" ARM) 136 detect_architecture("_M_ARM" arm)
137 detect_architecture("_M_ARM64" ARM64) 137 detect_architecture("_M_ARM64" arm64)
138 else() 138 else()
139 detect_architecture("__x86_64__" x86_64) 139 detect_architecture("__x86_64__" x86_64)
140 detect_architecture("__i386__" x86) 140 detect_architecture("__i386__" x86)
141 detect_architecture("__arm__" ARM) 141 detect_architecture("__arm__" arm)
142 detect_architecture("__aarch64__" ARM64) 142 detect_architecture("__aarch64__" arm64)
143 endif() 143 endif()
144endif() 144endif()
145 145
@@ -218,11 +218,11 @@ if(ENABLE_QT)
218 set(QT_VERSION 5.15) 218 set(QT_VERSION 5.15)
219 219
220 # Check for system Qt on Linux, fallback to bundled Qt 220 # Check for system Qt on Linux, fallback to bundled Qt
221 if (${CMAKE_SYSTEM_NAME} STREQUAL "Linux") 221 if (UNIX AND NOT APPLE)
222 if (NOT YUZU_USE_BUNDLED_QT) 222 if (NOT YUZU_USE_BUNDLED_QT)
223 find_package(Qt5 ${QT_VERSION} COMPONENTS Widgets DBus Multimedia) 223 find_package(Qt5 ${QT_VERSION} COMPONENTS Widgets DBus Multimedia)
224 endif() 224 endif()
225 if (NOT Qt5_FOUND OR YUZU_USE_BUNDLED_QT) 225 if (${CMAKE_SYSTEM_NAME} STREQUAL "Linux" AND (NOT Qt5_FOUND OR YUZU_USE_BUNDLED_QT))
226 # Check for dependencies, then enable bundled Qt download 226 # Check for dependencies, then enable bundled Qt download
227 227
228 # Check that the system GLIBCXX version is compatible 228 # Check that the system GLIBCXX version is compatible
@@ -323,7 +323,7 @@ if(ENABLE_QT)
323 323
324 set(YUZU_QT_NO_CMAKE_SYSTEM_PATH "NO_CMAKE_SYSTEM_PATH") 324 set(YUZU_QT_NO_CMAKE_SYSTEM_PATH "NO_CMAKE_SYSTEM_PATH")
325 endif() 325 endif()
326 if ((${CMAKE_SYSTEM_NAME} STREQUAL "Linux") AND YUZU_USE_BUNDLED_QT) 326 if (UNIX AND NOT APPLE AND YUZU_USE_BUNDLED_QT)
327 find_package(Qt5 ${QT_VERSION} REQUIRED COMPONENTS Widgets Concurrent Multimedia DBus ${QT_PREFIX_HINT} ${YUZU_QT_NO_CMAKE_SYSTEM_PATH}) 327 find_package(Qt5 ${QT_VERSION} REQUIRED COMPONENTS Widgets Concurrent Multimedia DBus ${QT_PREFIX_HINT} ${YUZU_QT_NO_CMAKE_SYSTEM_PATH})
328 else() 328 else()
329 find_package(Qt5 ${QT_VERSION} REQUIRED COMPONENTS Widgets Concurrent Multimedia ${QT_PREFIX_HINT} ${YUZU_QT_NO_CMAKE_SYSTEM_PATH}) 329 find_package(Qt5 ${QT_VERSION} REQUIRED COMPONENTS Widgets Concurrent Multimedia ${QT_PREFIX_HINT} ${YUZU_QT_NO_CMAKE_SYSTEM_PATH})
diff --git a/dist/languages/uk.ts b/dist/languages/uk.ts
new file mode 100644
index 000000000..66a3ac96e
--- /dev/null
+++ b/dist/languages/uk.ts
@@ -0,0 +1,7321 @@
1<?xml version="1.0" ?><!DOCTYPE TS><TS version="2.1" language="uk" sourcelanguage="en_US">
2<context>
3 <name>AboutDialog</name>
4 <message>
5 <location filename="../../src/yuzu/aboutdialog.ui" line="14"/>
6 <source>About yuzu</source>
7 <translation>Про yuzu</translation>
8 </message>
9 <message>
10 <location filename="../../src/yuzu/aboutdialog.ui" line="72"/>
11 <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-size:28pt;&quot;&gt;yuzu&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
12 <translation>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-size:28pt;&quot;&gt;yuzu&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</translation>
13 </message>
14 <message>
15 <location filename="../../src/yuzu/aboutdialog.ui" line="85"/>
16 <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;%1 (%2)&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
17 <translation>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;%1 (%2)&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</translation>
18 </message>
19 <message>
20 <location filename="../../src/yuzu/aboutdialog.ui" line="98"/>
21 <source>&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.0//EN&quot; &quot;http://www.w3.org/TR/REC-html40/strict.dtd&quot;&gt;
22&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
23p, li { white-space: pre-wrap; }
24&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:'Ubuntu'; font-size:11pt; font-weight:400; font-style:normal;&quot;&gt;
25&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:'MS Shell Dlg 2'; font-size:12pt;&quot;&gt;yuzu is an experimental open-source emulator for the Nintendo Switch licensed under GPLv3.0+.&lt;/span&gt;&lt;/p&gt;
26&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-family:'MS Shell Dlg 2'; font-size:8pt;&quot;&gt;&lt;br /&gt;&lt;/p&gt;
27&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:&apos;MS Shell Dlg 2&apos;; font-size:12pt;&quot;&gt;This software should not be used to play games you have not legally obtained.&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
28 <translation>&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.0//EN&quot; &quot;http://www.w3.org/TR/REC-html40/strict.dtd&quot;&gt;
29&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
30p, li { white-space: pre-wrap; }
31&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:&apos;Ubuntu&apos;; font-size:11pt; font-weight:400; font-style:normal;&quot;&gt;
32&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:&apos;MS Shell Dlg 2&apos;; font-size:12pt;&quot;&gt;yuzu є експериментальним емулятором Nintendo Switch з відкритим кодом ліцензований під GPLv3.0+.&lt;/span&gt;&lt;/p&gt;
33&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-family:&apos;MS Shell Dlg 2&apos;; font-size:8pt;&quot;&gt;&lt;br /&gt;&lt;/p&gt;
34&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:&apos;MS Shell Dlg 2&apos;; font-size:12pt;&quot;&gt;Це програмне забезпечення не слід використовувати для ігор, які ви отримали незаконним шляхом.&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</translation>
35 </message>
36 <message>
37 <location filename="../../src/yuzu/aboutdialog.ui" line="130"/>
38 <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;a href=&quot;https://yuzu-emu.org/&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#039be5;&quot;&gt;Website&lt;/span&gt;&lt;/a&gt; | &lt;a href=&quot;https://github.com/yuzu-emu&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#039be5;&quot;&gt;Source Code&lt;/span&gt;&lt;/a&gt; | &lt;a href=&quot;https://github.com/yuzu-emu/yuzu/graphs/contributors&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#039be5;&quot;&gt;Contributors&lt;/span&gt;&lt;/a&gt; | &lt;a href=&quot;https://github.com/yuzu-emu/yuzu/blob/master/LICENSE.txt&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#039be5;&quot;&gt;License&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
39 <translation>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;a href=&quot;https://yuzu-emu.org/&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#039be5;&quot;&gt;Веб-сайт&lt;/span&gt;&lt;/a&gt; | &lt;a href=&quot;https://github.com/yuzu-emu&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#039be5;&quot;&gt;Вихідний код&lt;/span&gt;&lt;/a&gt; | &lt;a href=&quot;https://github.com/yuzu-emu/yuzu/graphs/contributors&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#039be5;&quot;&gt;Вкладники&lt;/span&gt;&lt;/a&gt; | &lt;a href=&quot;https://github.com/yuzu-emu/yuzu/blob/master/LICENSE.txt&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#039be5;&quot;&gt;Ліцензія&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</translation>
40 </message>
41 <message>
42 <location filename="../../src/yuzu/aboutdialog.ui" line="146"/>
43 <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-size:7pt;&quot;&gt;&amp;quot;Nintendo Switch&amp;quot; is a trademark of Nintendo. yuzu is not affiliated with Nintendo in any way.&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
44 <translation>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-size:7pt;&quot;&gt;&amp;quot;Nintendo Switch&amp;quot; є торговою маркою Nintendo. yuzu не пов&apos;язаний з Nintendo у будь-якому вигляді.&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</translation>
45 </message>
46</context>
47<context>
48 <name>CalibrationConfigurationDialog</name>
49 <message>
50 <location filename="../../src/yuzu/configuration/configure_motion_touch.cpp" line="23"/>
51 <source>Communicating with the server...</source>
52 <translation>Зв&apos;язок із сервером...</translation>
53 </message>
54 <message>
55 <location filename="../../src/yuzu/configuration/configure_motion_touch.cpp" line="24"/>
56 <source>Cancel</source>
57 <translation>Скасувати</translation>
58 </message>
59 <message>
60 <location filename="../../src/yuzu/configuration/configure_motion_touch.cpp" line="43"/>
61 <source>Touch the top left corner &lt;br&gt;of your touchpad.</source>
62 <translation>Торкніться верхнього лівого кута &lt;br&gt; вашого тачпаду.</translation>
63 </message>
64 <message>
65 <location filename="../../src/yuzu/configuration/configure_motion_touch.cpp" line="46"/>
66 <source>Now touch the bottom right corner &lt;br&gt;of your touchpad.</source>
67 <translation>Тепер торкніться правого нижнього кута &lt;br&gt; вашого тачпаду.</translation>
68 </message>
69 <message>
70 <location filename="../../src/yuzu/configuration/configure_motion_touch.cpp" line="49"/>
71 <source>Configuration completed!</source>
72 <translation>Налаштування завершено!</translation>
73 </message>
74 <message>
75 <location filename="../../src/yuzu/configuration/configure_motion_touch.cpp" line="57"/>
76 <source>OK</source>
77 <translation>ОК</translation>
78 </message>
79</context>
80<context>
81 <name>ChatRoom</name>
82 <message>
83 <location filename="../../src/yuzu/multiplayer/chat_room.ui" line="14"/>
84 <source>Room Window</source>
85 <translation>Вікно кімнати</translation>
86 </message>
87 <message>
88 <location filename="../../src/yuzu/multiplayer/chat_room.ui" line="40"/>
89 <source>Send Chat Message</source>
90 <translation>Надіслати повідомлення в чат</translation>
91 </message>
92 <message>
93 <location filename="../../src/yuzu/multiplayer/chat_room.ui" line="47"/>
94 <source>Send Message</source>
95 <translation>Надіслати повідомлення</translation>
96 </message>
97 <message>
98 <location filename="../../src/yuzu/multiplayer/chat_room.cpp" line="181"/>
99 <source>Members</source>
100 <translation>Члени</translation>
101 </message>
102 <message>
103 <location filename="../../src/yuzu/multiplayer/chat_room.cpp" line="318"/>
104 <source>%1 has joined</source>
105 <translation>%1 приєднався</translation>
106 </message>
107 <message>
108 <location filename="../../src/yuzu/multiplayer/chat_room.cpp" line="321"/>
109 <source>%1 has left</source>
110 <translation>%1 вийшов</translation>
111 </message>
112 <message>
113 <location filename="../../src/yuzu/multiplayer/chat_room.cpp" line="324"/>
114 <source>%1 has been kicked</source>
115 <translation>%1 вигнано</translation>
116 </message>
117 <message>
118 <location filename="../../src/yuzu/multiplayer/chat_room.cpp" line="327"/>
119 <source>%1 has been banned</source>
120 <translation>%1 заблоковано</translation>
121 </message>
122 <message>
123 <location filename="../../src/yuzu/multiplayer/chat_room.cpp" line="330"/>
124 <source>%1 has been unbanned</source>
125 <translation>%1 розблоковано</translation>
126 </message>
127 <message>
128 <location filename="../../src/yuzu/multiplayer/chat_room.cpp" line="446"/>
129 <source>View Profile</source>
130 <translation>Переглянути профіль</translation>
131 </message>
132 <message>
133 <location filename="../../src/yuzu/multiplayer/chat_room.cpp" line="459"/>
134 <location filename="../../src/yuzu/multiplayer/chat_room.cpp" line="469"/>
135 <source>Block Player</source>
136 <translation>Заблокувати гравця</translation>
137 </message>
138 <message>
139 <location filename="../../src/yuzu/multiplayer/chat_room.cpp" line="470"/>
140 <source>When you block a player, you will no longer receive chat messages from them.&lt;br&gt;&lt;br&gt;Are you sure you would like to block %1?</source>
141 <translation>Коли ви блокуєте гравця, ви більше не отримуватиме від нього повідомлення у чаті. &lt;br&gt;&lt;br&gt;Ви впевнені що бажаєте заблокувати %1?</translation>
142 </message>
143 <message>
144 <location filename="../../src/yuzu/multiplayer/chat_room.cpp" line="483"/>
145 <source>Kick</source>
146 <translation>Вигнати</translation>
147 </message>
148 <message>
149 <location filename="../../src/yuzu/multiplayer/chat_room.cpp" line="484"/>
150 <source>Ban</source>
151 <translation>Заблокувати</translation>
152 </message>
153 <message>
154 <location filename="../../src/yuzu/multiplayer/chat_room.cpp" line="488"/>
155 <source>Kick Player</source>
156 <translation>Вигнати гравця</translation>
157 </message>
158 <message>
159 <location filename="../../src/yuzu/multiplayer/chat_room.cpp" line="489"/>
160 <source>Are you sure you would like to &lt;b&gt;kick&lt;/b&gt; %1?</source>
161 <translation>Ви впевнені що бажаєте &lt;b&gt;вигнати&lt;/b&gt; %1?</translation>
162 </message>
163 <message>
164 <location filename="../../src/yuzu/multiplayer/chat_room.cpp" line="497"/>
165 <source>Ban Player</source>
166 <translation>Заблокувати гравця</translation>
167 </message>
168 <message>
169 <location filename="../../src/yuzu/multiplayer/chat_room.cpp" line="498"/>
170 <source>Are you sure you would like to &lt;b&gt;kick and ban&lt;/b&gt; %1?
171
172This would ban both their forum username and their IP address.</source>
173 <translation>Ви впевнені що бажаєте &lt;b&gt;вигнати і заблокувати&lt;/b&gt; %1?
174
175Ця дія заблокує ім&apos;я користувача на форумі та IP-адресу.</translation>
176 </message>
177</context>
178<context>
179 <name>ClientRoom</name>
180 <message>
181 <location filename="../../src/yuzu/multiplayer/client_room.ui" line="14"/>
182 <source>Room Window</source>
183 <translation>Вікно кімнати</translation>
184 </message>
185 <message>
186 <location filename="../../src/yuzu/multiplayer/client_room.ui" line="27"/>
187 <source>Room Description</source>
188 <translation>Опис кімнати</translation>
189 </message>
190 <message>
191 <location filename="../../src/yuzu/multiplayer/client_room.ui" line="47"/>
192 <source>Moderation...</source>
193 <translation>Модерація...</translation>
194 </message>
195 <message>
196 <location filename="../../src/yuzu/multiplayer/client_room.ui" line="57"/>
197 <source>Leave Room</source>
198 <translation>Залишити кімнату</translation>
199 </message>
200</context>
201<context>
202 <name>ClientRoomWindow</name>
203 <message>
204 <location filename="../../src/yuzu/multiplayer/client_room.cpp" line="78"/>
205 <source>Connected</source>
206 <translation>З&apos;єднано</translation>
207 </message>
208 <message>
209 <location filename="../../src/yuzu/multiplayer/client_room.cpp" line="87"/>
210 <source>Disconnected</source>
211 <translation>Роз&apos;єднано</translation>
212 </message>
213 <message>
214 <location filename="../../src/yuzu/multiplayer/client_room.cpp" line="100"/>
215 <source>%1 - %2 (%3/%4 members) - connected</source>
216 <translation>%1 - %2 (%3/%4 члени) - з&apos;єднано</translation>
217 </message>
218</context>
219<context>
220 <name>CompatDB</name>
221 <message>
222 <location filename="../../src/yuzu/compatdb.ui" line="20"/>
223 <source>Report Compatibility</source>
224 <translation>Повідомити про сумісність</translation>
225 </message>
226 <message>
227 <location filename="../../src/yuzu/compatdb.ui" line="27"/>
228 <location filename="../../src/yuzu/compatdb.ui" line="63"/>
229 <source>Report Game Compatibility</source>
230 <translation>Повідомити про сумісність гри</translation>
231 </message>
232 <message>
233 <location filename="../../src/yuzu/compatdb.ui" line="36"/>
234 <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;Should you choose to submit a test case to the &lt;/span&gt;&lt;a href=&quot;https://yuzu-emu.org/game/&quot;&gt;&lt;span style=&quot; font-size:10pt; text-decoration: underline; color:#0000ff;&quot;&gt;yuzu Compatibility List&lt;/span&gt;&lt;/a&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;, The following information will be collected and displayed on the site:&lt;/span&gt;&lt;/p&gt;&lt;ul style=&quot;margin-top: 0px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; -qt-list-indent: 1;&quot;&gt;&lt;li style=&quot; margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;Hardware Information (CPU / GPU / Operating System)&lt;/li&gt;&lt;li style=&quot; margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;Which version of yuzu you are running&lt;/li&gt;&lt;li style=&quot; margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;The connected yuzu account&lt;/li&gt;&lt;/ul&gt;&lt;/body&gt;&lt;/html&gt;</source>
235 <translation>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;Якщо ви бажаєте надіслати звіт до &lt;/span&gt;&lt;a href=&quot;https://yuzu-emu.org/game/&quot;&gt;&lt;span style=&quot; font-size:10pt; text-decoration: underline; color:#0000ff;&quot;&gt;списку сумісності yuzu&lt;/span&gt;&lt;/a&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;, наступна інформація буде зібрана та відображена на сайті:&lt;/span&gt;&lt;/p&gt;&lt;ul style=&quot;margin-top: 0px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; -qt-list-indent: 1;&quot;&gt;&lt;li style=&quot; margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;Інформація про залізо (ЦП / ГП / Операційна система)&lt;/li&gt;&lt;li style=&quot; margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;Версія yuzu&lt;/li&gt;&lt;li style=&quot; margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;Підключений акаунт yuzu&lt;/li&gt;&lt;/ul&gt;&lt;/body&gt;&lt;/html&gt;</translation>
236 </message>
237 <message>
238 <location filename="../../src/yuzu/compatdb.ui" line="72"/>
239 <source>Perfect</source>
240 <translation>Ідеально</translation>
241 </message>
242 <message>
243 <location filename="../../src/yuzu/compatdb.ui" line="79"/>
244 <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Game functions flawlessly with no audio or graphical glitches.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
245 <translation>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Гра працює ідеально, без звукових чи графічних артефактів.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</translation>
246 </message>
247 <message>
248 <location filename="../../src/yuzu/compatdb.ui" line="89"/>
249 <source>Great</source>
250 <translation>Чудово</translation>
251 </message>
252 <message>
253 <location filename="../../src/yuzu/compatdb.ui" line="96"/>
254 <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Game functions with minor graphical or audio glitches and is playable from start to finish. May require some workarounds.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
255 <translation>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Гра працює з невеликими графічними або звуковими артефактами та може бути пройдена від початку до кінця. Можуть знадобитися обхідні шляхи.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</translation>
256 </message>
257 <message>
258 <location filename="../../src/yuzu/compatdb.ui" line="106"/>
259 <source>Okay</source>
260 <translation>Добре</translation>
261 </message>
262 <message>
263 <location filename="../../src/yuzu/compatdb.ui" line="113"/>
264 <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Game functions with major graphical or audio glitches, but game is playable from start to finish with workarounds.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
265 <translation>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Гра працює зі суттєвими графічними або звуковими артефактами, але з обхідними шляхами може бути пройдена.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</translation>
266 </message>
267 <message>
268 <location filename="../../src/yuzu/compatdb.ui" line="123"/>
269 <source>Bad</source>
270 <translation>Погано</translation>
271 </message>
272 <message>
273 <location filename="../../src/yuzu/compatdb.ui" line="130"/>
274 <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Game functions, but with major graphical or audio glitches. Unable to progress in specific areas due to glitches even with workarounds.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
275 <translation>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Гра працює, але з суттєвими графічними чи звуковими артефактами. У деяких місцях неможливо пройти навіть із обхідними шляхами.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</translation>
276 </message>
277 <message>
278 <location filename="../../src/yuzu/compatdb.ui" line="140"/>
279 <source>Intro/Menu</source>
280 <translation>Вступ/Меню</translation>
281 </message>
282 <message>
283 <location filename="../../src/yuzu/compatdb.ui" line="147"/>
284 <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Game is completely unplayable due to major graphical or audio glitches. Unable to progress past the Start Screen.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
285 <translation>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;У гру неможливо грати через серйозні графічні або звукові артефакти. Неможливо просунутися далі за стартове меню.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</translation>
286 </message>
287 <message>
288 <location filename="../../src/yuzu/compatdb.ui" line="157"/>
289 <source>Won&apos;t Boot</source>
290 <translation>Не запускається</translation>
291 </message>
292 <message>
293 <location filename="../../src/yuzu/compatdb.ui" line="170"/>
294 <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;The game crashes when attempting to startup.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
295 <translation>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Гра вилітає при спробі запуску.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</translation>
296 </message>
297 <message>
298 <location filename="../../src/yuzu/compatdb.ui" line="182"/>
299 <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Independent of speed or performance, how well does this game play from start to finish on this version of yuzu?&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
300 <translation>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Незалежно від швидкості або продуктивності, наскільки добре ця гра працює від початку до кінця у поточній версії yuzu?&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</translation>
301 </message>
302 <message>
303 <location filename="../../src/yuzu/compatdb.ui" line="206"/>
304 <source>Thank you for your submission!</source>
305 <translation>Дякуємо за ваш звіт!</translation>
306 </message>
307 <message>
308 <location filename="../../src/yuzu/compatdb.cpp" line="58"/>
309 <source>Submitting</source>
310 <translation>Надсилання</translation>
311 </message>
312 <message>
313 <location filename="../../src/yuzu/compatdb.cpp" line="71"/>
314 <source>Communication error</source>
315 <translation>Помилка з&apos;єднання</translation>
316 </message>
317 <message>
318 <location filename="../../src/yuzu/compatdb.cpp" line="72"/>
319 <source>An error occurred while sending the Testcase</source>
320 <translation>Сталася помилка під час надсилання звіту</translation>
321 </message>
322 <message>
323 <location filename="../../src/yuzu/compatdb.cpp" line="74"/>
324 <source>Next</source>
325 <translation>Далі</translation>
326 </message>
327</context>
328<context>
329 <name>ConfigureAudio</name>
330 <message>
331 <location filename="../../src/yuzu/configuration/configure_audio.ui" line="14"/>
332 <location filename="../../src/yuzu/configuration/configure_audio.ui" line="20"/>
333 <source>Audio</source>
334 <translation>Аудіо</translation>
335 </message>
336 <message>
337 <location filename="../../src/yuzu/configuration/configure_audio.ui" line="28"/>
338 <source>Output Engine:</source>
339 <translation>Рушій виводу:</translation>
340 </message>
341 <message>
342 <location filename="../../src/yuzu/configuration/configure_audio.ui" line="42"/>
343 <source>Output Device</source>
344 <translation>Пристрій виводу</translation>
345 </message>
346 <message>
347 <location filename="../../src/yuzu/configuration/configure_audio.ui" line="56"/>
348 <source>Input Device</source>
349 <translation>Пристрій вводу</translation>
350 </message>
351 <message>
352 <location filename="../../src/yuzu/configuration/configure_audio.ui" line="84"/>
353 <source>Use global volume</source>
354 <translation>Використовувати загальну гучність</translation>
355 </message>
356 <message>
357 <location filename="../../src/yuzu/configuration/configure_audio.ui" line="89"/>
358 <source>Set volume:</source>
359 <translation>Встановити гучність:</translation>
360 </message>
361 <message>
362 <location filename="../../src/yuzu/configuration/configure_audio.ui" line="97"/>
363 <source>Volume:</source>
364 <translation>Гучність</translation>
365 </message>
366 <message>
367 <location filename="../../src/yuzu/configuration/configure_audio.ui" line="142"/>
368 <source>0 %</source>
369 <translation>0 %</translation>
370 </message>
371 <message>
372 <location filename="../../src/yuzu/configuration/configure_audio.cpp" line="108"/>
373 <source>%1%</source>
374 <comment>Volume percentage (e.g. 50%)</comment>
375 <translation>%1%</translation>
376 </message>
377</context>
378<context>
379 <name>ConfigureCamera</name>
380 <message>
381 <location filename="../../src/yuzu/configuration/configure_camera.ui" line="14"/>
382 <source>Configure Infrared Camera</source>
383 <translation>Налаштування інфрачервоної камери</translation>
384 </message>
385 <message>
386 <location filename="../../src/yuzu/configuration/configure_camera.ui" line="26"/>
387 <source>Select where the image of the emulated camera comes from. It may be a virtual camera or a real camera.</source>
388 <translation>Виберіть, звідки береться зображення емульованої камери. Це може бути віртуальна або реальна камера.</translation>
389 </message>
390 <message>
391 <location filename="../../src/yuzu/configuration/configure_camera.ui" line="52"/>
392 <source>Camera Image Source:</source>
393 <translation>Джерело зображення камери:</translation>
394 </message>
395 <message>
396 <location filename="../../src/yuzu/configuration/configure_camera.ui" line="71"/>
397 <source>Input device:</source>
398 <translation>Пристрій вводу:</translation>
399 </message>
400 <message>
401 <location filename="../../src/yuzu/configuration/configure_camera.ui" line="96"/>
402 <source>Preview</source>
403 <translation>Попередній перегляд</translation>
404 </message>
405 <message>
406 <location filename="../../src/yuzu/configuration/configure_camera.ui" line="108"/>
407 <source>Resolution: 320*240</source>
408 <translation>Роздільна здатність: 320*240</translation>
409 </message>
410 <message>
411 <location filename="../../src/yuzu/configuration/configure_camera.ui" line="115"/>
412 <source>Click to preview</source>
413 <translation>Натисніть для попереднього перегляду</translation>
414 </message>
415 <message>
416 <location filename="../../src/yuzu/configuration/configure_camera.ui" line="140"/>
417 <source>Restore Defaults</source>
418 <translation>Значення за замовчуванням</translation>
419 </message>
420 <message>
421 <location filename="../../src/yuzu/configuration/configure_camera.cpp" line="135"/>
422 <source>Auto</source>
423 <translation>Авто</translation>
424 </message>
425</context>
426<context>
427 <name>ConfigureCpu</name>
428 <message>
429 <location filename="../../src/yuzu/configuration/configure_cpu.ui" line="14"/>
430 <source>Form</source>
431 <translation>Форма</translation>
432 </message>
433 <message>
434 <location filename="../../src/yuzu/configuration/configure_cpu.ui" line="17"/>
435 <source>CPU</source>
436 <translation>ЦП</translation>
437 </message>
438 <message>
439 <location filename="../../src/yuzu/configuration/configure_cpu.ui" line="25"/>
440 <source>General</source>
441 <translation>Загальні</translation>
442 </message>
443 <message>
444 <location filename="../../src/yuzu/configuration/configure_cpu.ui" line="34"/>
445 <source>Accuracy:</source>
446 <translation>Точність:</translation>
447 </message>
448 <message>
449 <location filename="../../src/yuzu/configuration/configure_cpu.ui" line="42"/>
450 <source>Auto</source>
451 <translation>Авто</translation>
452 </message>
453 <message>
454 <location filename="../../src/yuzu/configuration/configure_cpu.ui" line="47"/>
455 <source>Accurate</source>
456 <translation>Точно</translation>
457 </message>
458 <message>
459 <location filename="../../src/yuzu/configuration/configure_cpu.ui" line="52"/>
460 <source>Unsafe</source>
461 <translation>Небезпечно</translation>
462 </message>
463 <message>
464 <location filename="../../src/yuzu/configuration/configure_cpu.ui" line="57"/>
465 <source>Paranoid (disables most optimizations)</source>
466 <translation>Параноїк (відключає більшість оптимізацій)</translation>
467 </message>
468 <message>
469 <location filename="../../src/yuzu/configuration/configure_cpu.ui" line="68"/>
470 <source>We recommend setting accuracy to &quot;Auto&quot;.</source>
471 <translation>Ми рекомендуємо встановити точність на &quot;Авто&quot;.</translation>
472 </message>
473 <message>
474 <location filename="../../src/yuzu/configuration/configure_cpu.ui" line="85"/>
475 <source>Unsafe CPU Optimization Settings</source>
476 <translation>Небезпечні налаштування оптимізації ЦП</translation>
477 </message>
478 <message>
479 <location filename="../../src/yuzu/configuration/configure_cpu.ui" line="91"/>
480 <source>These settings reduce accuracy for speed.</source>
481 <translation>Ці налаштування зменшують точність заради швидкості. </translation>
482 </message>
483 <message>
484 <location filename="../../src/yuzu/configuration/configure_cpu.ui" line="101"/>
485 <source>
486 &lt;div&gt;This option improves speed by reducing accuracy of fused-multiply-add instructions on CPUs without native FMA support.&lt;/div&gt;
487 </source>
488 <translation type="unfinished"/>
489 </message>
490 <message>
491 <location filename="../../src/yuzu/configuration/configure_cpu.ui" line="106"/>
492 <source>Unfuse FMA (improve performance on CPUs without FMA)</source>
493 <translation>Не використовувати FMA (покращує продуктивність на ЦП без FMA)</translation>
494 </message>
495 <message>
496 <location filename="../../src/yuzu/configuration/configure_cpu.ui" line="113"/>
497 <source>
498 &lt;div&gt;This option improves the speed of some approximate floating-point functions by using less accurate native approximations.&lt;/div&gt;
499 </source>
500 <translation type="unfinished"/>
501 </message>
502 <message>
503 <location filename="../../src/yuzu/configuration/configure_cpu.ui" line="118"/>
504 <source>Faster FRSQRTE and FRECPE</source>
505 <translation>Прискорені FRSQRTE та FRECPE</translation>
506 </message>
507 <message>
508 <location filename="../../src/yuzu/configuration/configure_cpu.ui" line="125"/>
509 <source>
510 &lt;div&gt;This option improves the speed of 32 bits ASIMD floating-point functions by running with incorrect rounding modes.&lt;/div&gt;
511 </source>
512 <translation type="unfinished"/>
513 </message>
514 <message>
515 <location filename="../../src/yuzu/configuration/configure_cpu.ui" line="130"/>
516 <source>Faster ASIMD instructions (32 bits only)</source>
517 <translation>Швидші інструкції ASIMD (лише 32 біт)</translation>
518 </message>
519 <message>
520 <location filename="../../src/yuzu/configuration/configure_cpu.ui" line="137"/>
521 <source>
522 &lt;div&gt;This option improves speed by removing NaN checking. Please note this also reduces accuracy of certain floating-point instructions.&lt;/div&gt;
523 </source>
524 <translation type="unfinished"/>
525 </message>
526 <message>
527 <location filename="../../src/yuzu/configuration/configure_cpu.ui" line="142"/>
528 <source>Inaccurate NaN handling</source>
529 <translation>Неправильна обробка NaN</translation>
530 </message>
531 <message>
532 <location filename="../../src/yuzu/configuration/configure_cpu.ui" line="149"/>
533 <source>
534 &lt;div&gt;This option improves speed by eliminating a safety check before every memory read/write in guest. Disabling it may allow a game to read/write the emulator's memory.&lt;/div&gt;
535 </source>
536 <translation type="unfinished"/>
537 </message>
538 <message>
539 <location filename="../../src/yuzu/configuration/configure_cpu.ui" line="154"/>
540 <source>Disable address space checks</source>
541 <translation type="unfinished"/>
542 </message>
543 <message>
544 <location filename="../../src/yuzu/configuration/configure_cpu.ui" line="161"/>
545 <source>
546 &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;
547 </source>
548 <translation type="unfinished"/>
549 </message>
550 <message>
551 <location filename="../../src/yuzu/configuration/configure_cpu.ui" line="166"/>
552 <source>Ignore global monitor</source>
553 <translation type="unfinished"/>
554 </message>
555 <message>
556 <location filename="../../src/yuzu/configuration/configure_cpu.ui" line="191"/>
557 <source>CPU settings are available only when game is not running.</source>
558 <translation>Налаштування ЦП недоступні, поки запущена гра.</translation>
559 </message>
560</context>
561<context>
562 <name>ConfigureCpuDebug</name>
563 <message>
564 <location filename="../../src/yuzu/configuration/configure_cpu_debug.ui" line="14"/>
565 <source>Form</source>
566 <translation>Форма</translation>
567 </message>
568 <message>
569 <location filename="../../src/yuzu/configuration/configure_cpu_debug.ui" line="17"/>
570 <source>CPU</source>
571 <translation>ЦП</translation>
572 </message>
573 <message>
574 <location filename="../../src/yuzu/configuration/configure_cpu_debug.ui" line="25"/>
575 <source>Toggle CPU Optimizations</source>
576 <translation>Увімкнути оптимізації ЦП</translation>
577 </message>
578 <message>
579 <location filename="../../src/yuzu/configuration/configure_cpu_debug.ui" line="31"/>
580 <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-weight:600;&quot;&gt;For debugging only.&lt;/span&gt;&lt;br/&gt;If you&apos;re not sure what these do, keep all of these enabled. &lt;br/&gt;These settings, when disabled, only take effect when CPU Debugging is enabled. &lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
581 <translation>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-weight:600;&quot;&gt;Тільки для налагодження.&lt;/span&gt;&lt;br/&gt;Якщо ви не впевнені в тому, що вони роблять, залиште всі ці параметри увімкненими. &lt;br/&gt;Коли їх вимкнено, ці параметри набувають чинності лише за увімкненого налагодження ЦП. &lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</translation>
582 </message>
583 <message>
584 <location filename="../../src/yuzu/configuration/configure_cpu_debug.ui" line="41"/>
585 <source>
586 &lt;div style=&quot;white-space: nowrap&quot;&gt;This optimization speeds up memory accesses by the guest program.&lt;/div&gt;
587 &lt;div style=&quot;white-space: nowrap&quot;&gt;Enabling it inlines accesses to PageTable::pointers into emitted code.&lt;/div&gt;
588 &lt;div style=&quot;white-space: nowrap&quot;&gt;Disabling this forces all memory accesses to go through the Memory::Read/Memory::Write functions.&lt;/div&gt;
589 </source>
590 <translation type="unfinished"/>
591 </message>
592 <message>
593 <location filename="../../src/yuzu/configuration/configure_cpu_debug.ui" line="48"/>
594 <source>Enable inline page tables</source>
595 <translation type="unfinished"/>
596 </message>
597 <message>
598 <location filename="../../src/yuzu/configuration/configure_cpu_debug.ui" line="55"/>
599 <source>
600 &lt;div&gt;This optimization avoids dispatcher lookups by allowing emitted basic blocks to jump directly to other basic blocks if the destination PC is static.&lt;/div&gt;
601 </source>
602 <translation type="unfinished"/>
603 </message>
604 <message>
605 <location filename="../../src/yuzu/configuration/configure_cpu_debug.ui" line="60"/>
606 <source>Enable block linking</source>
607 <translation type="unfinished"/>
608 </message>
609 <message>
610 <location filename="../../src/yuzu/configuration/configure_cpu_debug.ui" line="67"/>
611 <source>
612 &lt;div&gt;This optimization avoids dispatcher lookups by keeping track potential return addresses of BL instructions. This approximates what happens with a return stack buffer on a real CPU.&lt;/div&gt;
613 </source>
614 <translation type="unfinished"/>
615 </message>
616 <message>
617 <location filename="../../src/yuzu/configuration/configure_cpu_debug.ui" line="72"/>
618 <source>Enable return stack buffer</source>
619 <translation type="unfinished"/>
620 </message>
621 <message>
622 <location filename="../../src/yuzu/configuration/configure_cpu_debug.ui" line="79"/>
623 <source>
624 &lt;div&gt;Enable a two-tiered dispatch system. A faster dispatcher written in assembly has a small MRU cache of jump destinations is used first. If that fails, dispatch falls back to the slower C++ dispatcher.&lt;/div&gt;
625 </source>
626 <translation type="unfinished"/>
627 </message>
628 <message>
629 <location filename="../../src/yuzu/configuration/configure_cpu_debug.ui" line="84"/>
630 <source>Enable fast dispatcher</source>
631 <translation type="unfinished"/>
632 </message>
633 <message>
634 <location filename="../../src/yuzu/configuration/configure_cpu_debug.ui" line="91"/>
635 <source>
636 &lt;div&gt;Enables an IR optimization that reduces unnecessary accesses to the CPU context structure.&lt;/div&gt;
637 </source>
638 <translation type="unfinished"/>
639 </message>
640 <message>
641 <location filename="../../src/yuzu/configuration/configure_cpu_debug.ui" line="96"/>
642 <source>Enable context elimination</source>
643 <translation type="unfinished"/>
644 </message>
645 <message>
646 <location filename="../../src/yuzu/configuration/configure_cpu_debug.ui" line="103"/>
647 <source>
648 &lt;div&gt;Enables IR optimizations that involve constant propagation.&lt;/div&gt;
649 </source>
650 <translation type="unfinished"/>
651 </message>
652 <message>
653 <location filename="../../src/yuzu/configuration/configure_cpu_debug.ui" line="108"/>
654 <source>Enable constant propagation</source>
655 <translation type="unfinished"/>
656 </message>
657 <message>
658 <location filename="../../src/yuzu/configuration/configure_cpu_debug.ui" line="115"/>
659 <source>
660 &lt;div&gt;Enables miscellaneous IR optimizations.&lt;/div&gt;
661 </source>
662 <translation type="unfinished"/>
663 </message>
664 <message>
665 <location filename="../../src/yuzu/configuration/configure_cpu_debug.ui" line="120"/>
666 <source>Enable miscellaneous optimizations</source>
667 <translation>Увімкнути різні оптимізації</translation>
668 </message>
669 <message>
670 <location filename="../../src/yuzu/configuration/configure_cpu_debug.ui" line="127"/>
671 <source>
672 &lt;div style=&quot;white-space: nowrap&quot;&gt;When enabled, a misalignment is only triggered when an access crosses a page boundary.&lt;/div&gt;
673 &lt;div style=&quot;white-space: nowrap&quot;&gt;When disabled, a misalignment is triggered on all misaligned accesses.&lt;/div&gt;
674 </source>
675 <translation type="unfinished"/>
676 </message>
677 <message>
678 <location filename="../../src/yuzu/configuration/configure_cpu_debug.ui" line="133"/>
679 <source>Enable misalignment check reduction</source>
680 <translation type="unfinished"/>
681 </message>
682 <message>
683 <location filename="../../src/yuzu/configuration/configure_cpu_debug.ui" line="140"/>
684 <source>
685 &lt;div style=&quot;white-space: nowrap&quot;&gt;This optimization speeds up memory accesses by the guest program.&lt;/div&gt;
686 &lt;div style=&quot;white-space: nowrap&quot;&gt;Enabling it causes guest memory reads/writes to be done directly into memory and make use of Host's MMU.&lt;/div&gt;
687 &lt;div style=&quot;white-space: nowrap&quot;&gt;Disabling this forces all memory accesses to use Software MMU Emulation.&lt;/div&gt;
688 </source>
689 <translation type="unfinished"/>
690 </message>
691 <message>
692 <location filename="../../src/yuzu/configuration/configure_cpu_debug.ui" line="147"/>
693 <source>Enable Host MMU Emulation (general memory instructions)</source>
694 <translation type="unfinished"/>
695 </message>
696 <message>
697 <location filename="../../src/yuzu/configuration/configure_cpu_debug.ui" line="154"/>
698 <source>
699 &lt;div style=&quot;white-space: nowrap&quot;&gt;This optimization speeds up exclusive memory accesses by the guest program.&lt;/div&gt;
700 &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;
701 &lt;div style=&quot;white-space: nowrap&quot;&gt;Disabling this forces all exclusive memory accesses to use Software MMU Emulation.&lt;/div&gt;
702 </source>
703 <translation type="unfinished"/>
704 </message>
705 <message>
706 <location filename="../../src/yuzu/configuration/configure_cpu_debug.ui" line="161"/>
707 <source>Enable Host MMU Emulation (exclusive memory instructions)</source>
708 <translation type="unfinished"/>
709 </message>
710 <message>
711 <location filename="../../src/yuzu/configuration/configure_cpu_debug.ui" line="168"/>
712 <source>
713 &lt;div style=&quot;white-space: nowrap&quot;&gt;This optimization speeds up exclusive memory accesses by the guest program.&lt;/div&gt;
714 &lt;div style=&quot;white-space: nowrap&quot;&gt;Enabling it reduces the overhead of fastmem failure of exclusive memory accesses.&lt;/div&gt;
715 </source>
716 <translation type="unfinished"/>
717 </message>
718 <message>
719 <location filename="../../src/yuzu/configuration/configure_cpu_debug.ui" line="174"/>
720 <source>Enable recompilation of exclusive memory instructions</source>
721 <translation type="unfinished"/>
722 </message>
723 <message>
724 <location filename="../../src/yuzu/configuration/configure_cpu_debug.ui" line="199"/>
725 <source>CPU settings are available only when game is not running.</source>
726 <translation>Налаштування ЦП доступні тільки тоді, коли гру не запущено.</translation>
727 </message>
728</context>
729<context>
730 <name>ConfigureDebug</name>
731 <message>
732 <location filename="../../src/yuzu/configuration/configure_debug.ui" line="15"/>
733 <source>Debugger</source>
734 <translation>Налагоджувач</translation>
735 </message>
736 <message>
737 <location filename="../../src/yuzu/configuration/configure_debug.ui" line="23"/>
738 <source>Enable GDB Stub</source>
739 <translation type="unfinished"/>
740 </message>
741 <message>
742 <location filename="../../src/yuzu/configuration/configure_debug.ui" line="43"/>
743 <source>Port:</source>
744 <translation>Порт:</translation>
745 </message>
746 <message>
747 <location filename="../../src/yuzu/configuration/configure_debug.ui" line="67"/>
748 <source>Logging</source>
749 <translation>Журналювання</translation>
750 </message>
751 <message>
752 <location filename="../../src/yuzu/configuration/configure_debug.ui" line="75"/>
753 <source>Global Log Filter</source>
754 <translation>Глобальний фільтр журналів</translation>
755 </message>
756 <message>
757 <location filename="../../src/yuzu/configuration/configure_debug.ui" line="87"/>
758 <source>Show Log in Console</source>
759 <translation>Показувати журнал у консолі</translation>
760 </message>
761 <message>
762 <location filename="../../src/yuzu/configuration/configure_debug.ui" line="94"/>
763 <source>Open Log Location</source>
764 <translation>Відкрити папку для журналів</translation>
765 </message>
766 <message>
767 <location filename="../../src/yuzu/configuration/configure_debug.ui" line="104"/>
768 <source>When checked, the max size of the log increases from 100 MB to 1 GB</source>
769 <translation>Якщо увімкнено, максимальний розмір журналу збільшується зі 100 МБ до 1 ГБ</translation>
770 </message>
771 <message>
772 <location filename="../../src/yuzu/configuration/configure_debug.ui" line="107"/>
773 <source>Enable Extended Logging**</source>
774 <translation>Увімкнути розширене ведення журналу**</translation>
775 </message>
776 <message>
777 <location filename="../../src/yuzu/configuration/configure_debug.ui" line="117"/>
778 <source>Homebrew</source>
779 <translation>Homebrew</translation>
780 </message>
781 <message>
782 <location filename="../../src/yuzu/configuration/configure_debug.ui" line="125"/>
783 <source>Arguments String</source>
784 <translation>Рядок аргументів</translation>
785 </message>
786 <message>
787 <location filename="../../src/yuzu/configuration/configure_debug.ui" line="140"/>
788 <source>Graphics</source>
789 <translation>Графіка</translation>
790 </message>
791 <message>
792 <location filename="../../src/yuzu/configuration/configure_debug.ui" line="149"/>
793 <source>When checked, the graphics API enters a slower debugging mode</source>
794 <translation>Якщо увімкнено, графічний API переходить у повільніший режим налагодження</translation>
795 </message>
796 <message>
797 <location filename="../../src/yuzu/configuration/configure_debug.ui" line="152"/>
798 <source>Enable Graphics Debugging</source>
799 <translation>Увімкнути налагодження графіки</translation>
800 </message>
801 <message>
802 <location filename="../../src/yuzu/configuration/configure_debug.ui" line="159"/>
803 <source>When checked, it enables Nsight Aftermath crash dumps</source>
804 <translation type="unfinished"/>
805 </message>
806 <message>
807 <location filename="../../src/yuzu/configuration/configure_debug.ui" line="162"/>
808 <source>Enable Nsight Aftermath</source>
809 <translation type="unfinished"/>
810 </message>
811 <message>
812 <location filename="../../src/yuzu/configuration/configure_debug.ui" line="172"/>
813 <source>When checked, it will dump all the original assembler shaders from the disk shader cache or game as found</source>
814 <translation>Якщо ввімкнено, буде дампити всі оригінальні шейдери асемблера з кешу шейдерів на диску або гри як знайдені</translation>
815 </message>
816 <message>
817 <location filename="../../src/yuzu/configuration/configure_debug.ui" line="175"/>
818 <source>Dump Game Shaders</source>
819 <translation>Дамп ігрових шейдерів</translation>
820 </message>
821 <message>
822 <location filename="../../src/yuzu/configuration/configure_debug.ui" line="185"/>
823 <source>When checked, it will dump all the macro programs of the GPU</source>
824 <translation type="unfinished"/>
825 </message>
826 <message>
827 <location filename="../../src/yuzu/configuration/configure_debug.ui" line="188"/>
828 <source>Dump Maxwell Macros</source>
829 <translation type="unfinished"/>
830 </message>
831 <message>
832 <location filename="../../src/yuzu/configuration/configure_debug.ui" line="198"/>
833 <source>When checked, it disables the macro Just In Time compiler. Enabling this makes games run slower</source>
834 <translation type="unfinished"/>
835 </message>
836 <message>
837 <location filename="../../src/yuzu/configuration/configure_debug.ui" line="201"/>
838 <source>Disable Macro JIT</source>
839 <translation type="unfinished"/>
840 </message>
841 <message>
842 <location filename="../../src/yuzu/configuration/configure_debug.ui" line="208"/>
843 <source>When checked, yuzu will log statistics about the compiled pipeline cache</source>
844 <translation>Якщо увімкнено, yuzu записуватиме статистику про скомпільований кеш конвеєра</translation>
845 </message>
846 <message>
847 <location filename="../../src/yuzu/configuration/configure_debug.ui" line="211"/>
848 <source>Enable Shader Feedback</source>
849 <translation>Увімкнути зворотний зв&apos;язок про шейдери</translation>
850 </message>
851 <message>
852 <location filename="../../src/yuzu/configuration/configure_debug.ui" line="218"/>
853 <source>When checked, it executes shaders without loop logic changes</source>
854 <translation type="unfinished"/>
855 </message>
856 <message>
857 <location filename="../../src/yuzu/configuration/configure_debug.ui" line="221"/>
858 <source>Disable Loop safety checks</source>
859 <translation type="unfinished"/>
860 </message>
861 <message>
862 <location filename="../../src/yuzu/configuration/configure_debug.ui" line="231"/>
863 <source>Debugging</source>
864 <translation>Налагодження</translation>
865 </message>
866 <message>
867 <location filename="../../src/yuzu/configuration/configure_debug.ui" line="237"/>
868 <source>Enable Verbose Reporting Services**</source>
869 <translation type="unfinished"/>
870 </message>
871 <message>
872 <location filename="../../src/yuzu/configuration/configure_debug.ui" line="244"/>
873 <source>Enable FS Access Log</source>
874 <translation type="unfinished"/>
875 </message>
876 <message>
877 <location filename="../../src/yuzu/configuration/configure_debug.ui" line="251"/>
878 <source>Enable this to output the latest generated audio command list to the console. Only affects games using the audio renderer.</source>
879 <translation type="unfinished"/>
880 </message>
881 <message>
882 <location filename="../../src/yuzu/configuration/configure_debug.ui" line="254"/>
883 <source>Dump Audio Commands To Console**</source>
884 <translation type="unfinished"/>
885 </message>
886 <message>
887 <location filename="../../src/yuzu/configuration/configure_debug.ui" line="261"/>
888 <source>Create Minidump After Crash</source>
889 <translation type="unfinished"/>
890 </message>
891 <message>
892 <location filename="../../src/yuzu/configuration/configure_debug.ui" line="271"/>
893 <source>Advanced</source>
894 <translation>Розширені</translation>
895 </message>
896 <message>
897 <location filename="../../src/yuzu/configuration/configure_debug.ui" line="277"/>
898 <source>Kiosk (Quest) Mode</source>
899 <translation>Режим кіоску (Квест)</translation>
900 </message>
901 <message>
902 <location filename="../../src/yuzu/configuration/configure_debug.ui" line="284"/>
903 <source>Enable CPU Debugging</source>
904 <translation>Увімкнути налагодження ЦП</translation>
905 </message>
906 <message>
907 <location filename="../../src/yuzu/configuration/configure_debug.ui" line="291"/>
908 <source>Enable Debug Asserts</source>
909 <translation type="unfinished"/>
910 </message>
911 <message>
912 <location filename="../../src/yuzu/configuration/configure_debug.ui" line="298"/>
913 <source>Enable Auto-Stub**</source>
914 <translation type="unfinished"/>
915 </message>
916 <message>
917 <location filename="../../src/yuzu/configuration/configure_debug.ui" line="305"/>
918 <source>Enable All Controller Types</source>
919 <translation>Увімкнути всі типи контролерів</translation>
920 </message>
921 <message>
922 <location filename="../../src/yuzu/configuration/configure_debug.ui" line="312"/>
923 <source>Disable Web Applet</source>
924 <translation>Вимкнути веб-аплет</translation>
925 </message>
926 <message>
927 <location filename="../../src/yuzu/configuration/configure_debug.ui" line="319"/>
928 <source>Enables yuzu to check for a working Vulkan environment when the program starts up. Disable this if this is causing issues with external programs seeing yuzu.</source>
929 <translation>Дозволяє yuzu перевіряти наявність робочого середовища Vulkan під час запуску програми. Вимкніть цю опцію, якщо це викликає проблеми з тим, що зовнішні програми бачать yuzu.</translation>
930 </message>
931 <message>
932 <location filename="../../src/yuzu/configuration/configure_debug.ui" line="322"/>
933 <source>Perform Startup Vulkan Check</source>
934 <translation>Виконувати перевірку Vulkan під час запуску</translation>
935 </message>
936 <message>
937 <location filename="../../src/yuzu/configuration/configure_debug.ui" line="337"/>
938 <source>**This will be reset automatically when yuzu closes.</source>
939 <translation>**Це буде автоматично скинуто після закриття yuzu.</translation>
940 </message>
941 <message>
942 <location filename="../../src/yuzu/configuration/configure_debug.cpp" line="35"/>
943 <source>Restart Required</source>
944 <translation>Потрібен перезапуск</translation>
945 </message>
946 <message>
947 <location filename="../../src/yuzu/configuration/configure_debug.cpp" line="36"/>
948 <source>yuzu is required to restart in order to apply this setting.</source>
949 <translation>yuzu потрібно перезапустити, щоб застосувати це налаштування.</translation>
950 </message>
951 <message>
952 <location filename="../../src/yuzu/configuration/configure_debug.cpp" line="86"/>
953 <source>Web applet not compiled</source>
954 <translation>Веб-аплет не скомпільовано</translation>
955 </message>
956 <message>
957 <location filename="../../src/yuzu/configuration/configure_debug.cpp" line="93"/>
958 <source>MiniDump creation not compiled</source>
959 <translation type="unfinished"/>
960 </message>
961</context>
962<context>
963 <name>ConfigureDebugController</name>
964 <message>
965 <location filename="../../src/yuzu/configuration/configure_debug_controller.ui" line="14"/>
966 <source>Configure Debug Controller</source>
967 <translation>Налаштування налагоджувального контролера</translation>
968 </message>
969 <message>
970 <location filename="../../src/yuzu/configuration/configure_debug_controller.ui" line="40"/>
971 <source>Clear</source>
972 <translation>Очистити</translation>
973 </message>
974 <message>
975 <location filename="../../src/yuzu/configuration/configure_debug_controller.ui" line="47"/>
976 <source>Defaults</source>
977 <translation>За замовчуванням</translation>
978 </message>
979</context>
980<context>
981 <name>ConfigureDebugTab</name>
982 <message>
983 <location filename="../../src/yuzu/configuration/configure_debug_tab.ui" line="14"/>
984 <source>Form</source>
985 <translation>Форма</translation>
986 </message>
987 <message>
988 <location filename="../../src/yuzu/configuration/configure_debug_tab.ui" line="17"/>
989 <location filename="../../src/yuzu/configuration/configure_debug_tab.cpp" line="16"/>
990 <source>Debug</source>
991 <translation>Налагодження</translation>
992 </message>
993 <message>
994 <location filename="../../src/yuzu/configuration/configure_debug_tab.cpp" line="17"/>
995 <source>CPU</source>
996 <translation>ЦП</translation>
997 </message>
998</context>
999<context>
1000 <name>ConfigureDialog</name>
1001 <message>
1002 <location filename="../../src/yuzu/configuration/configure.ui" line="20"/>
1003 <source>yuzu Configuration</source>
1004 <translation>Налаштування yuzu</translation>
1005 </message>
1006 <message>
1007 <location filename="../../src/yuzu/configuration/configure_dialog.cpp" line="52"/>
1008 <location filename="../../src/yuzu/configuration/configure_dialog.cpp" line="156"/>
1009 <source>Audio</source>
1010 <translation>Аудіо</translation>
1011 </message>
1012 <message>
1013 <location filename="../../src/yuzu/configuration/configure_dialog.cpp" line="53"/>
1014 <location filename="../../src/yuzu/configuration/configure_dialog.cpp" line="154"/>
1015 <source>CPU</source>
1016 <translation>ЦП</translation>
1017 </message>
1018 <message>
1019 <location filename="../../src/yuzu/configuration/configure_dialog.cpp" line="54"/>
1020 <source>Debug</source>
1021 <translation>Налагодження</translation>
1022 </message>
1023 <message>
1024 <location filename="../../src/yuzu/configuration/configure_dialog.cpp" line="55"/>
1025 <source>Filesystem</source>
1026 <translation>Файлова система</translation>
1027 </message>
1028 <message>
1029 <location filename="../../src/yuzu/configuration/configure_dialog.cpp" line="56"/>
1030 <location filename="../../src/yuzu/configuration/configure_dialog.cpp" line="150"/>
1031 <source>General</source>
1032 <translation>Загальні</translation>
1033 </message>
1034 <message>
1035 <location filename="../../src/yuzu/configuration/configure_dialog.cpp" line="57"/>
1036 <location filename="../../src/yuzu/configuration/configure_dialog.cpp" line="155"/>
1037 <source>Graphics</source>
1038 <translation>Графіка</translation>
1039 </message>
1040 <message>
1041 <location filename="../../src/yuzu/configuration/configure_dialog.cpp" line="58"/>
1042 <source>GraphicsAdvanced</source>
1043 <translation>ГрафікаРозширені</translation>
1044 </message>
1045 <message>
1046 <location filename="../../src/yuzu/configuration/configure_dialog.cpp" line="59"/>
1047 <source>Hotkeys</source>
1048 <translation>Гарячі клавіші</translation>
1049 </message>
1050 <message>
1051 <location filename="../../src/yuzu/configuration/configure_dialog.cpp" line="60"/>
1052 <location filename="../../src/yuzu/configuration/configure_dialog.cpp" line="157"/>
1053 <source>Controls</source>
1054 <translation>Керування</translation>
1055 </message>
1056 <message>
1057 <location filename="../../src/yuzu/configuration/configure_dialog.cpp" line="61"/>
1058 <source>Profiles</source>
1059 <translation>Профілі</translation>
1060 </message>
1061 <message>
1062 <location filename="../../src/yuzu/configuration/configure_dialog.cpp" line="62"/>
1063 <source>Network</source>
1064 <translation>Мережа</translation>
1065 </message>
1066 <message>
1067 <location filename="../../src/yuzu/configuration/configure_dialog.cpp" line="63"/>
1068 <location filename="../../src/yuzu/configuration/configure_dialog.cpp" line="152"/>
1069 <source>System</source>
1070 <translation>Система</translation>
1071 </message>
1072 <message>
1073 <location filename="../../src/yuzu/configuration/configure_dialog.cpp" line="64"/>
1074 <source>Game List</source>
1075 <translation>Список ігор</translation>
1076 </message>
1077 <message>
1078 <location filename="../../src/yuzu/configuration/configure_dialog.cpp" line="65"/>
1079 <source>Web</source>
1080 <translation>Мережа</translation>
1081 </message>
1082</context>
1083<context>
1084 <name>ConfigureFilesystem</name>
1085 <message>
1086 <location filename="../../src/yuzu/configuration/configure_filesystem.ui" line="14"/>
1087 <source>Form</source>
1088 <translation>Форма</translation>
1089 </message>
1090 <message>
1091 <location filename="../../src/yuzu/configuration/configure_filesystem.ui" line="17"/>
1092 <source>Filesystem</source>
1093 <translation>Файлова система</translation>
1094 </message>
1095 <message>
1096 <location filename="../../src/yuzu/configuration/configure_filesystem.ui" line="25"/>
1097 <source>Storage Directories</source>
1098 <translation>Каталоги зберігання</translation>
1099 </message>
1100 <message>
1101 <location filename="../../src/yuzu/configuration/configure_filesystem.ui" line="31"/>
1102 <source>NAND</source>
1103 <translation>NAND</translation>
1104 </message>
1105 <message>
1106 <location filename="../../src/yuzu/configuration/configure_filesystem.ui" line="38"/>
1107 <location filename="../../src/yuzu/configuration/configure_filesystem.ui" line="58"/>
1108 <location filename="../../src/yuzu/configuration/configure_filesystem.ui" line="114"/>
1109 <location filename="../../src/yuzu/configuration/configure_filesystem.ui" line="136"/>
1110 <location filename="../../src/yuzu/configuration/configure_filesystem.ui" line="143"/>
1111 <source>...</source>
1112 <translation>...</translation>
1113 </message>
1114 <message>
1115 <location filename="../../src/yuzu/configuration/configure_filesystem.ui" line="51"/>
1116 <source>SD Card</source>
1117 <translation>SD карта</translation>
1118 </message>
1119 <message>
1120 <location filename="../../src/yuzu/configuration/configure_filesystem.ui" line="84"/>
1121 <source>Gamecard</source>
1122 <translation>Картридж</translation>
1123 </message>
1124 <message>
1125 <location filename="../../src/yuzu/configuration/configure_filesystem.ui" line="90"/>
1126 <source>Path</source>
1127 <translation>Шлях</translation>
1128 </message>
1129 <message>
1130 <location filename="../../src/yuzu/configuration/configure_filesystem.ui" line="100"/>
1131 <source>Inserted</source>
1132 <translation>Вставлений</translation>
1133 </message>
1134 <message>
1135 <location filename="../../src/yuzu/configuration/configure_filesystem.ui" line="107"/>
1136 <source>Current Game</source>
1137 <translation>Поточна гра</translation>
1138 </message>
1139 <message>
1140 <location filename="../../src/yuzu/configuration/configure_filesystem.ui" line="124"/>
1141 <source>Patch Manager</source>
1142 <translation>Керування патчами</translation>
1143 </message>
1144 <message>
1145 <location filename="../../src/yuzu/configuration/configure_filesystem.ui" line="152"/>
1146 <source>Dump Decompressed NSOs</source>
1147 <translation>Дамп розпакованих NSO</translation>
1148 </message>
1149 <message>
1150 <location filename="../../src/yuzu/configuration/configure_filesystem.ui" line="159"/>
1151 <source>Dump ExeFS</source>
1152 <translation>Дамп ExeFS</translation>
1153 </message>
1154 <message>
1155 <location filename="../../src/yuzu/configuration/configure_filesystem.ui" line="168"/>
1156 <source>Mod Load Root</source>
1157 <translation>Папка з модами</translation>
1158 </message>
1159 <message>
1160 <location filename="../../src/yuzu/configuration/configure_filesystem.ui" line="175"/>
1161 <source>Dump Root</source>
1162 <translation>Корінь дампу</translation>
1163 </message>
1164 <message>
1165 <location filename="../../src/yuzu/configuration/configure_filesystem.ui" line="201"/>
1166 <source>Caching</source>
1167 <translation>Кешування</translation>
1168 </message>
1169 <message>
1170 <location filename="../../src/yuzu/configuration/configure_filesystem.ui" line="209"/>
1171 <source>Cache Game List Metadata</source>
1172 <translation>Кешувати метадані списку ігор</translation>
1173 </message>
1174 <message>
1175 <location filename="../../src/yuzu/configuration/configure_filesystem.ui" line="216"/>
1176 <location filename="../../src/yuzu/configuration/configure_filesystem.cpp" line="131"/>
1177 <location filename="../../src/yuzu/configuration/configure_filesystem.cpp" line="135"/>
1178 <location filename="../../src/yuzu/configuration/configure_filesystem.cpp" line="140"/>
1179 <source>Reset Metadata Cache</source>
1180 <translation>Скинути кеш метаданих</translation>
1181 </message>
1182 <message>
1183 <location filename="../../src/yuzu/configuration/configure_filesystem.cpp" line="93"/>
1184 <source>Select Emulated NAND Directory...</source>
1185 <translation>Виберіть папку для емульованого NAND...</translation>
1186 </message>
1187 <message>
1188 <location filename="../../src/yuzu/configuration/configure_filesystem.cpp" line="96"/>
1189 <source>Select Emulated SD Directory...</source>
1190 <translation>Виберіть папку для емульованого SD...</translation>
1191 </message>
1192 <message>
1193 <location filename="../../src/yuzu/configuration/configure_filesystem.cpp" line="99"/>
1194 <source>Select Gamecard Path...</source>
1195 <translation>Оберіть папку для картриджів...</translation>
1196 </message>
1197 <message>
1198 <location filename="../../src/yuzu/configuration/configure_filesystem.cpp" line="102"/>
1199 <source>Select Dump Directory...</source>
1200 <translation>Оберіть папку для дампів...</translation>
1201 </message>
1202 <message>
1203 <location filename="../../src/yuzu/configuration/configure_filesystem.cpp" line="105"/>
1204 <source>Select Mod Load Directory...</source>
1205 <translation>Оберіть папку для модів...</translation>
1206 </message>
1207 <message>
1208 <location filename="../../src/yuzu/configuration/configure_filesystem.cpp" line="132"/>
1209 <source>The metadata cache is already empty.</source>
1210 <translation>Кеш метаданих вже порожній.</translation>
1211 </message>
1212 <message>
1213 <location filename="../../src/yuzu/configuration/configure_filesystem.cpp" line="136"/>
1214 <source>The operation completed successfully.</source>
1215 <translation>Операція завершилася успішно.</translation>
1216 </message>
1217 <message>
1218 <location filename="../../src/yuzu/configuration/configure_filesystem.cpp" line="141"/>
1219 <source>The metadata cache couldn&apos;t be deleted. It might be in use or non-existent.</source>
1220 <translation>Кеш метаданих не можна видалити. Можливо, він використовується або відсутній.</translation>
1221 </message>
1222</context>
1223<context>
1224 <name>ConfigureGeneral</name>
1225 <message>
1226 <location filename="../../src/yuzu/configuration/configure_general.ui" line="14"/>
1227 <source>Form</source>
1228 <translation>Форма</translation>
1229 </message>
1230 <message>
1231 <location filename="../../src/yuzu/configuration/configure_general.ui" line="17"/>
1232 <location filename="../../src/yuzu/configuration/configure_general.ui" line="25"/>
1233 <source>General</source>
1234 <translation>Загальні</translation>
1235 </message>
1236 <message>
1237 <location filename="../../src/yuzu/configuration/configure_general.ui" line="35"/>
1238 <source>Limit Speed Percent</source>
1239 <translation>Обмеження відсотка швидкості</translation>
1240 </message>
1241 <message>
1242 <location filename="../../src/yuzu/configuration/configure_general.ui" line="42"/>
1243 <source>%</source>
1244 <translation>%</translation>
1245 </message>
1246 <message>
1247 <location filename="../../src/yuzu/configuration/configure_general.ui" line="60"/>
1248 <source>Multicore CPU Emulation</source>
1249 <translation>Багатоядерна емуляція ЦП</translation>
1250 </message>
1251 <message>
1252 <location filename="../../src/yuzu/configuration/configure_general.ui" line="67"/>
1253 <source>Extended memory layout (6GB DRAM)</source>
1254 <translation>Розширене компонування пам&apos;яті (6 ГБ DRAM)</translation>
1255 </message>
1256 <message>
1257 <location filename="../../src/yuzu/configuration/configure_general.ui" line="74"/>
1258 <source>Confirm exit while emulation is running</source>
1259 <translation>Підтверджувати вихід під час емуляції</translation>
1260 </message>
1261 <message>
1262 <location filename="../../src/yuzu/configuration/configure_general.ui" line="81"/>
1263 <source>Prompt for user on game boot</source>
1264 <translation>Запитувати користувача під час запуску гри</translation>
1265 </message>
1266 <message>
1267 <location filename="../../src/yuzu/configuration/configure_general.ui" line="88"/>
1268 <source>Pause emulation when in background</source>
1269 <translation>Призупиняти емуляцію у фоновому режимі</translation>
1270 </message>
1271 <message>
1272 <location filename="../../src/yuzu/configuration/configure_general.ui" line="95"/>
1273 <source>Mute audio when in background</source>
1274 <translation>Приглушити звук у фоновому режимі</translation>
1275 </message>
1276 <message>
1277 <location filename="../../src/yuzu/configuration/configure_general.ui" line="102"/>
1278 <source>Hide mouse on inactivity</source>
1279 <translation>Приховування миші при бездіяльності</translation>
1280 </message>
1281 <message>
1282 <location filename="../../src/yuzu/configuration/configure_general.ui" line="144"/>
1283 <source>Reset All Settings</source>
1284 <translation>Скинути всі налаштування</translation>
1285 </message>
1286 <message>
1287 <location filename="../../src/yuzu/configuration/configure_general.cpp" line="68"/>
1288 <source>yuzu</source>
1289 <translation>yuzu</translation>
1290 </message>
1291 <message>
1292 <location filename="../../src/yuzu/configuration/configure_general.cpp" line="69"/>
1293 <source>This reset all settings and remove all per-game configurations. This will not delete game directories, profiles, or input profiles. Proceed?</source>
1294 <translation>Це скине всі налаштування і видалить усі конфігурації під окремі ігри. При цьому не будуть видалені шляхи до ігор, профілів або профілів вводу. Продовжити?</translation>
1295 </message>
1296</context>
1297<context>
1298 <name>ConfigureGraphics</name>
1299 <message>
1300 <location filename="../../src/yuzu/configuration/configure_graphics.ui" line="14"/>
1301 <source>Form</source>
1302 <translation>Форма</translation>
1303 </message>
1304 <message>
1305 <location filename="../../src/yuzu/configuration/configure_graphics.ui" line="17"/>
1306 <source>Graphics</source>
1307 <translation>Графіка</translation>
1308 </message>
1309 <message>
1310 <location filename="../../src/yuzu/configuration/configure_graphics.ui" line="25"/>
1311 <source>API Settings</source>
1312 <translation>Налаштування API</translation>
1313 </message>
1314 <message>
1315 <location filename="../../src/yuzu/configuration/configure_graphics.ui" line="64"/>
1316 <source>Shader Backend:</source>
1317 <translation>Бекенд шейдерів:</translation>
1318 </message>
1319 <message>
1320 <location filename="../../src/yuzu/configuration/configure_graphics.ui" line="92"/>
1321 <source>Device:</source>
1322 <translation>Пристрій:</translation>
1323 </message>
1324 <message>
1325 <location filename="../../src/yuzu/configuration/configure_graphics.ui" line="120"/>
1326 <source>API:</source>
1327 <translation>API:</translation>
1328 </message>
1329 <message>
1330 <location filename="../../src/yuzu/configuration/configure_graphics.ui" line="156"/>
1331 <source>Graphics Settings</source>
1332 <translation>Налаштування графіки</translation>
1333 </message>
1334 <message>
1335 <location filename="../../src/yuzu/configuration/configure_graphics.ui" line="162"/>
1336 <source>Use disk pipeline cache</source>
1337 <translation>Використовувати кеш конвеєра на диску</translation>
1338 </message>
1339 <message>
1340 <location filename="../../src/yuzu/configuration/configure_graphics.ui" line="169"/>
1341 <source>Use asynchronous GPU emulation</source>
1342 <translation>Використовувати асинхронну емуляцію ГП</translation>
1343 </message>
1344 <message>
1345 <location filename="../../src/yuzu/configuration/configure_graphics.ui" line="176"/>
1346 <source>Accelerate ASTC texture decoding</source>
1347 <translation>Прискорення декодування текстур ASTC</translation>
1348 </message>
1349 <message>
1350 <location filename="../../src/yuzu/configuration/configure_graphics.ui" line="198"/>
1351 <source>NVDEC emulation:</source>
1352 <translation>Емуляція NVDEC:</translation>
1353 </message>
1354 <message>
1355 <location filename="../../src/yuzu/configuration/configure_graphics.ui" line="206"/>
1356 <source>No Video Output</source>
1357 <translation>Відсутність відеовиходу</translation>
1358 </message>
1359 <message>
1360 <location filename="../../src/yuzu/configuration/configure_graphics.ui" line="211"/>
1361 <source>CPU Video Decoding</source>
1362 <translation>Декодування відео на ЦП</translation>
1363 </message>
1364 <message>
1365 <location filename="../../src/yuzu/configuration/configure_graphics.ui" line="216"/>
1366 <source>GPU Video Decoding (Default)</source>
1367 <translation>Декодування відео на ГП (за замовчуванням)</translation>
1368 </message>
1369 <message>
1370 <location filename="../../src/yuzu/configuration/configure_graphics.ui" line="242"/>
1371 <source>Fullscreen Mode:</source>
1372 <translation>Повноекранний режим:</translation>
1373 </message>
1374 <message>
1375 <location filename="../../src/yuzu/configuration/configure_graphics.ui" line="250"/>
1376 <source>Borderless Windowed</source>
1377 <translation>Вікно без рамок</translation>
1378 </message>
1379 <message>
1380 <location filename="../../src/yuzu/configuration/configure_graphics.ui" line="255"/>
1381 <source>Exclusive Fullscreen</source>
1382 <translation>Ексклюзивний повноекранний</translation>
1383 </message>
1384 <message>
1385 <location filename="../../src/yuzu/configuration/configure_graphics.ui" line="281"/>
1386 <source>Aspect Ratio:</source>
1387 <translation>Співвідношення сторін:</translation>
1388 </message>
1389 <message>
1390 <location filename="../../src/yuzu/configuration/configure_graphics.ui" line="289"/>
1391 <source>Default (16:9)</source>
1392 <translation>За замовчуванням (16:9)</translation>
1393 </message>
1394 <message>
1395 <location filename="../../src/yuzu/configuration/configure_graphics.ui" line="294"/>
1396 <source>Force 4:3</source>
1397 <translation>Змусити 4:3</translation>
1398 </message>
1399 <message>
1400 <location filename="../../src/yuzu/configuration/configure_graphics.ui" line="299"/>
1401 <source>Force 21:9</source>
1402 <translation>Змусити 21:9</translation>
1403 </message>
1404 <message>
1405 <location filename="../../src/yuzu/configuration/configure_graphics.ui" line="304"/>
1406 <source>Force 16:10</source>
1407 <translation>Змусити 16:10</translation>
1408 </message>
1409 <message>
1410 <location filename="../../src/yuzu/configuration/configure_graphics.ui" line="309"/>
1411 <source>Stretch to Window</source>
1412 <translation>Розтягнути до вікна</translation>
1413 </message>
1414 <message>
1415 <location filename="../../src/yuzu/configuration/configure_graphics.ui" line="335"/>
1416 <source>Resolution:</source>
1417 <translation>Роздільна здатність:</translation>
1418 </message>
1419 <message>
1420 <location filename="../../src/yuzu/configuration/configure_graphics.ui" line="343"/>
1421 <source>0.5X (360p/540p) [EXPERIMENTAL]</source>
1422 <translation>0.5X (360p/540p) [ЕКСПЕРИМЕНТАЛЬНЕ]</translation>
1423 </message>
1424 <message>
1425 <location filename="../../src/yuzu/configuration/configure_graphics.ui" line="348"/>
1426 <source>0.75X (540p/810p) [EXPERIMENTAL]</source>
1427 <translation>0.75X (540p/810p) [ЕКСПЕРИМЕНТАЛЬНЕ]</translation>
1428 </message>
1429 <message>
1430 <location filename="../../src/yuzu/configuration/configure_graphics.ui" line="353"/>
1431 <source>1X (720p/1080p)</source>
1432 <translation>1X (720p/1080p)</translation>
1433 </message>
1434 <message>
1435 <location filename="../../src/yuzu/configuration/configure_graphics.ui" line="358"/>
1436 <source>2X (1440p/2160p)</source>
1437 <translation>2X (1440p/2160p)</translation>
1438 </message>
1439 <message>
1440 <location filename="../../src/yuzu/configuration/configure_graphics.ui" line="363"/>
1441 <source>3X (2160p/3240p)</source>
1442 <translation>3X (2160p/3240p)</translation>
1443 </message>
1444 <message>
1445 <location filename="../../src/yuzu/configuration/configure_graphics.ui" line="368"/>
1446 <source>4X (2880p/4320p)</source>
1447 <translation>4X (2880p/4320p)</translation>
1448 </message>
1449 <message>
1450 <location filename="../../src/yuzu/configuration/configure_graphics.ui" line="373"/>
1451 <source>5X (3600p/5400p)</source>
1452 <translation>5X (3600p/5400p)</translation>
1453 </message>
1454 <message>
1455 <location filename="../../src/yuzu/configuration/configure_graphics.ui" line="378"/>
1456 <source>6X (4320p/6480p)</source>
1457 <translation>6X (4320p/6480p)</translation>
1458 </message>
1459 <message>
1460 <location filename="../../src/yuzu/configuration/configure_graphics.ui" line="404"/>
1461 <source>Window Adapting Filter:</source>
1462 <translation>Фільтр адаптації вікна:</translation>
1463 </message>
1464 <message>
1465 <location filename="../../src/yuzu/configuration/configure_graphics.ui" line="412"/>
1466 <source>Nearest Neighbor</source>
1467 <translation>Найближчий сусід</translation>
1468 </message>
1469 <message>
1470 <location filename="../../src/yuzu/configuration/configure_graphics.ui" line="417"/>
1471 <source>Bilinear</source>
1472 <translation>Білінійне</translation>
1473 </message>
1474 <message>
1475 <location filename="../../src/yuzu/configuration/configure_graphics.ui" line="422"/>
1476 <source>Bicubic</source>
1477 <translation>Бікубічне</translation>
1478 </message>
1479 <message>
1480 <location filename="../../src/yuzu/configuration/configure_graphics.ui" line="427"/>
1481 <source>Gaussian</source>
1482 <translation>Гауса</translation>
1483 </message>
1484 <message>
1485 <location filename="../../src/yuzu/configuration/configure_graphics.ui" line="432"/>
1486 <source>ScaleForce</source>
1487 <translation>ScaleForce</translation>
1488 </message>
1489 <message>
1490 <location filename="../../src/yuzu/configuration/configure_graphics.ui" line="437"/>
1491 <source>AMD FidelityFX™️ Super Resolution (Vulkan Only)</source>
1492 <translation>AMD FidelityFX™️ Super Resolution (Лише для Vulkan)</translation>
1493 </message>
1494 <message>
1495 <location filename="../../src/yuzu/configuration/configure_graphics.ui" line="463"/>
1496 <source>Anti-Aliasing Method:</source>
1497 <translation>Метод згладжування:</translation>
1498 </message>
1499 <message>
1500 <location filename="../../src/yuzu/configuration/configure_graphics.ui" line="471"/>
1501 <source>None</source>
1502 <translation>Вимкнено</translation>
1503 </message>
1504 <message>
1505 <location filename="../../src/yuzu/configuration/configure_graphics.ui" line="476"/>
1506 <source>FXAA</source>
1507 <translation>FXAA</translation>
1508 </message>
1509 <message>
1510 <location filename="../../src/yuzu/configuration/configure_graphics.ui" line="511"/>
1511 <location filename="../../src/yuzu/configuration/configure_graphics.ui" line="521"/>
1512 <source>Use global background color</source>
1513 <translation>Використовувати глобальний фоновий колір</translation>
1514 </message>
1515 <message>
1516 <location filename="../../src/yuzu/configuration/configure_graphics.ui" line="526"/>
1517 <source>Set background color:</source>
1518 <translation>Встановити фоновий колір:</translation>
1519 </message>
1520 <message>
1521 <location filename="../../src/yuzu/configuration/configure_graphics.ui" line="534"/>
1522 <source>Background Color:</source>
1523 <translation>Фоновий колір:</translation>
1524 </message>
1525 <message>
1526 <location filename="../../src/yuzu/configuration/configure_graphics.cpp" line="33"/>
1527 <source>GLASM (Assembly Shaders, NVIDIA Only)</source>
1528 <translation>GLASM (асемблерні шейдери, лише для NVIDIA)</translation>
1529 </message>
1530</context>
1531<context>
1532 <name>ConfigureGraphicsAdvanced</name>
1533 <message>
1534 <location filename="../../src/yuzu/configuration/configure_graphics_advanced.ui" line="14"/>
1535 <source>Form</source>
1536 <translation>Форма</translation>
1537 </message>
1538 <message>
1539 <location filename="../../src/yuzu/configuration/configure_graphics_advanced.ui" line="17"/>
1540 <source>Advanced</source>
1541 <translation>Розширені</translation>
1542 </message>
1543 <message>
1544 <location filename="../../src/yuzu/configuration/configure_graphics_advanced.ui" line="25"/>
1545 <source>Advanced Graphics Settings</source>
1546 <translation>Розширені налаштування графіки</translation>
1547 </message>
1548 <message>
1549 <location filename="../../src/yuzu/configuration/configure_graphics_advanced.ui" line="46"/>
1550 <source>Accuracy Level:</source>
1551 <translation>Рівень точності:</translation>
1552 </message>
1553 <message>
1554 <location filename="../../src/yuzu/configuration/configure_graphics_advanced.ui" line="75"/>
1555 <source>VSync prevents the screen from tearing, but some graphics cards have lower performance with VSync enabled. Keep it enabled if you don&apos;t notice a performance difference.</source>
1556 <translation>Вертикальна синхронізація запобігає розривам екрана, але деякі відеокарти мають нижчу продуктивність при вертикальній синхронізації. Залишайте увімкненим, якщо ви не помічаєте різниці в продуктивності.</translation>
1557 </message>
1558 <message>
1559 <location filename="../../src/yuzu/configuration/configure_graphics_advanced.ui" line="78"/>
1560 <source>Use VSync</source>
1561 <translation>Використувати вертикальну сінхронізацію</translation>
1562 </message>
1563 <message>
1564 <location filename="../../src/yuzu/configuration/configure_graphics_advanced.ui" line="85"/>
1565 <source>Enables asynchronous shader compilation, which may reduce shader stutter. This feature is experimental.</source>
1566 <translation>Вмикає асинхронну компіляцію шейдерів, що зменшить зависання через шейдери. Функція є експериментальною.</translation>
1567 </message>
1568 <message>
1569 <location filename="../../src/yuzu/configuration/configure_graphics_advanced.ui" line="88"/>
1570 <source>Use asynchronous shader building (Hack)</source>
1571 <translation>Використовувати асинхронну побудову шейдерів (хак)</translation>
1572 </message>
1573 <message>
1574 <location filename="../../src/yuzu/configuration/configure_graphics_advanced.ui" line="95"/>
1575 <source>Enables Fast GPU Time. This option will force most games to run at their highest native resolution.</source>
1576 <translation>Вмикає функцію Fast GPU Time. Цей параметр змусить більшість ігор працювати в максимальній рідній роздільній здатності.</translation>
1577 </message>
1578 <message>
1579 <location filename="../../src/yuzu/configuration/configure_graphics_advanced.ui" line="98"/>
1580 <source>Use Fast GPU Time (Hack)</source>
1581 <translation>Увімкнути Fast GPU Time (Хак)</translation>
1582 </message>
1583 <message>
1584 <location filename="../../src/yuzu/configuration/configure_graphics_advanced.ui" line="105"/>
1585 <source>Enables pessimistic buffer flushes. This option will force unmodified buffers to be flushed, which can cost performance.</source>
1586 <translation>Вмикає песимістичне очищення буферів. Ця опція змушує промивати немодифіковані буфери, що може знизити продуктивність.</translation>
1587 </message>
1588 <message>
1589 <location filename="../../src/yuzu/configuration/configure_graphics_advanced.ui" line="108"/>
1590 <source>Use pessimistic buffer flushes (Hack)</source>
1591 <translation>Використовувати песимістичне очищення буферів (Хак)</translation>
1592 </message>
1593 <message>
1594 <location filename="../../src/yuzu/configuration/configure_graphics_advanced.ui" line="130"/>
1595 <source>Anisotropic Filtering:</source>
1596 <translation>Анізотропна фільтрація:</translation>
1597 </message>
1598 <message>
1599 <location filename="../../src/yuzu/configuration/configure_graphics_advanced.ui" line="138"/>
1600 <source>Automatic</source>
1601 <translation>Автоматично</translation>
1602 </message>
1603 <message>
1604 <location filename="../../src/yuzu/configuration/configure_graphics_advanced.ui" line="143"/>
1605 <source>Default</source>
1606 <translation>За замовчуванням</translation>
1607 </message>
1608 <message>
1609 <location filename="../../src/yuzu/configuration/configure_graphics_advanced.ui" line="148"/>
1610 <source>2x</source>
1611 <translation>2x</translation>
1612 </message>
1613 <message>
1614 <location filename="../../src/yuzu/configuration/configure_graphics_advanced.ui" line="153"/>
1615 <source>4x</source>
1616 <translation>4x</translation>
1617 </message>
1618 <message>
1619 <location filename="../../src/yuzu/configuration/configure_graphics_advanced.ui" line="158"/>
1620 <source>8x</source>
1621 <translation>8x</translation>
1622 </message>
1623 <message>
1624 <location filename="../../src/yuzu/configuration/configure_graphics_advanced.ui" line="163"/>
1625 <source>16x</source>
1626 <translation>16x</translation>
1627 </message>
1628</context>
1629<context>
1630 <name>ConfigureHotkeys</name>
1631 <message>
1632 <location filename="../../src/yuzu/configuration/configure_hotkeys.ui" line="14"/>
1633 <source>Hotkey Settings</source>
1634 <translation>Налаштування гарячих клавіш</translation>
1635 </message>
1636 <message>
1637 <location filename="../../src/yuzu/configuration/configure_hotkeys.ui" line="17"/>
1638 <source>Hotkeys</source>
1639 <translation>Гарячі клавіші</translation>
1640 </message>
1641 <message>
1642 <location filename="../../src/yuzu/configuration/configure_hotkeys.ui" line="25"/>
1643 <source>Double-click on a binding to change it.</source>
1644 <translation>Натисніть двічі на прив&apos;язці, щоб змінити її.</translation>
1645 </message>
1646 <message>
1647 <location filename="../../src/yuzu/configuration/configure_hotkeys.ui" line="45"/>
1648 <source>Clear All</source>
1649 <translation>Очистити все</translation>
1650 </message>
1651 <message>
1652 <location filename="../../src/yuzu/configuration/configure_hotkeys.ui" line="52"/>
1653 <source>Restore Defaults</source>
1654 <translation>Відновити значення за замовчуванням.</translation>
1655 </message>
1656 <message>
1657 <location filename="../../src/yuzu/configuration/configure_hotkeys.cpp" line="98"/>
1658 <source>Action</source>
1659 <translation>Дія</translation>
1660 </message>
1661 <message>
1662 <location filename="../../src/yuzu/configuration/configure_hotkeys.cpp" line="98"/>
1663 <source>Hotkey</source>
1664 <translation>Гаряча клавіша</translation>
1665 </message>
1666 <message>
1667 <location filename="../../src/yuzu/configuration/configure_hotkeys.cpp" line="98"/>
1668 <source>Controller Hotkey</source>
1669 <translation>Гаряча клавіша контролера</translation>
1670 </message>
1671 <message>
1672 <location filename="../../src/yuzu/configuration/configure_hotkeys.cpp" line="138"/>
1673 <location filename="../../src/yuzu/configuration/configure_hotkeys.cpp" line="164"/>
1674 <location filename="../../src/yuzu/configuration/configure_hotkeys.cpp" line="379"/>
1675 <source>Conflicting Key Sequence</source>
1676 <translation>Конфліктуюча комбінація клавіш</translation>
1677 </message>
1678 <message>
1679 <location filename="../../src/yuzu/configuration/configure_hotkeys.cpp" line="139"/>
1680 <location filename="../../src/yuzu/configuration/configure_hotkeys.cpp" line="165"/>
1681 <source>The entered key sequence is already assigned to: %1</source>
1682 <translation>Введена комбінація вже призначена до: %1</translation>
1683 </message>
1684 <message>
1685 <location filename="../../src/yuzu/configuration/configure_hotkeys.cpp" line="158"/>
1686 <source>Home+%1</source>
1687 <translation>Home+%1</translation>
1688 </message>
1689 <message>
1690 <location filename="../../src/yuzu/configuration/configure_hotkeys.cpp" line="172"/>
1691 <source>[waiting]</source>
1692 <translation>[очікування]</translation>
1693 </message>
1694 <message>
1695 <location filename="../../src/yuzu/configuration/configure_hotkeys.cpp" line="242"/>
1696 <source>Invalid</source>
1697 <translation>Неприпустимо</translation>
1698 </message>
1699 <message>
1700 <location filename="../../src/yuzu/configuration/configure_hotkeys.cpp" line="343"/>
1701 <source>Restore Default</source>
1702 <translation>Відновити значення за замовчуванням</translation>
1703 </message>
1704 <message>
1705 <location filename="../../src/yuzu/configuration/configure_hotkeys.cpp" line="344"/>
1706 <source>Clear</source>
1707 <translation>Очистити</translation>
1708 </message>
1709 <message>
1710 <location filename="../../src/yuzu/configuration/configure_hotkeys.cpp" line="365"/>
1711 <source>Conflicting Button Sequence</source>
1712 <translation>Конфліктуюче поєднання кнопок</translation>
1713 </message>
1714 <message>
1715 <location filename="../../src/yuzu/configuration/configure_hotkeys.cpp" line="366"/>
1716 <source>The default button sequence is already assigned to: %1</source>
1717 <translation>Типова комбінація кнопок вже призначена до: %1</translation>
1718 </message>
1719 <message>
1720 <location filename="../../src/yuzu/configuration/configure_hotkeys.cpp" line="380"/>
1721 <source>The default key sequence is already assigned to: %1</source>
1722 <translation>Типова комбінація клавіш вже призначена до: %1</translation>
1723 </message>
1724</context>
1725<context>
1726 <name>ConfigureInput</name>
1727 <message>
1728 <location filename="../../src/yuzu/configuration/configure_input.ui" line="14"/>
1729 <source>ConfigureInput</source>
1730 <translation>НалаштуванняВводу</translation>
1731 </message>
1732 <message>
1733 <location filename="../../src/yuzu/configuration/configure_input.ui" line="39"/>
1734 <location filename="../../src/yuzu/configuration/configure_input.ui" line="42"/>
1735 <source>Player 1</source>
1736 <translation>Гравець 1</translation>
1737 </message>
1738 <message>
1739 <location filename="../../src/yuzu/configuration/configure_input.ui" line="47"/>
1740 <location filename="../../src/yuzu/configuration/configure_input.ui" line="50"/>
1741 <source>Player 2</source>
1742 <translation>Гравець 2</translation>
1743 </message>
1744 <message>
1745 <location filename="../../src/yuzu/configuration/configure_input.ui" line="55"/>
1746 <location filename="../../src/yuzu/configuration/configure_input.ui" line="58"/>
1747 <source>Player 3</source>
1748 <translation>Гравець 3</translation>
1749 </message>
1750 <message>
1751 <location filename="../../src/yuzu/configuration/configure_input.ui" line="63"/>
1752 <location filename="../../src/yuzu/configuration/configure_input.ui" line="66"/>
1753 <source>Player 4</source>
1754 <translation>Гравець 4</translation>
1755 </message>
1756 <message>
1757 <location filename="../../src/yuzu/configuration/configure_input.ui" line="71"/>
1758 <location filename="../../src/yuzu/configuration/configure_input.ui" line="74"/>
1759 <source>Player 5</source>
1760 <translation>Гравець 5</translation>
1761 </message>
1762 <message>
1763 <location filename="../../src/yuzu/configuration/configure_input.ui" line="79"/>
1764 <location filename="../../src/yuzu/configuration/configure_input.ui" line="82"/>
1765 <source>Player 6</source>
1766 <translation>Гравець 6</translation>
1767 </message>
1768 <message>
1769 <location filename="../../src/yuzu/configuration/configure_input.ui" line="87"/>
1770 <location filename="../../src/yuzu/configuration/configure_input.ui" line="90"/>
1771 <source>Player 7</source>
1772 <translation>Гравець 7</translation>
1773 </message>
1774 <message>
1775 <location filename="../../src/yuzu/configuration/configure_input.ui" line="95"/>
1776 <location filename="../../src/yuzu/configuration/configure_input.ui" line="98"/>
1777 <source>Player 8</source>
1778 <translation>Гравець 8</translation>
1779 </message>
1780 <message>
1781 <location filename="../../src/yuzu/configuration/configure_input.ui" line="103"/>
1782 <location filename="../../src/yuzu/configuration/configure_input.ui" line="106"/>
1783 <source>Advanced</source>
1784 <translation>Розширені</translation>
1785 </message>
1786 <message>
1787 <location filename="../../src/yuzu/configuration/configure_input.ui" line="138"/>
1788 <source>Console Mode</source>
1789 <translation>Режим консолі</translation>
1790 </message>
1791 <message>
1792 <location filename="../../src/yuzu/configuration/configure_input.ui" line="159"/>
1793 <source>Docked</source>
1794 <translation>У док-станції</translation>
1795 </message>
1796 <message>
1797 <location filename="../../src/yuzu/configuration/configure_input.ui" line="169"/>
1798 <source>Handheld</source>
1799 <translation>Портативний</translation>
1800 </message>
1801 <message>
1802 <location filename="../../src/yuzu/configuration/configure_input.ui" line="179"/>
1803 <source>Vibration</source>
1804 <translation>Вібрація</translation>
1805 </message>
1806 <message>
1807 <location filename="../../src/yuzu/configuration/configure_input.ui" line="215"/>
1808 <location filename="../../src/yuzu/configuration/configure_input.ui" line="261"/>
1809 <source>Configure</source>
1810 <translation>Налаштувати</translation>
1811 </message>
1812 <message>
1813 <location filename="../../src/yuzu/configuration/configure_input.ui" line="225"/>
1814 <source>Motion</source>
1815 <translation>Рух</translation>
1816 </message>
1817 <message>
1818 <location filename="../../src/yuzu/configuration/configure_input.ui" line="296"/>
1819 <source>Controllers</source>
1820 <translation>Контролери</translation>
1821 </message>
1822 <message>
1823 <location filename="../../src/yuzu/configuration/configure_input.ui" line="324"/>
1824 <source>1</source>
1825 <translation>1</translation>
1826 </message>
1827 <message>
1828 <location filename="../../src/yuzu/configuration/configure_input.ui" line="365"/>
1829 <source>2</source>
1830 <translation>2</translation>
1831 </message>
1832 <message>
1833 <location filename="../../src/yuzu/configuration/configure_input.ui" line="375"/>
1834 <source>3</source>
1835 <translation>3</translation>
1836 </message>
1837 <message>
1838 <location filename="../../src/yuzu/configuration/configure_input.ui" line="385"/>
1839 <source>4</source>
1840 <translation>4</translation>
1841 </message>
1842 <message>
1843 <location filename="../../src/yuzu/configuration/configure_input.ui" line="395"/>
1844 <source>5</source>
1845 <translation>5</translation>
1846 </message>
1847 <message>
1848 <location filename="../../src/yuzu/configuration/configure_input.ui" line="405"/>
1849 <source>6</source>
1850 <translation>6</translation>
1851 </message>
1852 <message>
1853 <location filename="../../src/yuzu/configuration/configure_input.ui" line="415"/>
1854 <source>7</source>
1855 <translation>7</translation>
1856 </message>
1857 <message>
1858 <location filename="../../src/yuzu/configuration/configure_input.ui" line="425"/>
1859 <source>8</source>
1860 <translation>8</translation>
1861 </message>
1862 <message>
1863 <location filename="../../src/yuzu/configuration/configure_input.ui" line="435"/>
1864 <source>Connected</source>
1865 <translation>З&apos;єднано</translation>
1866 </message>
1867 <message>
1868 <location filename="../../src/yuzu/configuration/configure_input.ui" line="494"/>
1869 <source>Defaults</source>
1870 <translation>За замовчуванням</translation>
1871 </message>
1872 <message>
1873 <location filename="../../src/yuzu/configuration/configure_input.ui" line="537"/>
1874 <source>Clear</source>
1875 <translation>Очистити</translation>
1876 </message>
1877</context>
1878<context>
1879 <name>ConfigureInputAdvanced</name>
1880 <message>
1881 <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="14"/>
1882 <source>Configure Input</source>
1883 <translation>Налаштування вводу</translation>
1884 </message>
1885 <message>
1886 <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="74"/>
1887 <source>Joycon Colors</source>
1888 <translation>Кольори Joy-Con&apos;ів</translation>
1889 </message>
1890 <message>
1891 <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="125"/>
1892 <source>Player 1</source>
1893 <translation>Гравець 1</translation>
1894 </message>
1895 <message>
1896 <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="164"/>
1897 <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="450"/>
1898 <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="754"/>
1899 <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="1040"/>
1900 <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="1365"/>
1901 <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="1651"/>
1902 <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="1955"/>
1903 <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="2241"/>
1904 <source>L Body</source>
1905 <translation>Лівий контролер</translation>
1906 </message>
1907 <message>
1908 <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="219"/>
1909 <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="505"/>
1910 <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="809"/>
1911 <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="1095"/>
1912 <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="1420"/>
1913 <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="1706"/>
1914 <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="2010"/>
1915 <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="2296"/>
1916 <source>L Button</source>
1917 <translation>Кнопка L</translation>
1918 </message>
1919 <message>
1920 <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="295"/>
1921 <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="581"/>
1922 <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="885"/>
1923 <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="1171"/>
1924 <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="1496"/>
1925 <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="1782"/>
1926 <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="2086"/>
1927 <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="2372"/>
1928 <source>R Body</source>
1929 <translation>Правий контролер</translation>
1930 </message>
1931 <message>
1932 <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="350"/>
1933 <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="636"/>
1934 <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="940"/>
1935 <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="1226"/>
1936 <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="1551"/>
1937 <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="1837"/>
1938 <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="2141"/>
1939 <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="2427"/>
1940 <source>R Button</source>
1941 <translation>Кнопка R</translation>
1942 </message>
1943 <message>
1944 <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="411"/>
1945 <source>Player 2</source>
1946 <translation>Гравець 2</translation>
1947 </message>
1948 <message>
1949 <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="715"/>
1950 <source>Player 3</source>
1951 <translation>Гравець 3</translation>
1952 </message>
1953 <message>
1954 <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="1001"/>
1955 <source>Player 4</source>
1956 <translation>Гравець 4</translation>
1957 </message>
1958 <message>
1959 <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="1326"/>
1960 <source>Player 5</source>
1961 <translation>Гравець 5</translation>
1962 </message>
1963 <message>
1964 <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="1612"/>
1965 <source>Player 6</source>
1966 <translation>Гравець 6</translation>
1967 </message>
1968 <message>
1969 <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="1916"/>
1970 <source>Player 7</source>
1971 <translation>Гравець 7</translation>
1972 </message>
1973 <message>
1974 <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="2202"/>
1975 <source>Player 8</source>
1976 <translation>Гравець 8</translation>
1977 </message>
1978 <message>
1979 <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="2533"/>
1980 <source>Emulated Devices</source>
1981 <translation>Емульовані пристрої</translation>
1982 </message>
1983 <message>
1984 <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="2545"/>
1985 <source>Keyboard</source>
1986 <translation>Клавіатура</translation>
1987 </message>
1988 <message>
1989 <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="2558"/>
1990 <source>Mouse</source>
1991 <translation>Миша</translation>
1992 </message>
1993 <message>
1994 <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="2565"/>
1995 <source>Touchscreen</source>
1996 <translation>Сенсорний екран</translation>
1997 </message>
1998 <message>
1999 <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="2588"/>
2000 <source>Advanced</source>
2001 <translation>Розширені</translation>
2002 </message>
2003 <message>
2004 <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="2595"/>
2005 <source>Debug Controller</source>
2006 <translation>Налагоджувальний контролер</translation>
2007 </message>
2008 <message>
2009 <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="2602"/>
2010 <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="2616"/>
2011 <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="2630"/>
2012 <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="2743"/>
2013 <source>Configure</source>
2014 <translation>Налаштувати</translation>
2015 </message>
2016 <message>
2017 <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="2609"/>
2018 <source>Ring Controller</source>
2019 <translation>Контролер Ring</translation>
2020 </message>
2021 <message>
2022 <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="2623"/>
2023 <source>Infrared Camera</source>
2024 <translation>Інфрачервона камера</translation>
2025 </message>
2026 <message>
2027 <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="2640"/>
2028 <source>Other</source>
2029 <translation>Інше</translation>
2030 </message>
2031 <message>
2032 <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="2652"/>
2033 <source>Emulate Analog with Keyboard Input</source>
2034 <translation>Емуляція аналогового вводу з клавіатури</translation>
2035 </message>
2036 <message>
2037 <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="2659"/>
2038 <source>Requires restarting yuzu</source>
2039 <translation>Потребує перезапуску yuzu</translation>
2040 </message>
2041 <message>
2042 <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="2668"/>
2043 <source>Enable XInput 8 player support (disables web applet)</source>
2044 <translation>Увімкнути підтримку 8-ми гравців на XInput (відключає веб-аплет)</translation>
2045 </message>
2046 <message>
2047 <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="2681"/>
2048 <source>Enable UDP controllers (not needed for motion)</source>
2049 <translation>Увімкнути UDP контролери (не обов&apos;язково для руху)</translation>
2050 </message>
2051 <message>
2052 <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="2694"/>
2053 <source>Controller navigation</source>
2054 <translation>Навігація контролера</translation>
2055 </message>
2056 <message>
2057 <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="2707"/>
2058 <source>Enable mouse panning</source>
2059 <translation>Увімкнути панорамування миші</translation>
2060 </message>
2061 <message>
2062 <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="2714"/>
2063 <source>Mouse sensitivity</source>
2064 <translation>Чутливість миші</translation>
2065 </message>
2066 <message>
2067 <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="2720"/>
2068 <source>%</source>
2069 <translation>%</translation>
2070 </message>
2071 <message>
2072 <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="2736"/>
2073 <source>Motion / Touch</source>
2074 <translation>Рух і сенсор</translation>
2075 </message>
2076</context>
2077<context>
2078 <name>ConfigureInputPlayer</name>
2079 <message>
2080 <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="14"/>
2081 <source>Configure Input</source>
2082 <translation>Налаштування вводу</translation>
2083 </message>
2084 <message>
2085 <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="63"/>
2086 <source>Connect Controller</source>
2087 <translation>Підключити контролер</translation>
2088 </message>
2089 <message>
2090 <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="100"/>
2091 <source>Input Device</source>
2092 <translation>Пристрій вводу</translation>
2093 </message>
2094 <message>
2095 <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="156"/>
2096 <source>Profile</source>
2097 <translation>Профіль</translation>
2098 </message>
2099 <message>
2100 <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="196"/>
2101 <source>Save</source>
2102 <translation>Зберегти</translation>
2103 </message>
2104 <message>
2105 <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="212"/>
2106 <source>New</source>
2107 <translation>Новий</translation>
2108 </message>
2109 <message>
2110 <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="228"/>
2111 <source>Delete</source>
2112 <translation>Видалити</translation>
2113 </message>
2114 <message>
2115 <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="291"/>
2116 <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="1291"/>
2117 <source>Left Stick</source>
2118 <translation>Лівий міні-джойстик</translation>
2119 </message>
2120 <message>
2121 <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="349"/>
2122 <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="391"/>
2123 <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="925"/>
2124 <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="964"/>
2125 <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="2583"/>
2126 <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="2622"/>
2127 <source>Up</source>
2128 <translation>Вгору</translation>
2129 </message>
2130 <message>
2131 <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="422"/>
2132 <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="461"/>
2133 <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="995"/>
2134 <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="1034"/>
2135 <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="2069"/>
2136 <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="2653"/>
2137 <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="2692"/>
2138 <source>Left</source>
2139 <translation>Вліво</translation>
2140 </message>
2141 <message>
2142 <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="471"/>
2143 <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="510"/>
2144 <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="1044"/>
2145 <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="1083"/>
2146 <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="2118"/>
2147 <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="2702"/>
2148 <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="2741"/>
2149 <source>Right</source>
2150 <translation>Вправо</translation>
2151 </message>
2152 <message>
2153 <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="553"/>
2154 <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="592"/>
2155 <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="1126"/>
2156 <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="1165"/>
2157 <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="2784"/>
2158 <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="2823"/>
2159 <source>Down</source>
2160 <translation>Вниз</translation>
2161 </message>
2162 <message>
2163 <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="623"/>
2164 <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="662"/>
2165 <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="2854"/>
2166 <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="2893"/>
2167 <source>Pressed</source>
2168 <translation>Натиснення</translation>
2169 </message>
2170 <message>
2171 <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="672"/>
2172 <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="711"/>
2173 <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="2903"/>
2174 <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="2942"/>
2175 <source>Modifier</source>
2176 <translation>Модифікатор</translation>
2177 </message>
2178 <message>
2179 <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="721"/>
2180 <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="2952"/>
2181 <source>Range</source>
2182 <translation>Діапазон</translation>
2183 </message>
2184 <message>
2185 <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="754"/>
2186 <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="2985"/>
2187 <source>%</source>
2188 <translation>%</translation>
2189 </message>
2190 <message>
2191 <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="797"/>
2192 <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="3025"/>
2193 <source>Deadzone: 0%</source>
2194 <translation>Мертва зона: 0%</translation>
2195 </message>
2196 <message>
2197 <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="821"/>
2198 <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="3049"/>
2199 <source>Modifier Range: 0%</source>
2200 <translation>Діапазон модифікатора: 0%</translation>
2201 </message>
2202 <message>
2203 <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="867"/>
2204 <source>D-Pad</source>
2205 <translation>Кнопки напрямків</translation>
2206 </message>
2207 <message>
2208 <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="1251"/>
2209 <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="1290"/>
2210 <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="1296"/>
2211 <source>L</source>
2212 <translation>L</translation>
2213 </message>
2214 <message>
2215 <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="1306"/>
2216 <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="1345"/>
2217 <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="1288"/>
2218 <source>ZL</source>
2219 <translation>ZL</translation>
2220 </message>
2221 <message>
2222 <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="1426"/>
2223 <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="1465"/>
2224 <source>Minus</source>
2225 <translation>Мінус</translation>
2226 </message>
2227 <message>
2228 <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="1475"/>
2229 <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="1514"/>
2230 <source>Capture</source>
2231 <translation>Захоплення</translation>
2232 </message>
2233 <message>
2234 <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="1545"/>
2235 <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="1584"/>
2236 <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="1287"/>
2237 <source>Plus</source>
2238 <translation>Плюс</translation>
2239 </message>
2240 <message>
2241 <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="1594"/>
2242 <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="1633"/>
2243 <source>Home</source>
2244 <translation>Home</translation>
2245 </message>
2246 <message>
2247 <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="1698"/>
2248 <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="1737"/>
2249 <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="1290"/>
2250 <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="1297"/>
2251 <source>R</source>
2252 <translation>R</translation>
2253 </message>
2254 <message>
2255 <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="1753"/>
2256 <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="1792"/>
2257 <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="1289"/>
2258 <source>ZR</source>
2259 <translation>ZR</translation>
2260 </message>
2261 <message>
2262 <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="1873"/>
2263 <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="1912"/>
2264 <source>SL</source>
2265 <translation>SL</translation>
2266 </message>
2267 <message>
2268 <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="1922"/>
2269 <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="1961"/>
2270 <source>SR</source>
2271 <translation>SR</translation>
2272 </message>
2273 <message>
2274 <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="2030"/>
2275 <source>Motion 1</source>
2276 <translation>Рух 1</translation>
2277 </message>
2278 <message>
2279 <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="2079"/>
2280 <source>Motion 2</source>
2281 <translation>Рух 2</translation>
2282 </message>
2283 <message>
2284 <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="2170"/>
2285 <source>Face Buttons</source>
2286 <translation>Кнопки A/B/X/Y</translation>
2287 </message>
2288 <message>
2289 <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="2228"/>
2290 <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="2267"/>
2291 <source>X</source>
2292 <translation>X</translation>
2293 </message>
2294 <message>
2295 <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="2298"/>
2296 <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="2337"/>
2297 <source>Y</source>
2298 <translation>Y</translation>
2299 </message>
2300 <message>
2301 <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="2347"/>
2302 <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="2386"/>
2303 <source>A</source>
2304 <translation>A</translation>
2305 </message>
2306 <message>
2307 <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="2429"/>
2308 <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="2468"/>
2309 <source>B</source>
2310 <translation>B</translation>
2311 </message>
2312 <message>
2313 <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="2516"/>
2314 <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="1292"/>
2315 <source>Right Stick</source>
2316 <translation>Правий міні-джойстик</translation>
2317 </message>
2318 <message>
2319 <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="361"/>
2320 <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="434"/>
2321 <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="529"/>
2322 <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="624"/>
2323 <source>Clear</source>
2324 <translation>Очистити</translation>
2325 </message>
2326 <message>
2327 <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="363"/>
2328 <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="436"/>
2329 <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="533"/>
2330 <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="552"/>
2331 <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="626"/>
2332 <source>[not set]</source>
2333 <translation>[не задано]</translation>
2334 </message>
2335 <message>
2336 <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="366"/>
2337 <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="638"/>
2338 <source>Invert button</source>
2339 <translation>Інвертувати кнопку</translation>
2340 </message>
2341 <message>
2342 <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="372"/>
2343 <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="629"/>
2344 <source>Toggle button</source>
2345 <translation>Переключити кнопку</translation>
2346 </message>
2347 <message>
2348 <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="380"/>
2349 <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="580"/>
2350 <source>Invert axis</source>
2351 <translation>Інвертувати осі</translation>
2352 </message>
2353 <message>
2354 <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="386"/>
2355 <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="390"/>
2356 <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="443"/>
2357 <source>Set threshold</source>
2358 <translation>Встановити поріг</translation>
2359 </message>
2360 <message>
2361 <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="390"/>
2362 <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="443"/>
2363 <source>Choose a value between 0% and 100%</source>
2364 <translation>Оберіть значення між 0% і 100%</translation>
2365 </message>
2366 <message>
2367 <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="402"/>
2368 <source>Toggle axis</source>
2369 <translation>Переключити осі</translation>
2370 </message>
2371 <message>
2372 <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="439"/>
2373 <source>Set gyro threshold</source>
2374 <translation>Встановити поріг гіроскопа</translation>
2375 </message>
2376 <message>
2377 <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="485"/>
2378 <source>Map Analog Stick</source>
2379 <translation>Задати аналоговий міні-джойстик</translation>
2380 </message>
2381 <message>
2382 <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="486"/>
2383 <source>After pressing OK, first move your joystick horizontally, and then vertically.
2384To invert the axes, first move your joystick vertically, and then horizontally.</source>
2385 <translation>Після натискання на ОК, рухайте ваш міні-джойстик горизонтально, а потім вертикально.
2386Щоб інвертувати осі, спочатку рухайте ваш міні-джойстик вертикально, а потім горизонтально.</translation>
2387 </message>
2388 <message>
2389 <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="554"/>
2390 <source>Center axis</source>
2391 <translation>Центрувати осі</translation>
2392 </message>
2393 <message>
2394 <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="662"/>
2395 <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="1016"/>
2396 <source>Deadzone: %1%</source>
2397 <translation>Мертва зона: %1%</translation>
2398 </message>
2399 <message>
2400 <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="671"/>
2401 <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="1021"/>
2402 <source>Modifier Range: %1%</source>
2403 <translation>Діапазон модифікатора: %1%</translation>
2404 </message>
2405 <message>
2406 <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="697"/>
2407 <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="1046"/>
2408 <source>Pro Controller</source>
2409 <translation>Контролер Pro</translation>
2410 </message>
2411 <message>
2412 <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="1050"/>
2413 <source>Dual Joycons</source>
2414 <translation>Подвійні Joy-Con&apos;и</translation>
2415 </message>
2416 <message>
2417 <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="1054"/>
2418 <source>Left Joycon</source>
2419 <translation>Лівий Joy-Con</translation>
2420 </message>
2421 <message>
2422 <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="1058"/>
2423 <source>Right Joycon</source>
2424 <translation>Правий Joy-Con</translation>
2425 </message>
2426 <message>
2427 <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="1062"/>
2428 <source>Handheld</source>
2429 <translation>Портативний</translation>
2430 </message>
2431 <message>
2432 <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="1066"/>
2433 <source>GameCube Controller</source>
2434 <translation>Контролер GameCube</translation>
2435 </message>
2436 <message>
2437 <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="1075"/>
2438 <source>Poke Ball Plus</source>
2439 <translation>Poke Ball Plus</translation>
2440 </message>
2441 <message>
2442 <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="1079"/>
2443 <source>NES Controller</source>
2444 <translation>Контролер NES</translation>
2445 </message>
2446 <message>
2447 <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="1083"/>
2448 <source>SNES Controller</source>
2449 <translation>Контролер SNES</translation>
2450 </message>
2451 <message>
2452 <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="1087"/>
2453 <source>N64 Controller</source>
2454 <translation>Контролер N64</translation>
2455 </message>
2456 <message>
2457 <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="1091"/>
2458 <source>Sega Genesis</source>
2459 <translation>Sega Genesis</translation>
2460 </message>
2461 <message>
2462 <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="1295"/>
2463 <source>Start / Pause</source>
2464 <translation>Старт / Пауза</translation>
2465 </message>
2466 <message>
2467 <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="1298"/>
2468 <source>Z</source>
2469 <translation>Z</translation>
2470 </message>
2471 <message>
2472 <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="1299"/>
2473 <source>Control Stick</source>
2474 <translation>Міні-джойстик керування</translation>
2475 </message>
2476 <message>
2477 <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="1300"/>
2478 <source>C-Stick</source>
2479 <translation>C-Джойстик</translation>
2480 </message>
2481 <message>
2482 <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="1401"/>
2483 <source>Shake!</source>
2484 <translation>Потрусіть!</translation>
2485 </message>
2486 <message>
2487 <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="1403"/>
2488 <source>[waiting]</source>
2489 <translation>[очікування]</translation>
2490 </message>
2491 <message>
2492 <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="1485"/>
2493 <source>New Profile</source>
2494 <translation>Новий профіль</translation>
2495 </message>
2496 <message>
2497 <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="1485"/>
2498 <source>Enter a profile name:</source>
2499 <translation>Введіть ім&apos;я профілю:</translation>
2500 </message>
2501 <message>
2502 <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="1493"/>
2503 <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="1501"/>
2504 <source>Create Input Profile</source>
2505 <translation>Створити профіль контролю</translation>
2506 </message>
2507 <message>
2508 <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="1494"/>
2509 <source>The given profile name is not valid!</source>
2510 <translation>Задане ім&apos;я профілю недійсне!</translation>
2511 </message>
2512 <message>
2513 <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="1502"/>
2514 <source>Failed to create the input profile &quot;%1&quot;</source>
2515 <translation>Не вдалося створити профіль контролю &quot;%1&quot;</translation>
2516 </message>
2517 <message>
2518 <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="1522"/>
2519 <source>Delete Input Profile</source>
2520 <translation>Видалити профіль контролю</translation>
2521 </message>
2522 <message>
2523 <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="1523"/>
2524 <source>Failed to delete the input profile &quot;%1&quot;</source>
2525 <translation>Не вдалося видалити профіль контролю &quot;%1&quot;</translation>
2526 </message>
2527 <message>
2528 <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="1545"/>
2529 <source>Load Input Profile</source>
2530 <translation>Завантажити профіль контролю</translation>
2531 </message>
2532 <message>
2533 <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="1546"/>
2534 <source>Failed to load the input profile &quot;%1&quot;</source>
2535 <translation>Не вдалося завантажити профіль контролю &quot;%1&quot;</translation>
2536 </message>
2537 <message>
2538 <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="1565"/>
2539 <source>Save Input Profile</source>
2540 <translation>Зберегти профіль контролю</translation>
2541 </message>
2542 <message>
2543 <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="1566"/>
2544 <source>Failed to save the input profile &quot;%1&quot;</source>
2545 <translation>Не вдалося зберегти профіль контролю &quot;%1&quot;</translation>
2546 </message>
2547</context>
2548<context>
2549 <name>ConfigureInputProfileDialog</name>
2550 <message>
2551 <location filename="../../src/yuzu/configuration/configure_input_profile_dialog.ui" line="14"/>
2552 <source>Create Input Profile</source>
2553 <translation>Створити профіль контролю</translation>
2554 </message>
2555 <message>
2556 <location filename="../../src/yuzu/configuration/configure_input_profile_dialog.ui" line="40"/>
2557 <source>Clear</source>
2558 <translation>Очистити</translation>
2559 </message>
2560 <message>
2561 <location filename="../../src/yuzu/configuration/configure_input_profile_dialog.ui" line="47"/>
2562 <source>Defaults</source>
2563 <translation>За замовчуванням</translation>
2564 </message>
2565</context>
2566<context>
2567 <name>ConfigureMotionTouch</name>
2568 <message>
2569 <location filename="../../src/yuzu/configuration/configure_motion_touch.ui" line="6"/>
2570 <source>Configure Motion / Touch</source>
2571 <translation>Налаштування руху та сенсора</translation>
2572 </message>
2573 <message>
2574 <location filename="../../src/yuzu/configuration/configure_motion_touch.ui" line="15"/>
2575 <source>Touch</source>
2576 <translation>Сенсор</translation>
2577 </message>
2578 <message>
2579 <location filename="../../src/yuzu/configuration/configure_motion_touch.ui" line="23"/>
2580 <source>UDP Calibration:</source>
2581 <translation>Калібрація UDP:</translation>
2582 </message>
2583 <message>
2584 <location filename="../../src/yuzu/configuration/configure_motion_touch.ui" line="30"/>
2585 <source>(100, 50) - (1800, 850)</source>
2586 <translation>(100, 50) - (1800, 850)</translation>
2587 </message>
2588 <message>
2589 <location filename="../../src/yuzu/configuration/configure_motion_touch.ui" line="46"/>
2590 <location filename="../../src/yuzu/configuration/configure_motion_touch.ui" line="73"/>
2591 <location filename="../../src/yuzu/configuration/configure_motion_touch.cpp" line="243"/>
2592 <source>Configure</source>
2593 <translation>Налаштувати</translation>
2594 </message>
2595 <message>
2596 <location filename="../../src/yuzu/configuration/configure_motion_touch.ui" line="57"/>
2597 <source>Touch from button profile:</source>
2598 <translation>Торкніться з профілю кнопки:</translation>
2599 </message>
2600 <message>
2601 <location filename="../../src/yuzu/configuration/configure_motion_touch.ui" line="85"/>
2602 <source>CemuhookUDP Config</source>
2603 <translation>Налаштування CemuhookUDP</translation>
2604 </message>
2605 <message>
2606 <location filename="../../src/yuzu/configuration/configure_motion_touch.ui" line="91"/>
2607 <source>You may use any Cemuhook compatible UDP input source to provide motion and touch input.</source>
2608 <translation>Ви можете використовувати будь-яке сумісне з Cemuhook джерело UDP сигналу для руху і сенсора.</translation>
2609 </message>
2610 <message>
2611 <location filename="../../src/yuzu/configuration/configure_motion_touch.ui" line="134"/>
2612 <source>Server:</source>
2613 <translation>Сервер:</translation>
2614 </message>
2615 <message>
2616 <location filename="../../src/yuzu/configuration/configure_motion_touch.ui" line="161"/>
2617 <source>Port:</source>
2618 <translation>Порт:</translation>
2619 </message>
2620 <message>
2621 <location filename="../../src/yuzu/configuration/configure_motion_touch.ui" line="188"/>
2622 <source>Learn More</source>
2623 <translation>Дізнатися більше</translation>
2624 </message>
2625 <message>
2626 <location filename="../../src/yuzu/configuration/configure_motion_touch.ui" line="201"/>
2627 <location filename="../../src/yuzu/configuration/configure_motion_touch.cpp" line="266"/>
2628 <source>Test</source>
2629 <translation>Тест</translation>
2630 </message>
2631 <message>
2632 <location filename="../../src/yuzu/configuration/configure_motion_touch.ui" line="214"/>
2633 <source>Add Server</source>
2634 <translation>Додати сервер</translation>
2635 </message>
2636 <message>
2637 <location filename="../../src/yuzu/configuration/configure_motion_touch.ui" line="247"/>
2638 <source>Remove Server</source>
2639 <translation>Видалити сервер</translation>
2640 </message>
2641 <message>
2642 <location filename="../../src/yuzu/configuration/configure_motion_touch.cpp" line="87"/>
2643 <source>&lt;a href=&apos;https://yuzu-emu.org/wiki/using-a-controller-or-android-phone-for-motion-or-touch-input&apos;&gt;&lt;span style=&quot;text-decoration: underline; color:#039be5;&quot;&gt;Learn More&lt;/span&gt;&lt;/a&gt;</source>
2644 <translation>&lt;a href=&apos;https://yuzu-emu.org/wiki/using-a-controller-or-android-phone-for-motion-or-touch-input&apos;&gt;&lt;span style=&quot;text-decoration: underline; color:#039be5;&quot;&gt;Дізнатися більше&lt;/span&gt;&lt;/a&gt;</translation>
2645 </message>
2646 <message>
2647 <location filename="../../src/yuzu/configuration/configure_motion_touch.cpp" line="169"/>
2648 <source>%1:%2</source>
2649 <translation>%1:%2</translation>
2650 </message>
2651 <message>
2652 <location filename="../../src/yuzu/configuration/configure_motion_touch.cpp" line="174"/>
2653 <location filename="../../src/yuzu/configuration/configure_motion_touch.cpp" line="178"/>
2654 <location filename="../../src/yuzu/configuration/configure_motion_touch.cpp" line="182"/>
2655 <location filename="../../src/yuzu/configuration/configure_motion_touch.cpp" line="188"/>
2656 <location filename="../../src/yuzu/configuration/configure_motion_touch.cpp" line="194"/>
2657 <location filename="../../src/yuzu/configuration/configure_motion_touch.cpp" line="288"/>
2658 <source>yuzu</source>
2659 <translation>yuzu</translation>
2660 </message>
2661 <message>
2662 <location filename="../../src/yuzu/configuration/configure_motion_touch.cpp" line="174"/>
2663 <source>Port number has invalid characters</source>
2664 <translation>Номер порту містить неприпустимі символи</translation>
2665 </message>
2666 <message>
2667 <location filename="../../src/yuzu/configuration/configure_motion_touch.cpp" line="178"/>
2668 <source>Port has to be in range 0 and 65353</source>
2669 <translation>Порт повинен бути в районі від 0 до 65353</translation>
2670 </message>
2671 <message>
2672 <location filename="../../src/yuzu/configuration/configure_motion_touch.cpp" line="182"/>
2673 <source>IP address is not valid</source>
2674 <translation>IP-адреса недійсна</translation>
2675 </message>
2676 <message>
2677 <location filename="../../src/yuzu/configuration/configure_motion_touch.cpp" line="188"/>
2678 <source>This UDP server already exists</source>
2679 <translation>Цей UDP сервер уже існує</translation>
2680 </message>
2681 <message>
2682 <location filename="../../src/yuzu/configuration/configure_motion_touch.cpp" line="194"/>
2683 <source>Unable to add more than 8 servers</source>
2684 <translation>Неможливо додати більше 8 серверів</translation>
2685 </message>
2686 <message>
2687 <location filename="../../src/yuzu/configuration/configure_motion_touch.cpp" line="210"/>
2688 <source>Testing</source>
2689 <translation>Тестування</translation>
2690 </message>
2691 <message>
2692 <location filename="../../src/yuzu/configuration/configure_motion_touch.cpp" line="226"/>
2693 <source>Configuring</source>
2694 <translation>Налаштування</translation>
2695 </message>
2696 <message>
2697 <location filename="../../src/yuzu/configuration/configure_motion_touch.cpp" line="257"/>
2698 <source>Test Successful</source>
2699 <translation>Тест успішний</translation>
2700 </message>
2701 <message>
2702 <location filename="../../src/yuzu/configuration/configure_motion_touch.cpp" line="258"/>
2703 <source>Successfully received data from the server.</source>
2704 <translation>Успішно отримано інформацію із сервера</translation>
2705 </message>
2706 <message>
2707 <location filename="../../src/yuzu/configuration/configure_motion_touch.cpp" line="260"/>
2708 <source>Test Failed</source>
2709 <translation>Тест провалено</translation>
2710 </message>
2711 <message>
2712 <location filename="../../src/yuzu/configuration/configure_motion_touch.cpp" line="261"/>
2713 <source>Could not receive valid data from the server.&lt;br&gt;Please verify that the server is set up correctly and the address and port are correct.</source>
2714 <translation>Не вдалося отримати дійсні дані з сервера.&lt;br&gt;Переконайтеся, що сервер правильно налаштований, а також перевірте адресу та порт.</translation>
2715 </message>
2716 <message>
2717 <location filename="../../src/yuzu/configuration/configure_motion_touch.cpp" line="289"/>
2718 <source>UDP Test or calibration configuration is in progress.&lt;br&gt;Please wait for them to finish.</source>
2719 <translation>Тест UDP або калібрація в процесі.&lt;br&gt;Будь ласка, зачекайте завершення.</translation>
2720 </message>
2721</context>
2722<context>
2723 <name>ConfigureNetwork</name>
2724 <message>
2725 <location filename="../../src/yuzu/configuration/configure_network.ui" line="14"/>
2726 <source>Form</source>
2727 <translation>Форма</translation>
2728 </message>
2729 <message>
2730 <location filename="../../src/yuzu/configuration/configure_network.ui" line="17"/>
2731 <source>Network</source>
2732 <translation>Мережа</translation>
2733 </message>
2734 <message>
2735 <location filename="../../src/yuzu/configuration/configure_network.ui" line="25"/>
2736 <source>General</source>
2737 <translation>Загальні</translation>
2738 </message>
2739 <message>
2740 <location filename="../../src/yuzu/configuration/configure_network.ui" line="34"/>
2741 <source>Network Interface</source>
2742 <translation>Інтерфейс мережі</translation>
2743 </message>
2744 <message>
2745 <location filename="../../src/yuzu/configuration/configure_network.cpp" line="15"/>
2746 <source>None</source>
2747 <translation>Нічого</translation>
2748 </message>
2749</context>
2750<context>
2751 <name>ConfigurePerGame</name>
2752 <message>
2753 <location filename="../../src/yuzu/configuration/configure_per_game.ui" line="12"/>
2754 <source>Dialog</source>
2755 <translation>Діалог</translation>
2756 </message>
2757 <message>
2758 <location filename="../../src/yuzu/configuration/configure_per_game.ui" line="26"/>
2759 <source>Info</source>
2760 <translation>Інформація</translation>
2761 </message>
2762 <message>
2763 <location filename="../../src/yuzu/configuration/configure_per_game.ui" line="85"/>
2764 <source>Name</source>
2765 <translation>Назва</translation>
2766 </message>
2767 <message>
2768 <location filename="../../src/yuzu/configuration/configure_per_game.ui" line="92"/>
2769 <source>Title ID</source>
2770 <translation>Ідентифікатор гри</translation>
2771 </message>
2772 <message>
2773 <location filename="../../src/yuzu/configuration/configure_per_game.ui" line="129"/>
2774 <source>Filename</source>
2775 <translation>Ім&apos;я файлу</translation>
2776 </message>
2777 <message>
2778 <location filename="../../src/yuzu/configuration/configure_per_game.ui" line="156"/>
2779 <source>Format</source>
2780 <translation>Формат</translation>
2781 </message>
2782 <message>
2783 <location filename="../../src/yuzu/configuration/configure_per_game.ui" line="163"/>
2784 <source>Version</source>
2785 <translation>Версія</translation>
2786 </message>
2787 <message>
2788 <location filename="../../src/yuzu/configuration/configure_per_game.ui" line="170"/>
2789 <source>Size</source>
2790 <translation>Розмір</translation>
2791 </message>
2792 <message>
2793 <location filename="../../src/yuzu/configuration/configure_per_game.ui" line="177"/>
2794 <source>Developer</source>
2795 <translation>Розробник</translation>
2796 </message>
2797 <message>
2798 <location filename="../../src/yuzu/configuration/configure_per_game.cpp" line="57"/>
2799 <source>Add-Ons</source>
2800 <translation>Доповнення</translation>
2801 </message>
2802 <message>
2803 <location filename="../../src/yuzu/configuration/configure_per_game.cpp" line="58"/>
2804 <source>General</source>
2805 <translation>Загальні</translation>
2806 </message>
2807 <message>
2808 <location filename="../../src/yuzu/configuration/configure_per_game.cpp" line="59"/>
2809 <source>System</source>
2810 <translation>Система</translation>
2811 </message>
2812 <message>
2813 <location filename="../../src/yuzu/configuration/configure_per_game.cpp" line="60"/>
2814 <source>CPU</source>
2815 <translation>ЦП</translation>
2816 </message>
2817 <message>
2818 <location filename="../../src/yuzu/configuration/configure_per_game.cpp" line="61"/>
2819 <source>Graphics</source>
2820 <translation>Графіка</translation>
2821 </message>
2822 <message>
2823 <location filename="../../src/yuzu/configuration/configure_per_game.cpp" line="62"/>
2824 <source>Adv. Graphics</source>
2825 <translation>Розш. Графіка</translation>
2826 </message>
2827 <message>
2828 <location filename="../../src/yuzu/configuration/configure_per_game.cpp" line="63"/>
2829 <source>Audio</source>
2830 <translation>Аудіо</translation>
2831 </message>
2832 <message>
2833 <location filename="../../src/yuzu/configuration/configure_per_game.cpp" line="66"/>
2834 <source>Properties</source>
2835 <translation>Властивості</translation>
2836 </message>
2837 <message>
2838 <location filename="../../src/yuzu/configuration/configuration_shared.cpp" line="91"/>
2839 <source>Use global configuration (%1)</source>
2840 <translation>Використовувати глобальне налаштування (%1)</translation>
2841 </message>
2842</context>
2843<context>
2844 <name>ConfigurePerGameAddons</name>
2845 <message>
2846 <location filename="../../src/yuzu/configuration/configure_per_game_addons.ui" line="14"/>
2847 <source>Form</source>
2848 <translation>Форма</translation>
2849 </message>
2850 <message>
2851 <location filename="../../src/yuzu/configuration/configure_per_game_addons.ui" line="17"/>
2852 <source>Add-Ons</source>
2853 <translation>Доповнення</translation>
2854 </message>
2855 <message>
2856 <location filename="../../src/yuzu/configuration/configure_per_game_addons.cpp" line="46"/>
2857 <source>Patch Name</source>
2858 <translation>Назва патчу</translation>
2859 </message>
2860 <message>
2861 <location filename="../../src/yuzu/configuration/configure_per_game_addons.cpp" line="47"/>
2862 <source>Version</source>
2863 <translation>Версія</translation>
2864 </message>
2865</context>
2866<context>
2867 <name>ConfigureProfileManager</name>
2868 <message>
2869 <location filename="../../src/yuzu/configuration/configure_profile_manager.ui" line="14"/>
2870 <source>Form</source>
2871 <translation>Форма</translation>
2872 </message>
2873 <message>
2874 <location filename="../../src/yuzu/configuration/configure_profile_manager.ui" line="17"/>
2875 <source>Profiles</source>
2876 <translation>Профілі</translation>
2877 </message>
2878 <message>
2879 <location filename="../../src/yuzu/configuration/configure_profile_manager.ui" line="25"/>
2880 <source>Profile Manager</source>
2881 <translation>Керування профілями</translation>
2882 </message>
2883 <message>
2884 <location filename="../../src/yuzu/configuration/configure_profile_manager.ui" line="42"/>
2885 <source>Current User</source>
2886 <translation>Поточний користувач</translation>
2887 </message>
2888 <message>
2889 <location filename="../../src/yuzu/configuration/configure_profile_manager.ui" line="80"/>
2890 <source>Username</source>
2891 <translation>Ім&apos;я користувача</translation>
2892 </message>
2893 <message>
2894 <location filename="../../src/yuzu/configuration/configure_profile_manager.ui" line="110"/>
2895 <source>Set Image</source>
2896 <translation>Обрати зображення</translation>
2897 </message>
2898 <message>
2899 <location filename="../../src/yuzu/configuration/configure_profile_manager.ui" line="130"/>
2900 <source>Add</source>
2901 <translation>Додати</translation>
2902 </message>
2903 <message>
2904 <location filename="../../src/yuzu/configuration/configure_profile_manager.ui" line="140"/>
2905 <source>Rename</source>
2906 <translation>Перейменувати</translation>
2907 </message>
2908 <message>
2909 <location filename="../../src/yuzu/configuration/configure_profile_manager.ui" line="150"/>
2910 <source>Remove</source>
2911 <translation>Видалити</translation>
2912 </message>
2913 <message>
2914 <location filename="../../src/yuzu/configuration/configure_profile_manager.ui" line="162"/>
2915 <source>Profile management is available only when game is not running.</source>
2916 <translation>Керування профілями недоступне, поки запущена гра.</translation>
2917 </message>
2918 <message>
2919 <location filename="../../src/yuzu/configuration/configure_profile_manager.cpp" line="52"/>
2920 <source>%1
2921%2</source>
2922 <comment>%1 is the profile username, %2 is the formatted UUID (e.g. 00112233-4455-6677-8899-AABBCCDDEEFF))</comment>
2923 <translation>%1
2924%2</translation>
2925 </message>
2926 <message>
2927 <location filename="../../src/yuzu/configuration/configure_profile_manager.cpp" line="70"/>
2928 <source>Enter Username</source>
2929 <translation>Введіть ім&apos;я користувача</translation>
2930 </message>
2931 <message>
2932 <location filename="../../src/yuzu/configuration/configure_profile_manager.cpp" line="133"/>
2933 <source>Users</source>
2934 <translation>Користувачі</translation>
2935 </message>
2936 <message>
2937 <location filename="../../src/yuzu/configuration/configure_profile_manager.cpp" line="195"/>
2938 <source>Enter a username for the new user:</source>
2939 <translation>Введіть ім&apos;я користувача для нового профілю:</translation>
2940 </message>
2941 <message>
2942 <location filename="../../src/yuzu/configuration/configure_profile_manager.cpp" line="215"/>
2943 <source>Enter a new username:</source>
2944 <translation>Введіть нове ім&apos;я користувача:</translation>
2945 </message>
2946 <message>
2947 <location filename="../../src/yuzu/configuration/configure_profile_manager.cpp" line="240"/>
2948 <source>Confirm Delete</source>
2949 <translation>Підтвердити видалення</translation>
2950 </message>
2951 <message>
2952 <location filename="../../src/yuzu/configuration/configure_profile_manager.cpp" line="241"/>
2953 <source>You are about to delete user with name &quot;%1&quot;. Are you sure?</source>
2954 <translation>Ви збираєтеся видалити користувача &quot;%1&quot;. Ви впевнені?</translation>
2955 </message>
2956 <message>
2957 <location filename="../../src/yuzu/configuration/configure_profile_manager.cpp" line="268"/>
2958 <source>Select User Image</source>
2959 <translation>Оберіть зображення користувача</translation>
2960 </message>
2961 <message>
2962 <location filename="../../src/yuzu/configuration/configure_profile_manager.cpp" line="269"/>
2963 <source>JPEG Images (*.jpg *.jpeg)</source>
2964 <translation>Зображення JPEG (*.jpg *.jpeg)</translation>
2965 </message>
2966 <message>
2967 <location filename="../../src/yuzu/configuration/configure_profile_manager.cpp" line="278"/>
2968 <source>Error deleting image</source>
2969 <translation>Помилка під час видалення зображення</translation>
2970 </message>
2971 <message>
2972 <location filename="../../src/yuzu/configuration/configure_profile_manager.cpp" line="279"/>
2973 <source>Error occurred attempting to overwrite previous image at: %1.</source>
2974 <translation>Помилка під час спроби перезапису попереднього зображення в: %1.</translation>
2975 </message>
2976 <message>
2977 <location filename="../../src/yuzu/configuration/configure_profile_manager.cpp" line="287"/>
2978 <source>Error deleting file</source>
2979 <translation>Помилка під час видалення файлу</translation>
2980 </message>
2981 <message>
2982 <location filename="../../src/yuzu/configuration/configure_profile_manager.cpp" line="288"/>
2983 <source>Unable to delete existing file: %1.</source>
2984 <translation>Не вдалося видалити наявний файл: %1.</translation>
2985 </message>
2986 <message>
2987 <location filename="../../src/yuzu/configuration/configure_profile_manager.cpp" line="295"/>
2988 <source>Error creating user image directory</source>
2989 <translation>Помилка під час створення папки користувацьких зображень</translation>
2990 </message>
2991 <message>
2992 <location filename="../../src/yuzu/configuration/configure_profile_manager.cpp" line="296"/>
2993 <source>Unable to create directory %1 for storing user images.</source>
2994 <translation>Не вийшло створити папку %1 для зберігання зображень користувача.</translation>
2995 </message>
2996 <message>
2997 <location filename="../../src/yuzu/configuration/configure_profile_manager.cpp" line="301"/>
2998 <source>Error copying user image</source>
2999 <translation>Помилка під час копіювання зображення користувача</translation>
3000 </message>
3001 <message>
3002 <location filename="../../src/yuzu/configuration/configure_profile_manager.cpp" line="302"/>
3003 <source>Unable to copy image from %1 to %2</source>
3004 <translation>Не вийшло скопіювати зображення з %1 у %2</translation>
3005 </message>
3006 <message>
3007 <location filename="../../src/yuzu/configuration/configure_profile_manager.cpp" line="311"/>
3008 <source>Error resizing user image</source>
3009 <translation>Помилка під час зміни розміру зображення користувача</translation>
3010 </message>
3011 <message>
3012 <location filename="../../src/yuzu/configuration/configure_profile_manager.cpp" line="312"/>
3013 <source>Unable to resize image</source>
3014 <translation>Неможливо змінити розмір зображення</translation>
3015 </message>
3016</context>
3017<context>
3018 <name>ConfigureRingController</name>
3019 <message>
3020 <location filename="../../src/yuzu/configuration/configure_ringcon.ui" line="14"/>
3021 <source>Configure Ring Controller</source>
3022 <translation>Налаштування контролера Ring</translation>
3023 </message>
3024 <message>
3025 <location filename="../../src/yuzu/configuration/configure_ringcon.ui" line="26"/>
3026 <source>If you want to use this controller configure player 1 as right controller and player 2 as dual joycon before starting the game to allow this controller to be detected properly.</source>
3027 <translation>Якщо ви хочете використовувати цей контролер, налаштуйте гравця 1 як правий контролер, а гравця 2 як подвійний Joy-Соп перед початком гри, щоб цей контролер був виявлений правильно.</translation>
3028 </message>
3029 <message>
3030 <location filename="../../src/yuzu/configuration/configure_ringcon.ui" line="52"/>
3031 <source>Ring Sensor Parameters</source>
3032 <translation>Параметри сенсора Ring</translation>
3033 </message>
3034 <message>
3035 <location filename="../../src/yuzu/configuration/configure_ringcon.ui" line="84"/>
3036 <location filename="../../src/yuzu/configuration/configure_ringcon.ui" line="123"/>
3037 <source>Pull</source>
3038 <translation>Потягнути</translation>
3039 </message>
3040 <message>
3041 <location filename="../../src/yuzu/configuration/configure_ringcon.ui" line="133"/>
3042 <location filename="../../src/yuzu/configuration/configure_ringcon.ui" line="172"/>
3043 <source>Push</source>
3044 <translation>Натиснути</translation>
3045 </message>
3046 <message>
3047 <location filename="../../src/yuzu/configuration/configure_ringcon.ui" line="206"/>
3048 <source>Deadzone: 0%</source>
3049 <translation>Мертва зона: 0%</translation>
3050 </message>
3051 <message>
3052 <location filename="../../src/yuzu/configuration/configure_ringcon.ui" line="248"/>
3053 <source>Restore Defaults</source>
3054 <translation>За замовчуванням</translation>
3055 </message>
3056 <message>
3057 <location filename="../../src/yuzu/configuration/configure_ringcon.cpp" line="159"/>
3058 <source>Clear</source>
3059 <translation>Очистити</translation>
3060 </message>
3061 <message>
3062 <location filename="../../src/yuzu/configuration/configure_ringcon.cpp" line="161"/>
3063 <source>[not set]</source>
3064 <translation>[не задано]</translation>
3065 </message>
3066 <message>
3067 <location filename="../../src/yuzu/configuration/configure_ringcon.cpp" line="163"/>
3068 <source>Invert axis</source>
3069 <translation>Інвертувати осі</translation>
3070 </message>
3071 <message>
3072 <location filename="../../src/yuzu/configuration/configure_ringcon.cpp" line="182"/>
3073 <location filename="../../src/yuzu/configuration/configure_ringcon.cpp" line="238"/>
3074 <source>Deadzone: %1%</source>
3075 <translation>Мертва зона: %1%</translation>
3076 </message>
3077 <message>
3078 <location filename="../../src/yuzu/configuration/configure_ringcon.cpp" line="262"/>
3079 <source>[waiting]</source>
3080 <translation>[очікування]</translation>
3081 </message>
3082</context>
3083<context>
3084 <name>ConfigureSystem</name>
3085 <message>
3086 <location filename="../../src/yuzu/configuration/configure_system.ui" line="14"/>
3087 <source>Form</source>
3088 <translation>Форма</translation>
3089 </message>
3090 <message>
3091 <location filename="../../src/yuzu/configuration/configure_system.ui" line="17"/>
3092 <source>System</source>
3093 <translation>Система</translation>
3094 </message>
3095 <message>
3096 <location filename="../../src/yuzu/configuration/configure_system.ui" line="25"/>
3097 <source>System Settings</source>
3098 <translation>Налаштування системи</translation>
3099 </message>
3100 <message>
3101 <location filename="../../src/yuzu/configuration/configure_system.ui" line="33"/>
3102 <source>Region:</source>
3103 <translation>Регіон:</translation>
3104 </message>
3105 <message>
3106 <location filename="../../src/yuzu/configuration/configure_system.ui" line="41"/>
3107 <source>Auto</source>
3108 <translation>Авто</translation>
3109 </message>
3110 <message>
3111 <location filename="../../src/yuzu/configuration/configure_system.ui" line="46"/>
3112 <source>Default</source>
3113 <translation>За замовчуванням</translation>
3114 </message>
3115 <message>
3116 <location filename="../../src/yuzu/configuration/configure_system.ui" line="51"/>
3117 <source>CET</source>
3118 <translation>CET</translation>
3119 </message>
3120 <message>
3121 <location filename="../../src/yuzu/configuration/configure_system.ui" line="56"/>
3122 <source>CST6CDT</source>
3123 <translation>CST6CDT</translation>
3124 </message>
3125 <message>
3126 <location filename="../../src/yuzu/configuration/configure_system.ui" line="61"/>
3127 <source>Cuba</source>
3128 <translation>Куба</translation>
3129 </message>
3130 <message>
3131 <location filename="../../src/yuzu/configuration/configure_system.ui" line="66"/>
3132 <source>EET</source>
3133 <translation>EET</translation>
3134 </message>
3135 <message>
3136 <location filename="../../src/yuzu/configuration/configure_system.ui" line="71"/>
3137 <source>Egypt</source>
3138 <translation>Єгипет</translation>
3139 </message>
3140 <message>
3141 <location filename="../../src/yuzu/configuration/configure_system.ui" line="76"/>
3142 <source>Eire</source>
3143 <translation>Ейре</translation>
3144 </message>
3145 <message>
3146 <location filename="../../src/yuzu/configuration/configure_system.ui" line="81"/>
3147 <source>EST</source>
3148 <translation>EST</translation>
3149 </message>
3150 <message>
3151 <location filename="../../src/yuzu/configuration/configure_system.ui" line="86"/>
3152 <source>EST5EDT</source>
3153 <translation>EST5EDT</translation>
3154 </message>
3155 <message>
3156 <location filename="../../src/yuzu/configuration/configure_system.ui" line="91"/>
3157 <source>GB</source>
3158 <translation>GB</translation>
3159 </message>
3160 <message>
3161 <location filename="../../src/yuzu/configuration/configure_system.ui" line="96"/>
3162 <source>GB-Eire</source>
3163 <translation>GB-Ейре</translation>
3164 </message>
3165 <message>
3166 <location filename="../../src/yuzu/configuration/configure_system.ui" line="101"/>
3167 <source>GMT</source>
3168 <translation>GMT</translation>
3169 </message>
3170 <message>
3171 <location filename="../../src/yuzu/configuration/configure_system.ui" line="106"/>
3172 <source>GMT+0</source>
3173 <translation>GMT+0</translation>
3174 </message>
3175 <message>
3176 <location filename="../../src/yuzu/configuration/configure_system.ui" line="111"/>
3177 <source>GMT-0</source>
3178 <translation>GMT-0</translation>
3179 </message>
3180 <message>
3181 <location filename="../../src/yuzu/configuration/configure_system.ui" line="116"/>
3182 <source>GMT0</source>
3183 <translation>GMT0</translation>
3184 </message>
3185 <message>
3186 <location filename="../../src/yuzu/configuration/configure_system.ui" line="121"/>
3187 <source>Greenwich</source>
3188 <translation>Гринвіч</translation>
3189 </message>
3190 <message>
3191 <location filename="../../src/yuzu/configuration/configure_system.ui" line="126"/>
3192 <source>Hongkong</source>
3193 <translation>Гонконг</translation>
3194 </message>
3195 <message>
3196 <location filename="../../src/yuzu/configuration/configure_system.ui" line="131"/>
3197 <source>HST</source>
3198 <translation>HST</translation>
3199 </message>
3200 <message>
3201 <location filename="../../src/yuzu/configuration/configure_system.ui" line="136"/>
3202 <source>Iceland</source>
3203 <translation>Ісландія</translation>
3204 </message>
3205 <message>
3206 <location filename="../../src/yuzu/configuration/configure_system.ui" line="141"/>
3207 <source>Iran</source>
3208 <translation>Іран</translation>
3209 </message>
3210 <message>
3211 <location filename="../../src/yuzu/configuration/configure_system.ui" line="146"/>
3212 <source>Israel</source>
3213 <translation>Ізраїль</translation>
3214 </message>
3215 <message>
3216 <location filename="../../src/yuzu/configuration/configure_system.ui" line="151"/>
3217 <source>Jamaica</source>
3218 <translation>Ямайка</translation>
3219 </message>
3220 <message>
3221 <location filename="../../src/yuzu/configuration/configure_system.ui" line="156"/>
3222 <location filename="../../src/yuzu/configuration/configure_system.ui" line="275"/>
3223 <source>Japan</source>
3224 <translation>Японія</translation>
3225 </message>
3226 <message>
3227 <location filename="../../src/yuzu/configuration/configure_system.ui" line="161"/>
3228 <source>Kwajalein</source>
3229 <translation>Кваджалейн</translation>
3230 </message>
3231 <message>
3232 <location filename="../../src/yuzu/configuration/configure_system.ui" line="166"/>
3233 <source>Libya</source>
3234 <translation>Лівія</translation>
3235 </message>
3236 <message>
3237 <location filename="../../src/yuzu/configuration/configure_system.ui" line="171"/>
3238 <source>MET</source>
3239 <translation>MET</translation>
3240 </message>
3241 <message>
3242 <location filename="../../src/yuzu/configuration/configure_system.ui" line="176"/>
3243 <source>MST</source>
3244 <translation>MST</translation>
3245 </message>
3246 <message>
3247 <location filename="../../src/yuzu/configuration/configure_system.ui" line="181"/>
3248 <source>MST7MDT</source>
3249 <translation>MST7MDT</translation>
3250 </message>
3251 <message>
3252 <location filename="../../src/yuzu/configuration/configure_system.ui" line="186"/>
3253 <source>Navajo</source>
3254 <translation>Навахо</translation>
3255 </message>
3256 <message>
3257 <location filename="../../src/yuzu/configuration/configure_system.ui" line="191"/>
3258 <source>NZ</source>
3259 <translation>NZ</translation>
3260 </message>
3261 <message>
3262 <location filename="../../src/yuzu/configuration/configure_system.ui" line="196"/>
3263 <source>NZ-CHAT</source>
3264 <translation>NZ-CHAT</translation>
3265 </message>
3266 <message>
3267 <location filename="../../src/yuzu/configuration/configure_system.ui" line="201"/>
3268 <source>Poland</source>
3269 <translation>Польща</translation>
3270 </message>
3271 <message>
3272 <location filename="../../src/yuzu/configuration/configure_system.ui" line="206"/>
3273 <source>Portugal</source>
3274 <translation>Португалія</translation>
3275 </message>
3276 <message>
3277 <location filename="../../src/yuzu/configuration/configure_system.ui" line="211"/>
3278 <source>PRC</source>
3279 <translation>PRC</translation>
3280 </message>
3281 <message>
3282 <location filename="../../src/yuzu/configuration/configure_system.ui" line="216"/>
3283 <source>PST8PDT</source>
3284 <translation>PST8PDT</translation>
3285 </message>
3286 <message>
3287 <location filename="../../src/yuzu/configuration/configure_system.ui" line="221"/>
3288 <source>ROC</source>
3289 <translation>ROC</translation>
3290 </message>
3291 <message>
3292 <location filename="../../src/yuzu/configuration/configure_system.ui" line="226"/>
3293 <source>ROK</source>
3294 <translation>ROK</translation>
3295 </message>
3296 <message>
3297 <location filename="../../src/yuzu/configuration/configure_system.ui" line="231"/>
3298 <source>Singapore</source>
3299 <translation>Сінгапур</translation>
3300 </message>
3301 <message>
3302 <location filename="../../src/yuzu/configuration/configure_system.ui" line="236"/>
3303 <source>Turkey</source>
3304 <translation>Туреччина</translation>
3305 </message>
3306 <message>
3307 <location filename="../../src/yuzu/configuration/configure_system.ui" line="241"/>
3308 <source>UCT</source>
3309 <translation>UCT</translation>
3310 </message>
3311 <message>
3312 <location filename="../../src/yuzu/configuration/configure_system.ui" line="246"/>
3313 <source>Universal</source>
3314 <translation>Універсальний</translation>
3315 </message>
3316 <message>
3317 <location filename="../../src/yuzu/configuration/configure_system.ui" line="251"/>
3318 <source>UTC</source>
3319 <translation>UTC</translation>
3320 </message>
3321 <message>
3322 <location filename="../../src/yuzu/configuration/configure_system.ui" line="256"/>
3323 <source>W-SU</source>
3324 <translation>W-SU</translation>
3325 </message>
3326 <message>
3327 <location filename="../../src/yuzu/configuration/configure_system.ui" line="261"/>
3328 <source>WET</source>
3329 <translation>WET</translation>
3330 </message>
3331 <message>
3332 <location filename="../../src/yuzu/configuration/configure_system.ui" line="266"/>
3333 <source>Zulu</source>
3334 <translation>Зулуси</translation>
3335 </message>
3336 <message>
3337 <location filename="../../src/yuzu/configuration/configure_system.ui" line="280"/>
3338 <source>USA</source>
3339 <translation>США</translation>
3340 </message>
3341 <message>
3342 <location filename="../../src/yuzu/configuration/configure_system.ui" line="285"/>
3343 <source>Europe</source>
3344 <translation>Європа</translation>
3345 </message>
3346 <message>
3347 <location filename="../../src/yuzu/configuration/configure_system.ui" line="290"/>
3348 <source>Australia</source>
3349 <translation>Австралія</translation>
3350 </message>
3351 <message>
3352 <location filename="../../src/yuzu/configuration/configure_system.ui" line="295"/>
3353 <source>China</source>
3354 <translation>Китай</translation>
3355 </message>
3356 <message>
3357 <location filename="../../src/yuzu/configuration/configure_system.ui" line="300"/>
3358 <source>Korea</source>
3359 <translation>Корея</translation>
3360 </message>
3361 <message>
3362 <location filename="../../src/yuzu/configuration/configure_system.ui" line="305"/>
3363 <source>Taiwan</source>
3364 <translation>Тайвань</translation>
3365 </message>
3366 <message>
3367 <location filename="../../src/yuzu/configuration/configure_system.ui" line="313"/>
3368 <source>Time Zone:</source>
3369 <translation>Часовий пояс:</translation>
3370 </message>
3371 <message>
3372 <location filename="../../src/yuzu/configuration/configure_system.ui" line="320"/>
3373 <source>Note: this can be overridden when region setting is auto-select</source>
3374 <translation>Примітка: може бути перезаписано якщо регіон вибирається автоматично</translation>
3375 </message>
3376 <message>
3377 <location filename="../../src/yuzu/configuration/configure_system.ui" line="324"/>
3378 <source>Japanese (日本語)</source>
3379 <translation>Японська (日本語)</translation>
3380 </message>
3381 <message>
3382 <location filename="../../src/yuzu/configuration/configure_system.ui" line="329"/>
3383 <source>English</source>
3384 <translation>Англійська (English)</translation>
3385 </message>
3386 <message>
3387 <location filename="../../src/yuzu/configuration/configure_system.ui" line="334"/>
3388 <source>French (français)</source>
3389 <translation>Французька (français)</translation>
3390 </message>
3391 <message>
3392 <location filename="../../src/yuzu/configuration/configure_system.ui" line="339"/>
3393 <source>German (Deutsch)</source>
3394 <translation>Німецька (Deutsch)</translation>
3395 </message>
3396 <message>
3397 <location filename="../../src/yuzu/configuration/configure_system.ui" line="344"/>
3398 <source>Italian (italiano)</source>
3399 <translation>Італійська (italiano)</translation>
3400 </message>
3401 <message>
3402 <location filename="../../src/yuzu/configuration/configure_system.ui" line="349"/>
3403 <source>Spanish (español)</source>
3404 <translation>Іспанська (español)</translation>
3405 </message>
3406 <message>
3407 <location filename="../../src/yuzu/configuration/configure_system.ui" line="354"/>
3408 <source>Chinese</source>
3409 <translation>Китайська</translation>
3410 </message>
3411 <message>
3412 <location filename="../../src/yuzu/configuration/configure_system.ui" line="359"/>
3413 <source>Korean (한국어)</source>
3414 <translation>Корейська (한국어)</translation>
3415 </message>
3416 <message>
3417 <location filename="../../src/yuzu/configuration/configure_system.ui" line="364"/>
3418 <source>Dutch (Nederlands)</source>
3419 <translation>Голландська (Nederlands)</translation>
3420 </message>
3421 <message>
3422 <location filename="../../src/yuzu/configuration/configure_system.ui" line="369"/>
3423 <source>Portuguese (português)</source>
3424 <translation>Португальська (português)</translation>
3425 </message>
3426 <message>
3427 <location filename="../../src/yuzu/configuration/configure_system.ui" line="374"/>
3428 <source>Russian (Русский)</source>
3429 <translation>Російська (Русский)</translation>
3430 </message>
3431 <message>
3432 <location filename="../../src/yuzu/configuration/configure_system.ui" line="379"/>
3433 <source>Taiwanese</source>
3434 <translation>Тайванська</translation>
3435 </message>
3436 <message>
3437 <location filename="../../src/yuzu/configuration/configure_system.ui" line="384"/>
3438 <source>British English</source>
3439 <translation>Британська Англійська</translation>
3440 </message>
3441 <message>
3442 <location filename="../../src/yuzu/configuration/configure_system.ui" line="389"/>
3443 <source>Canadian French</source>
3444 <translation>Канадська Французька</translation>
3445 </message>
3446 <message>
3447 <location filename="../../src/yuzu/configuration/configure_system.ui" line="394"/>
3448 <source>Latin American Spanish</source>
3449 <translation>Латиноамериканська Іспанська</translation>
3450 </message>
3451 <message>
3452 <location filename="../../src/yuzu/configuration/configure_system.ui" line="399"/>
3453 <source>Simplified Chinese</source>
3454 <translation>Спрощена Китайська</translation>
3455 </message>
3456 <message>
3457 <location filename="../../src/yuzu/configuration/configure_system.ui" line="404"/>
3458 <source>Traditional Chinese (正體中文)</source>
3459 <translation>Традиційна Китайська (正體中文)</translation>
3460 </message>
3461 <message>
3462 <location filename="../../src/yuzu/configuration/configure_system.ui" line="409"/>
3463 <source>Brazilian Portuguese (português do Brasil)</source>
3464 <translation>Бразильська Португальська (português do Brasil)</translation>
3465 </message>
3466 <message>
3467 <location filename="../../src/yuzu/configuration/configure_system.ui" line="417"/>
3468 <source>Custom RTC</source>
3469 <translation>Користувацький RTC</translation>
3470 </message>
3471 <message>
3472 <location filename="../../src/yuzu/configuration/configure_system.ui" line="424"/>
3473 <source>Language</source>
3474 <translation>Мова</translation>
3475 </message>
3476 <message>
3477 <location filename="../../src/yuzu/configuration/configure_system.ui" line="431"/>
3478 <source>RNG Seed</source>
3479 <translation>Сід RNG</translation>
3480 </message>
3481 <message>
3482 <location filename="../../src/yuzu/configuration/configure_system.ui" line="439"/>
3483 <source>Mono</source>
3484 <translation>Моно</translation>
3485 </message>
3486 <message>
3487 <location filename="../../src/yuzu/configuration/configure_system.ui" line="444"/>
3488 <source>Stereo</source>
3489 <translation>Стерео</translation>
3490 </message>
3491 <message>
3492 <location filename="../../src/yuzu/configuration/configure_system.ui" line="449"/>
3493 <source>Surround</source>
3494 <translation>Об&apos;ємний звук</translation>
3495 </message>
3496 <message>
3497 <location filename="../../src/yuzu/configuration/configure_system.ui" line="457"/>
3498 <source>Console ID:</source>
3499 <translation>Ідентифікатор консолі:</translation>
3500 </message>
3501 <message>
3502 <location filename="../../src/yuzu/configuration/configure_system.ui" line="464"/>
3503 <source>Sound output mode</source>
3504 <translation>Режим виводу звуку</translation>
3505 </message>
3506 <message>
3507 <location filename="../../src/yuzu/configuration/configure_system.ui" line="512"/>
3508 <source>Regenerate</source>
3509 <translation>Перегенерувати</translation>
3510 </message>
3511 <message>
3512 <location filename="../../src/yuzu/configuration/configure_system.ui" line="537"/>
3513 <source>System settings are available only when game is not running.</source>
3514 <translation>Налаштування системи доступні тільки тоді, коли гру не запущено.</translation>
3515 </message>
3516 <message>
3517 <location filename="../../src/yuzu/configuration/configure_system.cpp" line="161"/>
3518 <source>This will replace your current virtual Switch with a new one. Your current virtual Switch will not be recoverable. This might have unexpected effects in games. This might fail, if you use an outdated config savegame. Continue?</source>
3519 <translation>Це замінить ваш поточний віртуальний Switch новим. Ваш поточний віртуальний Switch буде безповоротно втрачено. Це може мати несподівані наслідки в іграх. Може не спрацювати, якщо ви використовуєте застарілу конфігурацію збережених ігор. Продовжити?</translation>
3520 </message>
3521 <message>
3522 <location filename="../../src/yuzu/configuration/configure_system.cpp" line="165"/>
3523 <source>Warning</source>
3524 <translation>Увага</translation>
3525 </message>
3526 <message>
3527 <location filename="../../src/yuzu/configuration/configure_system.cpp" line="173"/>
3528 <source>Console ID: 0x%1</source>
3529 <translation>Ідентифікатор консолі: 0x%1</translation>
3530 </message>
3531</context>
3532<context>
3533 <name>ConfigureTas</name>
3534 <message>
3535 <location filename="../../src/yuzu/configuration/configure_tas.ui" line="11"/>
3536 <source>TAS</source>
3537 <translation>TAS</translation>
3538 </message>
3539 <message>
3540 <location filename="../../src/yuzu/configuration/configure_tas.ui" line="17"/>
3541 <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Reads controller input from scripts in the same format as TAS-nx scripts.&lt;br/&gt;For a more detailed explanation, please consult the &lt;a href=&quot;https://yuzu-emu.org/help/feature/tas/&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#039be5;&quot;&gt;help page&lt;/span&gt;&lt;/a&gt; on the yuzu website.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
3542 <translation>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Зчитує вхідні дані контролера зі скриптів у тому ж форматі, що і скрипти TAS-nx.&lt;br/&gt;Для більш детального пояснення зверніться до &lt;a href=&quot;https://yuzu-emu.org/help/feature/tas/&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#039be5;&quot;&gt;сторінки допомоги&lt;/span&gt;&lt;/a&gt; на сайті yuzu.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</translation>
3543 </message>
3544 <message>
3545 <location filename="../../src/yuzu/configuration/configure_tas.ui" line="27"/>
3546 <source>To check which hotkeys control the playback/recording, please refer to the Hotkey settings (Configure -&gt; General -&gt; Hotkeys).</source>
3547 <translation>Щоб перевірити, які гарячі клавіші керують відтворенням/записом, зверніться до налаштувань гарячих клавіш (Налаштування - Загальні -&gt; Гарячі клавіші).</translation>
3548 </message>
3549 <message>
3550 <location filename="../../src/yuzu/configuration/configure_tas.ui" line="37"/>
3551 <source>WARNING: This is an experimental feature.&lt;br/&gt;It will not play back scripts frame perfectly with the current, imperfect syncing method.</source>
3552 <translation>ПОПЕРЕДЖЕННЯ: Це експериментальна функція.&lt;br/&gt;Вона не буде ідеально відтворювати кадри сценаріїв за поточного недосконалого методу синхронізації.</translation>
3553 </message>
3554 <message>
3555 <location filename="../../src/yuzu/configuration/configure_tas.ui" line="54"/>
3556 <source>Settings</source>
3557 <translation>Налаштування</translation>
3558 </message>
3559 <message>
3560 <location filename="../../src/yuzu/configuration/configure_tas.ui" line="60"/>
3561 <source>Enable TAS features</source>
3562 <translation>Увімкнути функції TAS</translation>
3563 </message>
3564 <message>
3565 <location filename="../../src/yuzu/configuration/configure_tas.ui" line="67"/>
3566 <source>Loop script</source>
3567 <translation>Зациклити скрипт</translation>
3568 </message>
3569 <message>
3570 <location filename="../../src/yuzu/configuration/configure_tas.ui" line="77"/>
3571 <source>Pause execution during loads</source>
3572 <translation>Призупинити виконання під час завантаження</translation>
3573 </message>
3574 <message>
3575 <location filename="../../src/yuzu/configuration/configure_tas.ui" line="91"/>
3576 <source>Script Directory</source>
3577 <translation>Папка для скриптів</translation>
3578 </message>
3579 <message>
3580 <location filename="../../src/yuzu/configuration/configure_tas.ui" line="97"/>
3581 <source>Path</source>
3582 <translation>Шлях</translation>
3583 </message>
3584 <message>
3585 <location filename="../../src/yuzu/configuration/configure_tas.ui" line="104"/>
3586 <source>...</source>
3587 <translation>...</translation>
3588 </message>
3589</context>
3590<context>
3591 <name>ConfigureTasDialog</name>
3592 <message>
3593 <location filename="../../src/yuzu/configuration/configure_tas.cpp" line="19"/>
3594 <source>TAS Configuration</source>
3595 <translation>Налаштування TAS</translation>
3596 </message>
3597 <message>
3598 <location filename="../../src/yuzu/configuration/configure_tas.cpp" line="50"/>
3599 <source>Select TAS Load Directory...</source>
3600 <translation>Обрати папку завантаження TAS...</translation>
3601 </message>
3602</context>
3603<context>
3604 <name>ConfigureTouchFromButton</name>
3605 <message>
3606 <location filename="../../src/yuzu/configuration/configure_touch_from_button.ui" line="14"/>
3607 <source>Configure Touchscreen Mappings</source>
3608 <translation>Налаштування відображення сенсорного екрана</translation>
3609 </message>
3610 <message>
3611 <location filename="../../src/yuzu/configuration/configure_touch_from_button.ui" line="22"/>
3612 <source>Mapping:</source>
3613 <translation>Прив&apos;язки:</translation>
3614 </message>
3615 <message>
3616 <location filename="../../src/yuzu/configuration/configure_touch_from_button.ui" line="48"/>
3617 <source>New</source>
3618 <translation>Новий</translation>
3619 </message>
3620 <message>
3621 <location filename="../../src/yuzu/configuration/configure_touch_from_button.ui" line="61"/>
3622 <source>Delete</source>
3623 <translation>Видалити</translation>
3624 </message>
3625 <message>
3626 <location filename="../../src/yuzu/configuration/configure_touch_from_button.ui" line="74"/>
3627 <source>Rename</source>
3628 <translation>Перейменувати</translation>
3629 </message>
3630 <message>
3631 <location filename="../../src/yuzu/configuration/configure_touch_from_button.ui" line="92"/>
3632 <source>Click the bottom area to add a point, then press a button to bind.
3633Drag points to change position, or double-click table cells to edit values.</source>
3634 <translation>Натисніть на нижній області, щоб додати точку, після чого натисніть кнопку для прив&apos;язки.
3635Перетягніть точки, щоб змінити позицію, або натисніть двічі на комірки таблиці для зміни значень.</translation>
3636 </message>
3637 <message>
3638 <location filename="../../src/yuzu/configuration/configure_touch_from_button.ui" line="116"/>
3639 <source>Delete Point</source>
3640 <translation>Видалити точку</translation>
3641 </message>
3642 <message>
3643 <location filename="../../src/yuzu/configuration/configure_touch_from_button.cpp" line="78"/>
3644 <source>Button</source>
3645 <translation>Кнопка</translation>
3646 </message>
3647 <message>
3648 <location filename="../../src/yuzu/configuration/configure_touch_from_button.cpp" line="78"/>
3649 <source>X</source>
3650 <comment>X axis</comment>
3651 <translation>X</translation>
3652 </message>
3653 <message>
3654 <location filename="../../src/yuzu/configuration/configure_touch_from_button.cpp" line="78"/>
3655 <source>Y</source>
3656 <comment>Y axis</comment>
3657 <translation>Y</translation>
3658 </message>
3659 <message>
3660 <location filename="../../src/yuzu/configuration/configure_touch_from_button.cpp" line="195"/>
3661 <source>New Profile</source>
3662 <translation>Новий профіль</translation>
3663 </message>
3664 <message>
3665 <location filename="../../src/yuzu/configuration/configure_touch_from_button.cpp" line="195"/>
3666 <source>Enter the name for the new profile.</source>
3667 <translation>Введіть ім&apos;я вашого нового профілю.</translation>
3668 </message>
3669 <message>
3670 <location filename="../../src/yuzu/configuration/configure_touch_from_button.cpp" line="206"/>
3671 <source>Delete Profile</source>
3672 <translation>Видалити профіль</translation>
3673 </message>
3674 <message>
3675 <location filename="../../src/yuzu/configuration/configure_touch_from_button.cpp" line="206"/>
3676 <source>Delete profile %1?</source>
3677 <translation>Видалити профіль %1?</translation>
3678 </message>
3679 <message>
3680 <location filename="../../src/yuzu/configuration/configure_touch_from_button.cpp" line="219"/>
3681 <source>Rename Profile</source>
3682 <translation>Перейменувати профіль</translation>
3683 </message>
3684 <message>
3685 <location filename="../../src/yuzu/configuration/configure_touch_from_button.cpp" line="219"/>
3686 <source>New name:</source>
3687 <translation>Нова назва:</translation>
3688 </message>
3689 <message>
3690 <location filename="../../src/yuzu/configuration/configure_touch_from_button.cpp" line="231"/>
3691 <source>[press key]</source>
3692 <translation>[натисніть клавішу]</translation>
3693 </message>
3694</context>
3695<context>
3696 <name>ConfigureTouchscreenAdvanced</name>
3697 <message>
3698 <location filename="../../src/yuzu/configuration/configure_touchscreen_advanced.ui" line="14"/>
3699 <source>Configure Touchscreen</source>
3700 <translation>Налаштування сенсорного екрана</translation>
3701 </message>
3702 <message>
3703 <location filename="../../src/yuzu/configuration/configure_touchscreen_advanced.ui" line="26"/>
3704 <source>Warning: The settings in this page affect the inner workings of yuzu&apos;s emulated touchscreen. Changing them may result in undesirable behavior, such as the touchscreen partially or not working. You should only use this page if you know what you are doing.</source>
3705 <translation>Увага: Налаштування на цій сторінці впливають на внутрішню роботу емульованого сенсорного екрана yuzu. Їх зміна може призвести до небажаної поведінки, як часткова або повна непрацездатність сенсорного екрана. Використовуйте цю сторінку лише якщо ви знаєте, що робите.</translation>
3706 </message>
3707 <message>
3708 <location filename="../../src/yuzu/configuration/configure_touchscreen_advanced.ui" line="52"/>
3709 <source>Touch Parameters</source>
3710 <translation>Параметри сенсора</translation>
3711 </message>
3712 <message>
3713 <location filename="../../src/yuzu/configuration/configure_touchscreen_advanced.ui" line="71"/>
3714 <source>Touch Diameter Y</source>
3715 <translation>Діаметр сенсора Y</translation>
3716 </message>
3717 <message>
3718 <location filename="../../src/yuzu/configuration/configure_touchscreen_advanced.ui" line="91"/>
3719 <source>Touch Diameter X</source>
3720 <translation>Діаметр сенсора X</translation>
3721 </message>
3722 <message>
3723 <location filename="../../src/yuzu/configuration/configure_touchscreen_advanced.ui" line="98"/>
3724 <source>Rotational Angle</source>
3725 <translation>Кут повороту</translation>
3726 </message>
3727 <message>
3728 <location filename="../../src/yuzu/configuration/configure_touchscreen_advanced.ui" line="132"/>
3729 <source>Restore Defaults</source>
3730 <translation>За замовчуванням</translation>
3731 </message>
3732</context>
3733<context>
3734 <name>ConfigureUI</name>
3735 <message>
3736 <location filename="../../src/yuzu/configuration/configure_ui.cpp" line="20"/>
3737 <location filename="../../src/yuzu/configuration/configure_ui.cpp" line="28"/>
3738 <location filename="../../src/yuzu/configuration/configure_ui.cpp" line="40"/>
3739 <source>None</source>
3740 <translation>Нічого</translation>
3741 </message>
3742 <message>
3743 <location filename="../../src/yuzu/configuration/configure_ui.cpp" line="21"/>
3744 <source>Small (32x32)</source>
3745 <translation>Маленький (32х32)</translation>
3746 </message>
3747 <message>
3748 <location filename="../../src/yuzu/configuration/configure_ui.cpp" line="22"/>
3749 <source>Standard (64x64)</source>
3750 <translation>Стандартний (64х64)</translation>
3751 </message>
3752 <message>
3753 <location filename="../../src/yuzu/configuration/configure_ui.cpp" line="23"/>
3754 <source>Large (128x128)</source>
3755 <translation>Великий (128х128)</translation>
3756 </message>
3757 <message>
3758 <location filename="../../src/yuzu/configuration/configure_ui.cpp" line="24"/>
3759 <source>Full Size (256x256)</source>
3760 <translation>Повнорозмірний (256х256)</translation>
3761 </message>
3762 <message>
3763 <location filename="../../src/yuzu/configuration/configure_ui.cpp" line="29"/>
3764 <source>Small (24x24)</source>
3765 <translation>Маленький (24х24)</translation>
3766 </message>
3767 <message>
3768 <location filename="../../src/yuzu/configuration/configure_ui.cpp" line="30"/>
3769 <source>Standard (48x48)</source>
3770 <translation>Стандартний (48х48)</translation>
3771 </message>
3772 <message>
3773 <location filename="../../src/yuzu/configuration/configure_ui.cpp" line="31"/>
3774 <source>Large (72x72)</source>
3775 <translation>Великий (72х72)</translation>
3776 </message>
3777 <message>
3778 <location filename="../../src/yuzu/configuration/configure_ui.cpp" line="36"/>
3779 <source>Filename</source>
3780 <translation>Ім&apos;я файлу</translation>
3781 </message>
3782 <message>
3783 <location filename="../../src/yuzu/configuration/configure_ui.cpp" line="37"/>
3784 <source>Filetype</source>
3785 <translation>Тип файлу</translation>
3786 </message>
3787 <message>
3788 <location filename="../../src/yuzu/configuration/configure_ui.cpp" line="38"/>
3789 <source>Title ID</source>
3790 <translation>Ідентифікатор гри</translation>
3791 </message>
3792 <message>
3793 <location filename="../../src/yuzu/configuration/configure_ui.cpp" line="39"/>
3794 <source>Title Name</source>
3795 <translation>Назва гри</translation>
3796 </message>
3797</context>
3798<context>
3799 <name>ConfigureUi</name>
3800 <message>
3801 <location filename="../../src/yuzu/configuration/configure_ui.ui" line="14"/>
3802 <source>Form</source>
3803 <translation>Форма</translation>
3804 </message>
3805 <message>
3806 <location filename="../../src/yuzu/configuration/configure_ui.ui" line="17"/>
3807 <source>UI</source>
3808 <translation>Інтерфейс</translation>
3809 </message>
3810 <message>
3811 <location filename="../../src/yuzu/configuration/configure_ui.ui" line="23"/>
3812 <source>General</source>
3813 <translation>Загальні</translation>
3814 </message>
3815 <message>
3816 <location filename="../../src/yuzu/configuration/configure_ui.ui" line="31"/>
3817 <source>Note: Changing language will apply your configuration.</source>
3818 <translation>Примітка: Зміна мови призведе до застосування налаштувань.</translation>
3819 </message>
3820 <message>
3821 <location filename="../../src/yuzu/configuration/configure_ui.ui" line="43"/>
3822 <source>Interface language:</source>
3823 <translation>Мова інтерфейсу:</translation>
3824 </message>
3825 <message>
3826 <location filename="../../src/yuzu/configuration/configure_ui.ui" line="57"/>
3827 <source>Theme:</source>
3828 <translation>Тема:</translation>
3829 </message>
3830 <message>
3831 <location filename="../../src/yuzu/configuration/configure_ui.ui" line="74"/>
3832 <source>Game List</source>
3833 <translation>Список ігор</translation>
3834 </message>
3835 <message>
3836 <location filename="../../src/yuzu/configuration/configure_ui.ui" line="82"/>
3837 <source>Show Compatibility List</source>
3838 <translation>Показати список сумісності</translation>
3839 </message>
3840 <message>
3841 <location filename="../../src/yuzu/configuration/configure_ui.ui" line="89"/>
3842 <source>Show Add-Ons Column</source>
3843 <translation>Показувати стовпець доповнень</translation>
3844 </message>
3845 <message>
3846 <location filename="../../src/yuzu/configuration/configure_ui.ui" line="98"/>
3847 <source>Game Icon Size:</source>
3848 <translation>Розмір іконки гри:</translation>
3849 </message>
3850 <message>
3851 <location filename="../../src/yuzu/configuration/configure_ui.ui" line="112"/>
3852 <source>Folder Icon Size:</source>
3853 <translation>Розмір іконки папки:</translation>
3854 </message>
3855 <message>
3856 <location filename="../../src/yuzu/configuration/configure_ui.ui" line="126"/>
3857 <source>Row 1 Text:</source>
3858 <translation>Текст 1-го рядку:</translation>
3859 </message>
3860 <message>
3861 <location filename="../../src/yuzu/configuration/configure_ui.ui" line="140"/>
3862 <source>Row 2 Text:</source>
3863 <translation>Текст 2-го рядку:</translation>
3864 </message>
3865 <message>
3866 <location filename="../../src/yuzu/configuration/configure_ui.ui" line="157"/>
3867 <source>Screenshots</source>
3868 <translation>Знімки екрану</translation>
3869 </message>
3870 <message>
3871 <location filename="../../src/yuzu/configuration/configure_ui.ui" line="165"/>
3872 <source>Ask Where To Save Screenshots (Windows Only)</source>
3873 <translation>Запитувати куди зберігати знімки екрану (Тільки для Windows)</translation>
3874 </message>
3875 <message>
3876 <location filename="../../src/yuzu/configuration/configure_ui.ui" line="174"/>
3877 <source>Screenshots Path: </source>
3878 <translation>Папка для знімків екрану:</translation>
3879 </message>
3880 <message>
3881 <location filename="../../src/yuzu/configuration/configure_ui.ui" line="184"/>
3882 <source>...</source>
3883 <translation>...</translation>
3884 </message>
3885 <message>
3886 <location filename="../../src/yuzu/configuration/configure_ui.cpp" line="94"/>
3887 <source>Select Screenshots Path...</source>
3888 <translation>Виберіть папку для знімків екрану...</translation>
3889 </message>
3890 <message>
3891 <location filename="../../src/yuzu/configuration/configure_ui.cpp" line="219"/>
3892 <source>&lt;System&gt;</source>
3893 <translation>&lt;System&gt;</translation>
3894 </message>
3895</context>
3896<context>
3897 <name>ConfigureVibration</name>
3898 <message>
3899 <location filename="../../src/yuzu/configuration/configure_vibration.ui" line="14"/>
3900 <source>Configure Vibration</source>
3901 <translation>Налаштування вібрації</translation>
3902 </message>
3903 <message>
3904 <location filename="../../src/yuzu/configuration/configure_vibration.ui" line="23"/>
3905 <source>Press any controller button to vibrate the controller.</source>
3906 <translation>Натисніть будь-яку кнопку контролера, щоб викликати вібрацію.</translation>
3907 </message>
3908 <message>
3909 <location filename="../../src/yuzu/configuration/configure_vibration.ui" line="30"/>
3910 <source>Vibration</source>
3911 <translation>Вібрація</translation>
3912 </message>
3913 <message>
3914 <location filename="../../src/yuzu/configuration/configure_vibration.ui" line="63"/>
3915 <source>Player 1</source>
3916 <translation>Гравець 1</translation>
3917 </message>
3918 <message>
3919 <location filename="../../src/yuzu/configuration/configure_vibration.ui" line="96"/>
3920 <location filename="../../src/yuzu/configuration/configure_vibration.ui" line="148"/>
3921 <location filename="../../src/yuzu/configuration/configure_vibration.ui" line="200"/>
3922 <location filename="../../src/yuzu/configuration/configure_vibration.ui" line="252"/>
3923 <location filename="../../src/yuzu/configuration/configure_vibration.ui" line="322"/>
3924 <location filename="../../src/yuzu/configuration/configure_vibration.ui" line="374"/>
3925 <location filename="../../src/yuzu/configuration/configure_vibration.ui" line="426"/>
3926 <location filename="../../src/yuzu/configuration/configure_vibration.ui" line="478"/>
3927 <source>%</source>
3928 <translation>%</translation>
3929 </message>
3930 <message>
3931 <location filename="../../src/yuzu/configuration/configure_vibration.ui" line="115"/>
3932 <source>Player 2</source>
3933 <translation>Гравець 2</translation>
3934 </message>
3935 <message>
3936 <location filename="../../src/yuzu/configuration/configure_vibration.ui" line="167"/>
3937 <source>Player 3</source>
3938 <translation>Гравець 3</translation>
3939 </message>
3940 <message>
3941 <location filename="../../src/yuzu/configuration/configure_vibration.ui" line="219"/>
3942 <source>Player 4</source>
3943 <translation>Гравець 4</translation>
3944 </message>
3945 <message>
3946 <location filename="../../src/yuzu/configuration/configure_vibration.ui" line="289"/>
3947 <source>Player 5</source>
3948 <translation>Гравець 5</translation>
3949 </message>
3950 <message>
3951 <location filename="../../src/yuzu/configuration/configure_vibration.ui" line="341"/>
3952 <source>Player 6</source>
3953 <translation>Гравець 6</translation>
3954 </message>
3955 <message>
3956 <location filename="../../src/yuzu/configuration/configure_vibration.ui" line="393"/>
3957 <source>Player 7</source>
3958 <translation>Гравець 7</translation>
3959 </message>
3960 <message>
3961 <location filename="../../src/yuzu/configuration/configure_vibration.ui" line="445"/>
3962 <source>Player 8</source>
3963 <translation>Гравець 8</translation>
3964 </message>
3965 <message>
3966 <location filename="../../src/yuzu/configuration/configure_vibration.ui" line="503"/>
3967 <source>Settings</source>
3968 <translation>Налаштування</translation>
3969 </message>
3970 <message>
3971 <location filename="../../src/yuzu/configuration/configure_vibration.ui" line="509"/>
3972 <source>Enable Accurate Vibration</source>
3973 <translation>Увімкнути точну вібрацію</translation>
3974 </message>
3975</context>
3976<context>
3977 <name>ConfigureWeb</name>
3978 <message>
3979 <location filename="../../src/yuzu/configuration/configure_web.ui" line="14"/>
3980 <source>Form</source>
3981 <translation>Форма</translation>
3982 </message>
3983 <message>
3984 <location filename="../../src/yuzu/configuration/configure_web.ui" line="17"/>
3985 <source>Web</source>
3986 <translation>Мережа</translation>
3987 </message>
3988 <message>
3989 <location filename="../../src/yuzu/configuration/configure_web.ui" line="25"/>
3990 <source>yuzu Web Service</source>
3991 <translation>Веб-сервіс yuzu</translation>
3992 </message>
3993 <message>
3994 <location filename="../../src/yuzu/configuration/configure_web.ui" line="31"/>
3995 <source>By providing your username and token, you agree to allow yuzu to collect additional usage data, which may include user identifying information.</source>
3996 <translation>Надаючи своє ім&apos;я користувача і токен, ви погоджуєтеся дозволити yuzu збирати додаткові дані про використання, які можуть включати інформацію, що ідентифікує користувача.</translation>
3997 </message>
3998 <message>
3999 <location filename="../../src/yuzu/configuration/configure_web.ui" line="49"/>
4000 <location filename="../../src/yuzu/configuration/configure_web.cpp" line="159"/>
4001 <source>Verify</source>
4002 <translation>Підтвердити</translation>
4003 </message>
4004 <message>
4005 <location filename="../../src/yuzu/configuration/configure_web.ui" line="56"/>
4006 <source>Sign up</source>
4007 <translation>Реєстрація</translation>
4008 </message>
4009 <message>
4010 <location filename="../../src/yuzu/configuration/configure_web.ui" line="66"/>
4011 <source>Token: </source>
4012 <translation>Токен:</translation>
4013 </message>
4014 <message>
4015 <location filename="../../src/yuzu/configuration/configure_web.ui" line="76"/>
4016 <source>Username: </source>
4017 <translation>Ім&apos;я користувача:</translation>
4018 </message>
4019 <message>
4020 <location filename="../../src/yuzu/configuration/configure_web.ui" line="93"/>
4021 <source>What is my token?</source>
4022 <translation>Що таке токен і де його знайти?</translation>
4023 </message>
4024 <message>
4025 <location filename="../../src/yuzu/configuration/configure_web.ui" line="118"/>
4026 <source>Web Service configuration can only be changed when a public room isn&apos;t being hosted.</source>
4027 <translation>Налаштування веб-служби можуть бути змінені тільки в тому випадку, коли не хоститься публічна кімната.</translation>
4028 </message>
4029 <message>
4030 <location filename="../../src/yuzu/configuration/configure_web.ui" line="128"/>
4031 <source>Telemetry</source>
4032 <translation>Телеметрія</translation>
4033 </message>
4034 <message>
4035 <location filename="../../src/yuzu/configuration/configure_web.ui" line="134"/>
4036 <source>Share anonymous usage data with the yuzu team</source>
4037 <translation>Ділитися анонімною інформацією про використання з командою yuzu</translation>
4038 </message>
4039 <message>
4040 <location filename="../../src/yuzu/configuration/configure_web.ui" line="141"/>
4041 <source>Learn more</source>
4042 <translation>Дізнатися більше</translation>
4043 </message>
4044 <message>
4045 <location filename="../../src/yuzu/configuration/configure_web.ui" line="150"/>
4046 <source>Telemetry ID:</source>
4047 <translation>Ідентифікатор телеметрії:</translation>
4048 </message>
4049 <message>
4050 <location filename="../../src/yuzu/configuration/configure_web.ui" line="166"/>
4051 <source>Regenerate</source>
4052 <translation>Перегенерувати</translation>
4053 </message>
4054 <message>
4055 <location filename="../../src/yuzu/configuration/configure_web.ui" line="180"/>
4056 <source>Discord Presence</source>
4057 <translation>Discord Presence</translation>
4058 </message>
4059 <message>
4060 <location filename="../../src/yuzu/configuration/configure_web.ui" line="186"/>
4061 <source>Show Current Game in your Discord Status</source>
4062 <translation>Показувати поточну гру у вашому статусі Discord</translation>
4063 </message>
4064 <message>
4065 <location filename="../../src/yuzu/configuration/configure_web.cpp" line="68"/>
4066 <source>&lt;a href=&apos;https://yuzu-emu.org/help/feature/telemetry/&apos;&gt;&lt;span style=&quot;text-decoration: underline; color:#039be5;&quot;&gt;Learn more&lt;/span&gt;&lt;/a&gt;</source>
4067 <translation>&lt;a href=&apos;https://yuzu-emu.org/help/feature/telemetry/&apos;&gt;&lt;span style=&quot;text-decoration: underline; color:#039be5;&quot;&gt;Дізнатися більше&lt;/span&gt;&lt;/a&gt;</translation>
4068 </message>
4069 <message>
4070 <location filename="../../src/yuzu/configuration/configure_web.cpp" line="72"/>
4071 <source>&lt;a href=&apos;https://profile.yuzu-emu.org/&apos;&gt;&lt;span style=&quot;text-decoration: underline; color:#039be5;&quot;&gt;Sign up&lt;/span&gt;&lt;/a&gt;</source>
4072 <translation>&lt;a href=&apos;https://profile.yuzu-emu.org/&apos;&gt;&lt;span style=&quot;text-decoration: underline; color:#039be5;&quot;&gt;Реєстрація&lt;/span&gt;&lt;/a&gt;</translation>
4073 </message>
4074 <message>
4075 <location filename="../../src/yuzu/configuration/configure_web.cpp" line="76"/>
4076 <source>&lt;a href=&apos;https://yuzu-emu.org/wiki/yuzu-web-service/&apos;&gt;&lt;span style=&quot;text-decoration: underline; color:#039be5;&quot;&gt;What is my token?&lt;/span&gt;&lt;/a&gt;</source>
4077 <translation>&lt;a href=&apos;https://yuzu-emu.org/wiki/yuzu-web-service/&apos;&gt;&lt;span style=&quot;text-decoration: underline; color:#039be5;&quot;&gt;Що таке токен і де його знайти?&lt;/span&gt;&lt;/a&gt;</translation>
4078 </message>
4079 <message>
4080 <location filename="../../src/yuzu/configuration/configure_web.cpp" line="80"/>
4081 <location filename="../../src/yuzu/configuration/configure_web.cpp" line="125"/>
4082 <source>Telemetry ID: 0x%1</source>
4083 <translation>Ідентифікатор телеметрії: 0x%1</translation>
4084 </message>
4085 <message>
4086 <location filename="../../src/yuzu/configuration/configure_web.cpp" line="91"/>
4087 <location filename="../../src/yuzu/configuration/configure_web.cpp" line="170"/>
4088 <source>Unspecified</source>
4089 <translation>Відсутній</translation>
4090 </message>
4091 <message>
4092 <location filename="../../src/yuzu/configuration/configure_web.cpp" line="117"/>
4093 <source>Token not verified</source>
4094 <translation>Токен не підтверджено</translation>
4095 </message>
4096 <message>
4097 <location filename="../../src/yuzu/configuration/configure_web.cpp" line="118"/>
4098 <source>Token was not verified. The change to your token has not been saved.</source>
4099 <translation>Токен не було підтверджено. Зміну вашого токена не було збережено.</translation>
4100 </message>
4101 <message>
4102 <location filename="../../src/yuzu/configuration/configure_web.cpp" line="141"/>
4103 <source>Unverified, please click Verify before saving configuration</source>
4104 <comment>Tooltip</comment>
4105 <translation>Не підтверджено, будь ласка, натисніть кнопку Підтвердити, перш ніж зберігати конфігурацію.</translation>
4106 </message>
4107 <message>
4108 <location filename="../../src/yuzu/configuration/configure_web.cpp" line="147"/>
4109 <location filename="../../src/yuzu/configuration/configure_web.cpp" line="149"/>
4110 <source>Verifying...</source>
4111 <translation>Підтверждення...</translation>
4112 </message>
4113 <message>
4114 <location filename="../../src/yuzu/configuration/configure_web.cpp" line="164"/>
4115 <source>Verified</source>
4116 <comment>Tooltip</comment>
4117 <translation>Підтверджено</translation>
4118 </message>
4119 <message>
4120 <location filename="../../src/yuzu/configuration/configure_web.cpp" line="169"/>
4121 <source>Verification failed</source>
4122 <comment>Tooltip</comment>
4123 <translation>Підтверждення не було успішним</translation>
4124 </message>
4125 <message>
4126 <location filename="../../src/yuzu/configuration/configure_web.cpp" line="171"/>
4127 <source>Verification failed</source>
4128 <translation>Підтверждення не було успішним</translation>
4129 </message>
4130 <message>
4131 <location filename="../../src/yuzu/configuration/configure_web.cpp" line="172"/>
4132 <source>Verification failed. Check that you have entered your token correctly, and that your internet connection is working.</source>
4133 <translation>Підтверждення не було успішним. Переконайтеся, що ви правильно ввели свій токен і що ваше інтернет-з&apos;єднання працює.</translation>
4134 </message>
4135</context>
4136<context>
4137 <name>ControllerDialog</name>
4138 <message>
4139 <location filename="../../src/yuzu/debugger/controller.cpp" line="20"/>
4140 <source>Controller P1</source>
4141 <translation>Контролер P1</translation>
4142 </message>
4143 <message>
4144 <location filename="../../src/yuzu/debugger/controller.cpp" line="59"/>
4145 <source>&amp;Controller P1</source>
4146 <translation>[&amp;C] Контролер P1</translation>
4147 </message>
4148</context>
4149<context>
4150 <name>DirectConnect</name>
4151 <message>
4152 <location filename="../../src/yuzu/multiplayer/direct_connect.ui" line="14"/>
4153 <source>Direct Connect</source>
4154 <translation>Пряме підключення</translation>
4155 </message>
4156 <message>
4157 <location filename="../../src/yuzu/multiplayer/direct_connect.ui" line="33"/>
4158 <source>IP Address</source>
4159 <translation>IP-адреса</translation>
4160 </message>
4161 <message>
4162 <location filename="../../src/yuzu/multiplayer/direct_connect.ui" line="56"/>
4163 <source>IP</source>
4164 <translation>IP</translation>
4165 </message>
4166 <message>
4167 <location filename="../../src/yuzu/multiplayer/direct_connect.ui" line="63"/>
4168 <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;IPv4 address of the host&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
4169 <translation>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;IPv4 адреса хоста&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</translation>
4170 </message>
4171 <message>
4172 <location filename="../../src/yuzu/multiplayer/direct_connect.ui" line="73"/>
4173 <source>Port</source>
4174 <translation>Порт</translation>
4175 </message>
4176 <message>
4177 <location filename="../../src/yuzu/multiplayer/direct_connect.ui" line="80"/>
4178 <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Port number the host is listening on&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
4179 <translation>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Номер порту, який прослуховується хостом&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</translation>
4180 </message>
4181 <message>
4182 <location filename="../../src/yuzu/multiplayer/direct_connect.ui" line="100"/>
4183 <source>Nickname</source>
4184 <translation>Псевдонім</translation>
4185 </message>
4186 <message>
4187 <location filename="../../src/yuzu/multiplayer/direct_connect.ui" line="114"/>
4188 <source>Password</source>
4189 <translation>Пароль</translation>
4190 </message>
4191 <message>
4192 <location filename="../../src/yuzu/multiplayer/direct_connect.ui" line="156"/>
4193 <source>Connect</source>
4194 <translation>Підключитися</translation>
4195 </message>
4196</context>
4197<context>
4198 <name>DirectConnectWindow</name>
4199 <message>
4200 <location filename="../../src/yuzu/multiplayer/direct_connect.cpp" line="127"/>
4201 <source>Connecting</source>
4202 <translation>Підключення</translation>
4203 </message>
4204 <message>
4205 <location filename="../../src/yuzu/multiplayer/direct_connect.cpp" line="132"/>
4206 <source>Connect</source>
4207 <translation>Підключитися</translation>
4208 </message>
4209</context>
4210<context>
4211 <name>GMainWindow</name>
4212 <message>
4213 <location filename="../../src/yuzu/main.cpp" line="188"/>
4214 <source>&lt;a href=&apos;https://yuzu-emu.org/help/feature/telemetry/&apos;&gt;Anonymous data is collected&lt;/a&gt; to help improve yuzu. &lt;br/&gt;&lt;br/&gt;Would you like to share your usage data with us?</source>
4215 <translation>&lt;a href=&apos;https://yuzu-emu.org/help/feature/telemetry/&apos;&gt;Анонімні дані збираються для того,&lt;/a&gt; щоб допомогти поліпшити роботу yuzu. &lt;br/&gt;&lt;br/&gt;Хотіли б ви ділитися даними про використання з нами?</translation>
4216 </message>
4217 <message>
4218 <location filename="../../src/yuzu/main.cpp" line="191"/>
4219 <source>Telemetry</source>
4220 <translation>Телеметрія</translation>
4221 </message>
4222 <message>
4223 <location filename="../../src/yuzu/main.cpp" line="405"/>
4224 <source>Broken Vulkan Installation Detected</source>
4225 <translation>Виявлено пошкоджену інсталяцію Vulkan</translation>
4226 </message>
4227 <message>
4228 <location filename="../../src/yuzu/main.cpp" line="406"/>
4229 <source>Vulkan initialization failed during boot.&lt;br&gt;&lt;br&gt;Click &lt;a href=&apos;https://yuzu-emu.org/wiki/faq/#yuzu-starts-with-the-error-broken-vulkan-installation-detected&apos;&gt;here for instructions to fix the issue&lt;/a&gt;.</source>
4230 <translation>Не вдалося виконати ініціалізацію Vulkan під час завантаження.&lt;br&gt;&lt;br&gt;Натисніть &lt;a href=&apos;https://yuzu-emu.org/wiki/faq/#yuzu-starts-with-the-error-broken-vulkan-installation-detected&apos;&gt;тут для отримання інструкцій щодо усунення проблеми&lt;/a&gt;.</translation>
4231 </message>
4232 <message>
4233 <location filename="../../src/yuzu/main.cpp" line="734"/>
4234 <source>Loading Web Applet...</source>
4235 <translation>Завантаження веб-аплета...</translation>
4236 </message>
4237 <message>
4238 <location filename="../../src/yuzu/main.cpp" line="781"/>
4239 <location filename="../../src/yuzu/main.cpp" line="784"/>
4240 <source>Disable Web Applet</source>
4241 <translation>Вимкнути веб-аплет</translation>
4242 </message>
4243 <message>
4244 <location filename="../../src/yuzu/main.cpp" line="785"/>
4245 <source>Disabling the web applet can lead to undefined behavior and should only be used with Super Mario 3D All-Stars. Are you sure you want to disable the web applet?
4246(This can be re-enabled in the Debug settings.)</source>
4247 <translation>Вимкнення веб-апплета може призвести до несподіваної поведінки, і його слід вимикати лише заради Super Mario 3D All-Stars. Ви впевнені, що хочете вимкнути веб-апплет?
4248(Його можна знову ввімкнути в налаштуваннях налагодження.)</translation>
4249 </message>
4250 <message>
4251 <location filename="../../src/yuzu/main.cpp" line="892"/>
4252 <source>The amount of shaders currently being built</source>
4253 <translation>Кількість створюваних шейдерів на цей момент</translation>
4254 </message>
4255 <message>
4256 <location filename="../../src/yuzu/main.cpp" line="894"/>
4257 <source>The current selected resolution scaling multiplier.</source>
4258 <translation>Поточний обраний множник масштабування роздільної здатності.</translation>
4259 </message>
4260 <message>
4261 <location filename="../../src/yuzu/main.cpp" line="897"/>
4262 <source>Current emulation speed. Values higher or lower than 100% indicate emulation is running faster or slower than a Switch.</source>
4263 <translation>Поточна швидкість емуляції. Значення вище або нижче 100% вказують на те, що емуляція йде швидше або повільніше, ніж на Switch.</translation>
4264 </message>
4265 <message>
4266 <location filename="../../src/yuzu/main.cpp" line="900"/>
4267 <source>How many frames per second the game is currently displaying. This will vary from game to game and scene to scene.</source>
4268 <translation>Кількість кадрів на секунду в цей момент. Значення буде змінюватися між іграми та сценами.</translation>
4269 </message>
4270 <message>
4271 <location filename="../../src/yuzu/main.cpp" line="904"/>
4272 <source>Time taken to emulate a Switch frame, not counting framelimiting or v-sync. For full-speed emulation this should be at most 16.67 ms.</source>
4273 <translation>Час, який потрібен для емуляції 1 кадру Switch, не беручи до уваги обмеження FPS або вертикальну синхронізацію. Для емуляції в повній швидкості значення має бути не більше 16,67 мс.</translation>
4274 </message>
4275 <message>
4276 <location filename="../../src/yuzu/main.cpp" line="983"/>
4277 <source>VULKAN</source>
4278 <translation>VULKAN</translation>
4279 </message>
4280 <message>
4281 <location filename="../../src/yuzu/main.cpp" line="983"/>
4282 <source>OPENGL</source>
4283 <translation>OPENGL</translation>
4284 </message>
4285 <message>
4286 <location filename="../../src/yuzu/main.cpp" line="1045"/>
4287 <source>&amp;Clear Recent Files</source>
4288 <translation>[&amp;C] Очистити нещодавні файли</translation>
4289 </message>
4290 <message>
4291 <location filename="../../src/yuzu/main.cpp" line="1353"/>
4292 <source>&amp;Continue</source>
4293 <translation>[&amp;C] Продовжити</translation>
4294 </message>
4295 <message>
4296 <location filename="../../src/yuzu/main.cpp" line="1355"/>
4297 <source>&amp;Pause</source>
4298 <translation>[&amp;P] Пауза</translation>
4299 </message>
4300 <message>
4301 <location filename="../../src/yuzu/main.cpp" line="1435"/>
4302 <source>yuzu is running a game</source>
4303 <extracomment>TRANSLATORS: This string is shown to the user to explain why yuzu needs to prevent the computer from sleeping</extracomment>
4304 <translation>В yuzu запущено гру</translation>
4305 </message>
4306 <message>
4307 <location filename="../../src/yuzu/main.cpp" line="1566"/>
4308 <source>Warning Outdated Game Format</source>
4309 <translation>Попередження застарілий формат гри</translation>
4310 </message>
4311 <message>
4312 <location filename="../../src/yuzu/main.cpp" line="1567"/>
4313 <source>You are using the deconstructed ROM directory format for this game, which is an outdated format that has been superseded by others such as NCA, NAX, XCI, or NSP. Deconstructed ROM directories lack icons, metadata, and update support.&lt;br&gt;&lt;br&gt;For an explanation of the various Switch formats yuzu supports, &lt;a href=&apos;https://yuzu-emu.org/wiki/overview-of-switch-game-formats&apos;&gt;check out our wiki&lt;/a&gt;. This message will not be shown again.</source>
4314 <translation>Для цієї гри ви використовуєте розархівований формат ROM&apos;а, який є застарілим і був замінений іншими, такими як NCA, NAX, XCI або NSP. У розархівованих каталогах ROM&apos;а відсутні іконки, метадані та підтримка оновлень. &lt;br&gt;&lt;br&gt;Для отримання інформації про різні формати Switch, підтримувані yuzu, &lt;a href=&apos;https://yuzu-emu.org/wiki/overview-of-switch-game-formats&apos;&gt;перегляньте нашу вікі&lt;/a&gt;. Це повідомлення більше не буде відображатися.</translation>
4315 </message>
4316 <message>
4317 <location filename="../../src/yuzu/main.cpp" line="1579"/>
4318 <location filename="../../src/yuzu/main.cpp" line="1613"/>
4319 <source>Error while loading ROM!</source>
4320 <translation>Помилка під час завантаження ROM!</translation>
4321 </message>
4322 <message>
4323 <location filename="../../src/yuzu/main.cpp" line="1580"/>
4324 <source>The ROM format is not supported.</source>
4325 <translation>Формат ROM&apos;а не підтримується.</translation>
4326 </message>
4327 <message>
4328 <location filename="../../src/yuzu/main.cpp" line="1584"/>
4329 <source>An error occurred initializing the video core.</source>
4330 <translation>Сталася помилка під час ініціалізації відеоядра.</translation>
4331 </message>
4332 <message>
4333 <location filename="../../src/yuzu/main.cpp" line="1585"/>
4334 <source>yuzu has encountered an error while running the video core. This is usually caused by outdated GPU drivers, including integrated ones. Please see the log for more details. For more information on accessing the log, please see the following page: &lt;a href=&apos;https://yuzu-emu.org/help/reference/log-files/&apos;&gt;How to Upload the Log File&lt;/a&gt;. </source>
4335 <translation>yuzu зіткнувся з помилкою під час запуску відеоядра. Зазвичай це спричинено застарілими драйверами ГП, включно з інтегрованими. Перевірте журнал для отримання більш детальної інформації. Додаткову інформацію про доступ до журналу дивіться на наступній сторінці: &lt;a href=&apos;https://yuzu-emu.org/help/reference/log-files/&apos;&gt;Як завантажити файл журналу&lt;/a&gt;. </translation>
4336 </message>
4337 <message>
4338 <location filename="../../src/yuzu/main.cpp" line="1600"/>
4339 <source>Error while loading ROM! %1</source>
4340 <comment>%1 signifies a numeric error code.</comment>
4341 <translation>Помилка під час завантаження ROM&apos;а! %1</translation>
4342 </message>
4343 <message>
4344 <location filename="../../src/yuzu/main.cpp" line="1603"/>
4345 <source>%1&lt;br&gt;Please follow &lt;a href=&apos;https://yuzu-emu.org/help/quickstart/&apos;&gt;the yuzu quickstart guide&lt;/a&gt; to redump your files.&lt;br&gt;You can refer to the yuzu wiki&lt;/a&gt; or the yuzu Discord&lt;/a&gt; for help.</source>
4346 <comment>%1 signifies an error string.</comment>
4347 <translation>%1&lt;br&gt;Будь ласка, дотримуйтесь &lt;a href=&apos;https://yuzu-emu.org/help/quickstart/&apos;&gt;короткого керівництва користувача yuzu&lt;/a&gt; щоб пере-дампити ваші файли&lt;br&gt;Ви можете звернутися до вікі yuzu&lt;/a&gt; або Discord yuzu&lt;/a&gt; для допомоги</translation>
4348 </message>
4349 <message>
4350 <location filename="../../src/yuzu/main.cpp" line="1614"/>
4351 <source>An unknown error occurred. Please see the log for more details.</source>
4352 <translation>Сталася невідома помилка. Будь ласка, перевірте журнал для подробиць.</translation>
4353 </message>
4354 <message>
4355 <location filename="../../src/yuzu/main.cpp" line="1746"/>
4356 <source>(64-bit)</source>
4357 <translation>(64-бітний)</translation>
4358 </message>
4359 <message>
4360 <location filename="../../src/yuzu/main.cpp" line="1746"/>
4361 <source>(32-bit)</source>
4362 <translation>(32-бітний)</translation>
4363 </message>
4364 <message>
4365 <location filename="../../src/yuzu/main.cpp" line="1747"/>
4366 <source>%1 %2</source>
4367 <comment>%1 is the title name. %2 indicates if the title is 64-bit or 32-bit</comment>
4368 <translation>%1 %2</translation>
4369 </message>
4370 <message>
4371 <location filename="../../src/yuzu/main.cpp" line="1897"/>
4372 <source>Save Data</source>
4373 <translation>Збереження</translation>
4374 </message>
4375 <message>
4376 <location filename="../../src/yuzu/main.cpp" line="1947"/>
4377 <source>Mod Data</source>
4378 <translation>Дані модів</translation>
4379 </message>
4380 <message>
4381 <location filename="../../src/yuzu/main.cpp" line="1959"/>
4382 <source>Error Opening %1 Folder</source>
4383 <translation>Помилка під час відкриття папки %1</translation>
4384 </message>
4385 <message>
4386 <location filename="../../src/yuzu/main.cpp" line="1960"/>
4387 <location filename="../../src/yuzu/main.cpp" line="2366"/>
4388 <source>Folder does not exist!</source>
4389 <translation>Папка не існує!</translation>
4390 </message>
4391 <message>
4392 <location filename="../../src/yuzu/main.cpp" line="1972"/>
4393 <source>Error Opening Transferable Shader Cache</source>
4394 <translation>Помилка під час відкриття переносного кешу шейдерів</translation>
4395 </message>
4396 <message>
4397 <location filename="../../src/yuzu/main.cpp" line="1973"/>
4398 <source>Failed to create the shader cache directory for this title.</source>
4399 <translation>Не вдалося створити папку кешу шейдерів для цієї гри.</translation>
4400 </message>
4401 <message>
4402 <location filename="../../src/yuzu/main.cpp" line="2025"/>
4403 <source>Contents</source>
4404 <translation>Зміст</translation>
4405 </message>
4406 <message>
4407 <location filename="../../src/yuzu/main.cpp" line="2027"/>
4408 <source>Update</source>
4409 <translation>Оновлення</translation>
4410 </message>
4411 <message>
4412 <location filename="../../src/yuzu/main.cpp" line="2029"/>
4413 <source>DLC</source>
4414 <translation>DLC</translation>
4415 </message>
4416 <message>
4417 <location filename="../../src/yuzu/main.cpp" line="2036"/>
4418 <source>Remove Entry</source>
4419 <translation>Видалити запис</translation>
4420 </message>
4421 <message>
4422 <location filename="../../src/yuzu/main.cpp" line="2036"/>
4423 <source>Remove Installed Game %1?</source>
4424 <translation>Видалити встановлену гру %1?</translation>
4425 </message>
4426 <message>
4427 <location filename="../../src/yuzu/main.cpp" line="2066"/>
4428 <location filename="../../src/yuzu/main.cpp" line="2082"/>
4429 <location filename="../../src/yuzu/main.cpp" line="2113"/>
4430 <location filename="../../src/yuzu/main.cpp" line="2174"/>
4431 <location filename="../../src/yuzu/main.cpp" line="2192"/>
4432 <location filename="../../src/yuzu/main.cpp" line="2215"/>
4433 <source>Successfully Removed</source>
4434 <translation>Успішно видалено</translation>
4435 </message>
4436 <message>
4437 <location filename="../../src/yuzu/main.cpp" line="2067"/>
4438 <source>Successfully removed the installed base game.</source>
4439 <translation>Встановлену гру успішно видалено.</translation>
4440 </message>
4441 <message>
4442 <location filename="../../src/yuzu/main.cpp" line="2070"/>
4443 <location filename="../../src/yuzu/main.cpp" line="2085"/>
4444 <location filename="../../src/yuzu/main.cpp" line="2108"/>
4445 <source>Error Removing %1</source>
4446 <translation>Помилка під час видалення %1</translation>
4447 </message>
4448 <message>
4449 <location filename="../../src/yuzu/main.cpp" line="2071"/>
4450 <source>The base game is not installed in the NAND and cannot be removed.</source>
4451 <translation>Гру не встановлено в NAND і не може буде видалено.</translation>
4452 </message>
4453 <message>
4454 <location filename="../../src/yuzu/main.cpp" line="2083"/>
4455 <source>Successfully removed the installed update.</source>
4456 <translation>Встановлене оновлення успішно видалено.</translation>
4457 </message>
4458 <message>
4459 <location filename="../../src/yuzu/main.cpp" line="2086"/>
4460 <source>There is no update installed for this title.</source>
4461 <translation>Для цієї гри не було встановлено оновлення.</translation>
4462 </message>
4463 <message>
4464 <location filename="../../src/yuzu/main.cpp" line="2109"/>
4465 <source>There are no DLC installed for this title.</source>
4466 <translation>Для цієї гри не було встановлено DLC.</translation>
4467 </message>
4468 <message>
4469 <location filename="../../src/yuzu/main.cpp" line="2114"/>
4470 <source>Successfully removed %1 installed DLC.</source>
4471 <translation>Встановлений DLC %1 було успішно видалено</translation>
4472 </message>
4473 <message>
4474 <location filename="../../src/yuzu/main.cpp" line="2122"/>
4475 <source>Delete OpenGL Transferable Shader Cache?</source>
4476 <translation>Видалити переносний кеш шейдерів OpenGL?</translation>
4477 </message>
4478 <message>
4479 <location filename="../../src/yuzu/main.cpp" line="2124"/>
4480 <source>Delete Vulkan Transferable Shader Cache?</source>
4481 <translation>Видалити переносний кеш шейдерів Vulakn?</translation>
4482 </message>
4483 <message>
4484 <location filename="../../src/yuzu/main.cpp" line="2126"/>
4485 <source>Delete All Transferable Shader Caches?</source>
4486 <translation>Видалити весь переносний кеш шейдерів?</translation>
4487 </message>
4488 <message>
4489 <location filename="../../src/yuzu/main.cpp" line="2128"/>
4490 <source>Remove Custom Game Configuration?</source>
4491 <translation>Видалити користувацьке налаштування гри?</translation>
4492 </message>
4493 <message>
4494 <location filename="../../src/yuzu/main.cpp" line="2134"/>
4495 <source>Remove File</source>
4496 <translation>Видалити файл</translation>
4497 </message>
4498 <message>
4499 <location filename="../../src/yuzu/main.cpp" line="2169"/>
4500 <location filename="../../src/yuzu/main.cpp" line="2177"/>
4501 <source>Error Removing Transferable Shader Cache</source>
4502 <translation>Помилка під час видалення переносного кешу шейдерів</translation>
4503 </message>
4504 <message>
4505 <location filename="../../src/yuzu/main.cpp" line="2170"/>
4506 <location filename="../../src/yuzu/main.cpp" line="2188"/>
4507 <source>A shader cache for this title does not exist.</source>
4508 <translation>Кеш шейдерів для цієї гри не існує.</translation>
4509 </message>
4510 <message>
4511 <location filename="../../src/yuzu/main.cpp" line="2175"/>
4512 <source>Successfully removed the transferable shader cache.</source>
4513 <translation>Переносний кеш шейдерів успішно видалено.</translation>
4514 </message>
4515 <message>
4516 <location filename="../../src/yuzu/main.cpp" line="2178"/>
4517 <source>Failed to remove the transferable shader cache.</source>
4518 <translation>Не вдалося видалити переносний кеш шейдерів.</translation>
4519 </message>
4520 <message>
4521 <location filename="../../src/yuzu/main.cpp" line="2187"/>
4522 <location filename="../../src/yuzu/main.cpp" line="2195"/>
4523 <source>Error Removing Transferable Shader Caches</source>
4524 <translation>Помилка під час видалення переносного кешу шейдерів</translation>
4525 </message>
4526 <message>
4527 <location filename="../../src/yuzu/main.cpp" line="2193"/>
4528 <source>Successfully removed the transferable shader caches.</source>
4529 <translation>Переносний кеш шейдерів успішно видалено.</translation>
4530 </message>
4531 <message>
4532 <location filename="../../src/yuzu/main.cpp" line="2196"/>
4533 <source>Failed to remove the transferable shader cache directory.</source>
4534 <translation>Помилка під час видалення папки переносного кешу шейдерів.</translation>
4535 </message>
4536 <message>
4537 <location filename="../../src/yuzu/main.cpp" line="2209"/>
4538 <location filename="../../src/yuzu/main.cpp" line="2218"/>
4539 <source>Error Removing Custom Configuration</source>
4540 <translation>Помилка під час видалення користувацького налаштування</translation>
4541 </message>
4542 <message>
4543 <location filename="../../src/yuzu/main.cpp" line="2210"/>
4544 <source>A custom configuration for this title does not exist.</source>
4545 <translation>Користувацьких налаштувань для цієї гри не існує.</translation>
4546 </message>
4547 <message>
4548 <location filename="../../src/yuzu/main.cpp" line="2216"/>
4549 <source>Successfully removed the custom game configuration.</source>
4550 <translation>Користувацьке налаштування гри успішно видалено.</translation>
4551 </message>
4552 <message>
4553 <location filename="../../src/yuzu/main.cpp" line="2219"/>
4554 <source>Failed to remove the custom game configuration.</source>
4555 <translation>Не вдалося видалити користувацьке налаштування гри.</translation>
4556 </message>
4557 <message>
4558 <location filename="../../src/yuzu/main.cpp" line="2226"/>
4559 <location filename="../../src/yuzu/main.cpp" line="2305"/>
4560 <source>RomFS Extraction Failed!</source>
4561 <translation>Не вдалося вилучити RomFS!</translation>
4562 </message>
4563 <message>
4564 <location filename="../../src/yuzu/main.cpp" line="2227"/>
4565 <source>There was an error copying the RomFS files or the user cancelled the operation.</source>
4566 <translation>Сталася помилка під час копіювання файлів RomFS або користувач скасував операцію.</translation>
4567 </message>
4568 <message>
4569 <location filename="../../src/yuzu/main.cpp" line="2285"/>
4570 <source>Full</source>
4571 <translation>Повний</translation>
4572 </message>
4573 <message>
4574 <location filename="../../src/yuzu/main.cpp" line="2285"/>
4575 <source>Skeleton</source>
4576 <translation>Скелет</translation>
4577 </message>
4578 <message>
4579 <location filename="../../src/yuzu/main.cpp" line="2287"/>
4580 <source>Select RomFS Dump Mode</source>
4581 <translation>Виберіть режим дампа RomFS</translation>
4582 </message>
4583 <message>
4584 <location filename="../../src/yuzu/main.cpp" line="2288"/>
4585 <source>Please select the how you would like the RomFS dumped.&lt;br&gt;Full will copy all of the files into the new directory while &lt;br&gt;skeleton will only create the directory structure.</source>
4586 <translation>Будь ласка, виберіть, як ви хочете виконати дамп RomFS &lt;br&gt;Повний скопіює всі файли в нову папку, тоді як &lt;br&gt;скелет створить лише структуру папок.</translation>
4587 </message>
4588 <message>
4589 <location filename="../../src/yuzu/main.cpp" line="2306"/>
4590 <source>There is not enough free space at %1 to extract the RomFS. Please free up space or select a different dump directory at Emulation &gt; Configure &gt; System &gt; Filesystem &gt; Dump Root</source>
4591 <translation>В %1 недостатньо вільного місця для вилучення RomFS. Будь ласка, звільніть місце або виберіть іншу папку для дампа в Емуляція &gt; Налаштування &gt; Система &gt; Файлова система &gt; Корінь дампа</translation>
4592 </message>
4593 <message>
4594 <location filename="../../src/yuzu/main.cpp" line="2313"/>
4595 <source>Extracting RomFS...</source>
4596 <translation>Вилучення RomFS...</translation>
4597 </message>
4598 <message>
4599 <location filename="../../src/yuzu/main.cpp" line="2313"/>
4600 <location filename="../../src/yuzu/main.cpp" line="2499"/>
4601 <source>Cancel</source>
4602 <translation>Скасувати</translation>
4603 </message>
4604 <message>
4605 <location filename="../../src/yuzu/main.cpp" line="2320"/>
4606 <source>RomFS Extraction Succeeded!</source>
4607 <translation>Вилучення RomFS пройшло успішно!</translation>
4608 </message>
4609 <message>
4610 <location filename="../../src/yuzu/main.cpp" line="2321"/>
4611 <source>The operation completed successfully.</source>
4612 <translation>Операція завершилася успішно.</translation>
4613 </message>
4614 <message>
4615 <location filename="../../src/yuzu/main.cpp" line="2365"/>
4616 <source>Error Opening %1</source>
4617 <translation>Помилка відкриття %1</translation>
4618 </message>
4619 <message>
4620 <location filename="../../src/yuzu/main.cpp" line="2374"/>
4621 <source>Select Directory</source>
4622 <translation>Обрати папку</translation>
4623 </message>
4624 <message>
4625 <location filename="../../src/yuzu/main.cpp" line="2401"/>
4626 <source>Properties</source>
4627 <translation>Властивості</translation>
4628 </message>
4629 <message>
4630 <location filename="../../src/yuzu/main.cpp" line="2402"/>
4631 <source>The game properties could not be loaded.</source>
4632 <translation>Не вдалося завантажити властивості гри.</translation>
4633 </message>
4634 <message>
4635 <location filename="../../src/yuzu/main.cpp" line="2419"/>
4636 <source>Switch Executable (%1);;All Files (*.*)</source>
4637 <comment>%1 is an identifier for the Switch executable file extensions.</comment>
4638 <translation>Виконуваний файл Switch (%1);;Усі файли (*.*)</translation>
4639 </message>
4640 <message>
4641 <location filename="../../src/yuzu/main.cpp" line="2423"/>
4642 <source>Load File</source>
4643 <translation>Завантажити файл</translation>
4644 </message>
4645 <message>
4646 <location filename="../../src/yuzu/main.cpp" line="2436"/>
4647 <source>Open Extracted ROM Directory</source>
4648 <translation>Відкрити папку вилученого ROM&apos;а</translation>
4649 </message>
4650 <message>
4651 <location filename="../../src/yuzu/main.cpp" line="2447"/>
4652 <source>Invalid Directory Selected</source>
4653 <translation>Вибрано неприпустиму папку</translation>
4654 </message>
4655 <message>
4656 <location filename="../../src/yuzu/main.cpp" line="2448"/>
4657 <source>The directory you have selected does not contain a &apos;main&apos; file.</source>
4658 <translation>Папка, яку ви вибрали, не містить файлу &apos;main&apos;.</translation>
4659 </message>
4660 <message>
4661 <location filename="../../src/yuzu/main.cpp" line="2458"/>
4662 <source>Installable Switch File (*.nca *.nsp *.xci);;Nintendo Content Archive (*.nca);;Nintendo Submission Package (*.nsp);;NX Cartridge Image (*.xci)</source>
4663 <translation>Встановлюваний файл Switch (*.nca, *.nsp, *.xci);;Архів контенту Nintendo (*.nca);;Пакет подачі Nintendo (*.nsp);;Образ картриджа NX (*.xci)</translation>
4664 </message>
4665 <message>
4666 <location filename="../../src/yuzu/main.cpp" line="2463"/>
4667 <source>Install Files</source>
4668 <translation>Встановити файли</translation>
4669 </message>
4670 <message numerus="yes">
4671 <location filename="../../src/yuzu/main.cpp" line="2507"/>
4672 <source>%n file(s) remaining</source>
4673 <translation><numerusform>Залишився %n файл</numerusform><numerusform>Залишилося %n файл(ів)</numerusform><numerusform>Залишилося %n файл(ів)</numerusform><numerusform>Залишилося %n файл(ів)</numerusform></translation>
4674 </message>
4675 <message>
4676 <location filename="../../src/yuzu/main.cpp" line="2509"/>
4677 <source>Installing file &quot;%1&quot;...</source>
4678 <translation>Встановлення файлу &quot;%1&quot;...</translation>
4679 </message>
4680 <message>
4681 <location filename="../../src/yuzu/main.cpp" line="2555"/>
4682 <location filename="../../src/yuzu/main.cpp" line="2569"/>
4683 <source>Install Results</source>
4684 <translation>Результати встановлення</translation>
4685 </message>
4686 <message>
4687 <location filename="../../src/yuzu/main.cpp" line="2556"/>
4688 <source>To avoid possible conflicts, we discourage users from installing base games to the NAND.
4689Please, only use this feature to install updates and DLC.</source>
4690 <translation>Щоб уникнути можливих конфліктів, ми не рекомендуємо користувачам встановлювати ігри в NAND.
4691Будь ласка, використовуйте цю функцію тільки для встановлення оновлень і завантажуваного контенту.</translation>
4692 </message>
4693 <message numerus="yes">
4694 <location filename="../../src/yuzu/main.cpp" line="2562"/>
4695 <source>%n file(s) were newly installed
4696</source>
4697 <translation><numerusform>%n файл було нещодавно встановлено
4698</numerusform><numerusform>%n файл(ів) було нещодавно встановлено
4699</numerusform><numerusform>%n файл(ів) було нещодавно встановлено
4700</numerusform><numerusform>%n файл(ів) було нещодавно встановлено
4701</numerusform></translation>
4702 </message>
4703 <message numerus="yes">
4704 <location filename="../../src/yuzu/main.cpp" line="2565"/>
4705 <source>%n file(s) were overwritten
4706</source>
4707 <translation><numerusform>%n файл було перезаписано
4708</numerusform><numerusform>%n файл(ів) було перезаписано
4709</numerusform><numerusform>%n файл(ів) було перезаписано
4710</numerusform><numerusform>%n файл(ів) було перезаписано
4711</numerusform></translation>
4712 </message>
4713 <message numerus="yes">
4714 <location filename="../../src/yuzu/main.cpp" line="2567"/>
4715 <source>%n file(s) failed to install
4716</source>
4717 <translation><numerusform>%n файл не вдалося встановити
4718</numerusform><numerusform>%n файл(ів) не вдалося встановити
4719</numerusform><numerusform>%n файл(ів) не вдалося встановити
4720</numerusform><numerusform>%n файл(ів) не вдалося встановити
4721</numerusform></translation>
4722 </message>
4723 <message>
4724 <location filename="../../src/yuzu/main.cpp" line="2668"/>
4725 <source>System Application</source>
4726 <translation>Системний додаток</translation>
4727 </message>
4728 <message>
4729 <location filename="../../src/yuzu/main.cpp" line="2669"/>
4730 <source>System Archive</source>
4731 <translation>Системний архів</translation>
4732 </message>
4733 <message>
4734 <location filename="../../src/yuzu/main.cpp" line="2670"/>
4735 <source>System Application Update</source>
4736 <translation>Оновлення системного додатку</translation>
4737 </message>
4738 <message>
4739 <location filename="../../src/yuzu/main.cpp" line="2671"/>
4740 <source>Firmware Package (Type A)</source>
4741 <translation>Пакет прошивки (Тип А)</translation>
4742 </message>
4743 <message>
4744 <location filename="../../src/yuzu/main.cpp" line="2672"/>
4745 <source>Firmware Package (Type B)</source>
4746 <translation>Пакет прошивки (Тип Б)</translation>
4747 </message>
4748 <message>
4749 <location filename="../../src/yuzu/main.cpp" line="2673"/>
4750 <source>Game</source>
4751 <translation>Гра</translation>
4752 </message>
4753 <message>
4754 <location filename="../../src/yuzu/main.cpp" line="2674"/>
4755 <source>Game Update</source>
4756 <translation>Оновлення гри</translation>
4757 </message>
4758 <message>
4759 <location filename="../../src/yuzu/main.cpp" line="2675"/>
4760 <source>Game DLC</source>
4761 <translation>DLC до гри</translation>
4762 </message>
4763 <message>
4764 <location filename="../../src/yuzu/main.cpp" line="2676"/>
4765 <source>Delta Title</source>
4766 <translation>Дельта-титул</translation>
4767 </message>
4768 <message>
4769 <location filename="../../src/yuzu/main.cpp" line="2679"/>
4770 <source>Select NCA Install Type...</source>
4771 <translation>Виберіть тип установки NCA...</translation>
4772 </message>
4773 <message>
4774 <location filename="../../src/yuzu/main.cpp" line="2680"/>
4775 <source>Please select the type of title you would like to install this NCA as:
4776(In most instances, the default &apos;Game&apos; is fine.)</source>
4777 <translation>Будь ласка, виберіть тип додатку, який ви хочете встановити для цього NCA:
4778(У більшості випадків, підходить стандартний вибір &quot;Гра&quot;.)</translation>
4779 </message>
4780 <message>
4781 <location filename="../../src/yuzu/main.cpp" line="2686"/>
4782 <source>Failed to Install</source>
4783 <translation>Помилка встановлення</translation>
4784 </message>
4785 <message>
4786 <location filename="../../src/yuzu/main.cpp" line="2687"/>
4787 <source>The title type you selected for the NCA is invalid.</source>
4788 <translation>Тип додатку, який ви вибрали для NCA, недійсний.</translation>
4789 </message>
4790 <message>
4791 <location filename="../../src/yuzu/main.cpp" line="2722"/>
4792 <source>File not found</source>
4793 <translation>Файл не знайдено</translation>
4794 </message>
4795 <message>
4796 <location filename="../../src/yuzu/main.cpp" line="2723"/>
4797 <source>File &quot;%1&quot; not found</source>
4798 <translation>Файл &quot;%1&quot; не знайдено</translation>
4799 </message>
4800 <message>
4801 <location filename="../../src/yuzu/main.cpp" line="2798"/>
4802 <source>OK</source>
4803 <translation>ОК</translation>
4804 </message>
4805 <message>
4806 <location filename="../../src/yuzu/main.cpp" line="2812"/>
4807 <source>Missing yuzu Account</source>
4808 <translation>Відсутній обліковий запис yuzu</translation>
4809 </message>
4810 <message>
4811 <location filename="../../src/yuzu/main.cpp" line="2813"/>
4812 <source>In order to submit a game compatibility test case, you must link your yuzu account.&lt;br&gt;&lt;br/&gt;To link your yuzu account, go to Emulation &amp;gt; Configuration &amp;gt; Web.</source>
4813 <translation>Щоб надіслати звіт про сумісність гри, необхідно прив&apos;язати свій обліковий запис yuzu. &lt;br&gt;&lt;br/&gt;Щоб прив&apos;язати свій обліковий запис yuzu, перейдіть у розділ Емуляція &amp;gt; Параметри &amp;gt; Мережа.</translation>
4814 </message>
4815 <message>
4816 <location filename="../../src/yuzu/main.cpp" line="2823"/>
4817 <source>Error opening URL</source>
4818 <translation>Помилка під час відкриття URL</translation>
4819 </message>
4820 <message>
4821 <location filename="../../src/yuzu/main.cpp" line="2824"/>
4822 <source>Unable to open the URL &quot;%1&quot;.</source>
4823 <translation>Не вдалося відкрити URL: &quot;%1&quot;.</translation>
4824 </message>
4825 <message>
4826 <location filename="../../src/yuzu/main.cpp" line="3120"/>
4827 <source>TAS Recording</source>
4828 <translation>Запис TAS</translation>
4829 </message>
4830 <message>
4831 <location filename="../../src/yuzu/main.cpp" line="3121"/>
4832 <source>Overwrite file of player 1?</source>
4833 <translation>Перезаписати файл гравця 1?</translation>
4834 </message>
4835 <message>
4836 <location filename="../../src/yuzu/main.cpp" line="3147"/>
4837 <source>Invalid config detected</source>
4838 <translation>Виявлено неприпустиму конфігурацію</translation>
4839 </message>
4840 <message>
4841 <location filename="../../src/yuzu/main.cpp" line="3148"/>
4842 <source>Handheld controller can&apos;t be used on docked mode. Pro controller will be selected.</source>
4843 <translation>Портативний контролер не може бути використаний у режимі док-станції. Буде обрано контролер Pro.</translation>
4844 </message>
4845 <message>
4846 <location filename="../../src/yuzu/main.cpp" line="3243"/>
4847 <location filename="../../src/yuzu/main.cpp" line="3271"/>
4848 <source>Amiibo</source>
4849 <translation>Amiibo</translation>
4850 </message>
4851 <message>
4852 <location filename="../../src/yuzu/main.cpp" line="3243"/>
4853 <location filename="../../src/yuzu/main.cpp" line="3271"/>
4854 <source>The current amiibo has been removed</source>
4855 <translation>Поточний amiibo було прибрано</translation>
4856 </message>
4857 <message>
4858 <location filename="../../src/yuzu/main.cpp" line="3248"/>
4859 <source>Error</source>
4860 <translation>Помилка</translation>
4861 </message>
4862 <message>
4863 <location filename="../../src/yuzu/main.cpp" line="3248"/>
4864 <location filename="../../src/yuzu/main.cpp" line="3283"/>
4865 <source>The current game is not looking for amiibos</source>
4866 <translation>Поточна гра не шукає amiibo</translation>
4867 </message>
4868 <message>
4869 <location filename="../../src/yuzu/main.cpp" line="3254"/>
4870 <source>Amiibo File (%1);; All Files (*.*)</source>
4871 <translation>Файл Amiibo (%1);; Всі Файли (*.*)</translation>
4872 </message>
4873 <message>
4874 <location filename="../../src/yuzu/main.cpp" line="3255"/>
4875 <source>Load Amiibo</source>
4876 <translation>Завантажити Amiibo</translation>
4877 </message>
4878 <message>
4879 <location filename="../../src/yuzu/main.cpp" line="3267"/>
4880 <source>Error loading Amiibo data</source>
4881 <translation>Помилка під час завантаження даних Amiibo</translation>
4882 </message>
4883 <message>
4884 <location filename="../../src/yuzu/main.cpp" line="3277"/>
4885 <source>The selected file is not a valid amiibo</source>
4886 <translation>Обраний файл не є допустимим amiibo</translation>
4887 </message>
4888 <message>
4889 <location filename="../../src/yuzu/main.cpp" line="3280"/>
4890 <source>The selected file is already on use</source>
4891 <translation>Обраний файл уже використовується</translation>
4892 </message>
4893 <message>
4894 <location filename="../../src/yuzu/main.cpp" line="3286"/>
4895 <source>An unknown error occurred</source>
4896 <translation>Виникла невідома помилка</translation>
4897 </message>
4898 <message>
4899 <location filename="../../src/yuzu/main.cpp" line="3338"/>
4900 <source>Capture Screenshot</source>
4901 <translation>Зробити знімок екрану</translation>
4902 </message>
4903 <message>
4904 <location filename="../../src/yuzu/main.cpp" line="3339"/>
4905 <source>PNG Image (*.png)</source>
4906 <translation>Зображення PNG (*.png)</translation>
4907 </message>
4908 <message>
4909 <location filename="../../src/yuzu/main.cpp" line="3405"/>
4910 <source>TAS state: Running %1/%2</source>
4911 <translation>Стан TAS: Виконується %1/%2</translation>
4912 </message>
4913 <message>
4914 <location filename="../../src/yuzu/main.cpp" line="3407"/>
4915 <source>TAS state: Recording %1</source>
4916 <translation>Стан TAS: Записується %1</translation>
4917 </message>
4918 <message>
4919 <location filename="../../src/yuzu/main.cpp" line="3409"/>
4920 <source>TAS state: Idle %1/%2</source>
4921 <translation>Стан TAS: Простий %1/%2</translation>
4922 </message>
4923 <message>
4924 <location filename="../../src/yuzu/main.cpp" line="3411"/>
4925 <source>TAS State: Invalid</source>
4926 <translation>Стан TAS: Неприпустимий</translation>
4927 </message>
4928 <message>
4929 <location filename="../../src/yuzu/main.cpp" line="3425"/>
4930 <source>&amp;Stop Running</source>
4931 <translation>[&amp;S] Зупинка</translation>
4932 </message>
4933 <message>
4934 <location filename="../../src/yuzu/main.cpp" line="3425"/>
4935 <source>&amp;Start</source>
4936 <translation>[&amp;S] Почати</translation>
4937 </message>
4938 <message>
4939 <location filename="../../src/yuzu/main.cpp" line="3426"/>
4940 <source>Stop R&amp;ecording</source>
4941 <translation>[&amp;E] Закінчити запис</translation>
4942 </message>
4943 <message>
4944 <location filename="../../src/yuzu/main.cpp" line="3426"/>
4945 <source>R&amp;ecord</source>
4946 <translation>[&amp;E] Запис</translation>
4947 </message>
4948 <message numerus="yes">
4949 <location filename="../../src/yuzu/main.cpp" line="3450"/>
4950 <source>Building: %n shader(s)</source>
4951 <translation><numerusform>Побудова: %n шейдер</numerusform><numerusform>Побудова: %n шейдер(ів)</numerusform><numerusform>Побудова: %n шейдер(ів)</numerusform><numerusform>Побудова: %n шейдер(ів)</numerusform></translation>
4952 </message>
4953 <message>
4954 <location filename="../../src/yuzu/main.cpp" line="3459"/>
4955 <source>Scale: %1x</source>
4956 <comment>%1 is the resolution scaling factor</comment>
4957 <translation>Масштаб: %1x</translation>
4958 </message>
4959 <message>
4960 <location filename="../../src/yuzu/main.cpp" line="3462"/>
4961 <source>Speed: %1% / %2%</source>
4962 <translation>Швидкість: %1% / %2%</translation>
4963 </message>
4964 <message>
4965 <location filename="../../src/yuzu/main.cpp" line="3466"/>
4966 <source>Speed: %1%</source>
4967 <translation>Швидкість: %1%</translation>
4968 </message>
4969 <message>
4970 <location filename="../../src/yuzu/main.cpp" line="3470"/>
4971 <source>Game: %1 FPS (Unlocked)</source>
4972 <translation>Гра: %1 FPS (Необмежено)</translation>
4973 </message>
4974 <message>
4975 <location filename="../../src/yuzu/main.cpp" line="3473"/>
4976 <source>Game: %1 FPS</source>
4977 <translation>Гра: %1 FPS</translation>
4978 </message>
4979 <message>
4980 <location filename="../../src/yuzu/main.cpp" line="3475"/>
4981 <source>Frame: %1 ms</source>
4982 <translation>Кадр: %1 мс</translation>
4983 </message>
4984 <message>
4985 <location filename="../../src/yuzu/main.cpp" line="3486"/>
4986 <source>GPU NORMAL</source>
4987 <translation>ГП НОРМАЛЬНО</translation>
4988 </message>
4989 <message>
4990 <location filename="../../src/yuzu/main.cpp" line="3491"/>
4991 <source>GPU HIGH</source>
4992 <translation>ГП ВИСОКО</translation>
4993 </message>
4994 <message>
4995 <location filename="../../src/yuzu/main.cpp" line="3496"/>
4996 <source>GPU EXTREME</source>
4997 <translation>ГП ЕКСТРИМ</translation>
4998 </message>
4999 <message>
5000 <location filename="../../src/yuzu/main.cpp" line="3501"/>
5001 <source>GPU ERROR</source>
5002 <translation>ГП ПОМИЛКА</translation>
5003 </message>
5004 <message>
5005 <location filename="../../src/yuzu/main.cpp" line="3510"/>
5006 <source>DOCKED</source>
5007 <translation>В ДОК-СТАНЦІЇ</translation>
5008 </message>
5009 <message>
5010 <location filename="../../src/yuzu/main.cpp" line="3510"/>
5011 <source>HANDHELD</source>
5012 <translation>ПОРТАТИВНИЙ</translation>
5013 </message>
5014 <message>
5015 <location filename="../../src/yuzu/main.cpp" line="3517"/>
5016 <source>NEAREST</source>
5017 <translation>НАЙБЛИЖЧІЙ</translation>
5018 </message>
5019 <message>
5020 <location filename="../../src/yuzu/main.cpp" line="3520"/>
5021 <location filename="../../src/yuzu/main.cpp" line="3535"/>
5022 <source>BILINEAR</source>
5023 <translation>БІЛІНІЙНИЙ</translation>
5024 </message>
5025 <message>
5026 <location filename="../../src/yuzu/main.cpp" line="3523"/>
5027 <source>BICUBIC</source>
5028 <translation>БІКУБІЧНИЙ</translation>
5029 </message>
5030 <message>
5031 <location filename="../../src/yuzu/main.cpp" line="3526"/>
5032 <source>GAUSSIAN</source>
5033 <translation>ГАУС</translation>
5034 </message>
5035 <message>
5036 <location filename="../../src/yuzu/main.cpp" line="3529"/>
5037 <source>SCALEFORCE</source>
5038 <translation>SCALEFORCE</translation>
5039 </message>
5040 <message>
5041 <location filename="../../src/yuzu/main.cpp" line="3532"/>
5042 <source>FSR</source>
5043 <translation>FSR</translation>
5044 </message>
5045 <message>
5046 <location filename="../../src/yuzu/main.cpp" line="3544"/>
5047 <location filename="../../src/yuzu/main.cpp" line="3550"/>
5048 <source>NO AA</source>
5049 <translation>БЕЗ ЗГЛАДЖУВАННЯ</translation>
5050 </message>
5051 <message>
5052 <location filename="../../src/yuzu/main.cpp" line="3547"/>
5053 <source>FXAA</source>
5054 <translation>FXAA</translation>
5055 </message>
5056 <message>
5057 <location filename="../../src/yuzu/main.cpp" line="3624"/>
5058 <source>The game you are trying to load requires additional files from your Switch to be dumped before playing.&lt;br/&gt;&lt;br/&gt;For more information on dumping these files, please see the following wiki page: &lt;a href=&apos;https://yuzu-emu.org/wiki/dumping-system-archives-and-the-shared-fonts-from-a-switch-console/&apos;&gt;Dumping System Archives and the Shared Fonts from a Switch Console&lt;/a&gt;.&lt;br/&gt;&lt;br/&gt;Would you like to quit back to the game list? Continuing emulation may result in crashes, corrupted save data, or other bugs.</source>
5059 <translation>Гра, яку ви намагаєтеся завантажити, вимагає, щоб додаткові файли були здамплені з вашого Switch перед початком гри. &lt;br/&gt;&lt;br/&gt;Для отримання додаткової інформації про дамп цих файлів див. наступну вікі: &lt;a href=&apos;https://yuzu-emu.org/wiki/dumping-system-archives-and-the-shared-fonts-from-a-switch-console/&apos;&gt;Дамп системних архівів і загальних шрифтів з консолі&lt;/a&gt;. &lt;br/&gt;&lt;br/&gt;Хочете повернутися до списку ігор? Продовження емуляції може призвести до збоїв, пошкодження збережених даних або інших помилок.</translation>
5060 </message>
5061 <message>
5062 <location filename="../../src/yuzu/main.cpp" line="3639"/>
5063 <source>yuzu was unable to locate a Switch system archive. %1</source>
5064 <translation>yuzu не вдалося знайти системний архів Switch. %1</translation>
5065 </message>
5066 <message>
5067 <location filename="../../src/yuzu/main.cpp" line="3641"/>
5068 <source>yuzu was unable to locate a Switch system archive: %1. %2</source>
5069 <translation>yuzu не вдалося знайти системний архів Switch: %1. %2</translation>
5070 </message>
5071 <message>
5072 <location filename="../../src/yuzu/main.cpp" line="3645"/>
5073 <source>System Archive Not Found</source>
5074 <translation>Системний архів не знайдено</translation>
5075 </message>
5076 <message>
5077 <location filename="../../src/yuzu/main.cpp" line="3647"/>
5078 <source>System Archive Missing</source>
5079 <translation>Відсутній системний архів</translation>
5080 </message>
5081 <message>
5082 <location filename="../../src/yuzu/main.cpp" line="3653"/>
5083 <source>yuzu was unable to locate the Switch shared fonts. %1</source>
5084 <translation>yuzu не вдалося знайти загальні шрифти Switch. %1</translation>
5085 </message>
5086 <message>
5087 <location filename="../../src/yuzu/main.cpp" line="3654"/>
5088 <source>Shared Fonts Not Found</source>
5089 <translation>Загальні шрифти не знайдено</translation>
5090 </message>
5091 <message>
5092 <location filename="../../src/yuzu/main.cpp" line="3656"/>
5093 <source>Shared Font Missing</source>
5094 <translation>Загальні шрифти відсутні</translation>
5095 </message>
5096 <message>
5097 <location filename="../../src/yuzu/main.cpp" line="3662"/>
5098 <source>Fatal Error</source>
5099 <translation>Фатальна помилка</translation>
5100 </message>
5101 <message>
5102 <location filename="../../src/yuzu/main.cpp" line="3663"/>
5103 <source>yuzu has encountered a fatal error, please see the log for more details. For more information on accessing the log, please see the following page: &lt;a href=&apos;https://community.citra-emu.org/t/how-to-upload-the-log-file/296&apos;&gt;How to Upload the Log File&lt;/a&gt;.&lt;br/&gt;&lt;br/&gt;Would you like to quit back to the game list? Continuing emulation may result in crashes, corrupted save data, or other bugs.</source>
5104 <translation>yuzu зіткнувся з фатальною помилкою, перевірте журнал для отримання більш детальної інформації. Для отримання додаткової інформації про доступ до журналу відкрийте наступну сторінку: &lt;a href=&apos;https://community.citra-emu.org/t/how-to-upload-the-log-file/296&apos;&gt;Як завантажити файл журналу&lt;/a&gt;.&lt;br/&gt;&lt;br/&gt;Ви хочете повернутися до списку ігор? Продовження емуляції може призвести до збоїв, пошкодження збережень або інших помилок.</translation>
5105 </message>
5106 <message>
5107 <location filename="../../src/yuzu/main.cpp" line="3672"/>
5108 <source>Fatal Error encountered</source>
5109 <translation>Сталася фатальна помилка</translation>
5110 </message>
5111 <message>
5112 <location filename="../../src/yuzu/main.cpp" line="3695"/>
5113 <source>Confirm Key Rederivation</source>
5114 <translation>Підтвердіть перерахунок ключа</translation>
5115 </message>
5116 <message>
5117 <location filename="../../src/yuzu/main.cpp" line="3696"/>
5118 <source>You are about to force rederive all of your keys.
5119If you do not know what this means or what you are doing,
5120this is a potentially destructive action.
5121Please make sure this is what you want
5122and optionally make backups.
5123
5124This will delete your autogenerated key files and re-run the key derivation module.</source>
5125 <translation>Ви збираєтеся примусово перерахувати всі ваші ключі.
5126Якщо ви не знаєте, що це означає або що ви робите,
5127це потенційно руйнівна дія.
5128Будь ласка, переконайтеся, що це те, що ви хочете
5129і, по бажанню, зробіть резервні копії.
5130
5131Це видалить ваші автоматично згенеровані файли ключів і повторно запустить модуль розрахунку ключів.</translation>
5132 </message>
5133 <message>
5134 <location filename="../../src/yuzu/main.cpp" line="3728"/>
5135 <source>Missing fuses</source>
5136 <translation>Відсутні запобіжники</translation>
5137 </message>
5138 <message>
5139 <location filename="../../src/yuzu/main.cpp" line="3731"/>
5140 <source> - Missing BOOT0</source>
5141 <translation>- Відсутній BOOT0</translation>
5142 </message>
5143 <message>
5144 <location filename="../../src/yuzu/main.cpp" line="3734"/>
5145 <source> - Missing BCPKG2-1-Normal-Main</source>
5146 <translation>- Відсутній BCPKG2-1-Normal-Main</translation>
5147 </message>
5148 <message>
5149 <location filename="../../src/yuzu/main.cpp" line="3737"/>
5150 <source> - Missing PRODINFO</source>
5151 <translation> - Відсутній PRODINFO</translation>
5152 </message>
5153 <message>
5154 <location filename="../../src/yuzu/main.cpp" line="3741"/>
5155 <source>Derivation Components Missing</source>
5156 <translation>Компоненти розрахунку відсутні</translation>
5157 </message>
5158 <message>
5159 <location filename="../../src/yuzu/main.cpp" line="3742"/>
5160 <source>Encryption keys are missing. &lt;br&gt;Please follow &lt;a href=&apos;https://yuzu-emu.org/help/quickstart/&apos;&gt;the yuzu quickstart guide&lt;/a&gt; to get all your keys, firmware and games.&lt;br&gt;&lt;br&gt;&lt;small&gt;(%1)&lt;/small&gt;</source>
5161 <translation>Ключі шифрування відсутні.&lt;br&gt;Будь ласка, дотримуйтесь &lt;a href=&apos;https://yuzu-emu.org/help/quickstart/&apos;&gt;короткого керівництва користувача yuzu&lt;/a&gt;, щоб отримати всі ваші ключі, прошивку та ігри&lt;br&gt;&lt;br&gt;&lt;small&gt;(%1)&lt;/small&gt;</translation>
5162 </message>
5163 <message>
5164 <location filename="../../src/yuzu/main.cpp" line="3751"/>
5165 <source>Deriving keys...
5166This may take up to a minute depending
5167on your system&apos;s performance.</source>
5168 <translation>Отримання ключів...
5169Це може зайняти до хвилини залежно від
5170від продуктивності вашої системи.</translation>
5171 </message>
5172 <message>
5173 <location filename="../../src/yuzu/main.cpp" line="3753"/>
5174 <source>Deriving Keys</source>
5175 <translation>Отримання ключів</translation>
5176 </message>
5177 <message>
5178 <location filename="../../src/yuzu/main.cpp" line="3798"/>
5179 <source>Select RomFS Dump Target</source>
5180 <translation>Оберіть ціль для дампа RomFS</translation>
5181 </message>
5182 <message>
5183 <location filename="../../src/yuzu/main.cpp" line="3799"/>
5184 <source>Please select which RomFS you would like to dump.</source>
5185 <translation>Будь ласка, виберіть, який RomFS ви хочете здампити.</translation>
5186 </message>
5187 <message>
5188 <location filename="../../src/yuzu/main.cpp" line="3814"/>
5189 <source>Are you sure you want to close yuzu?</source>
5190 <translation>Ви впевнені, що хочете закрити yuzu?</translation>
5191 </message>
5192 <message>
5193 <location filename="../../src/yuzu/main.cpp" line="3815"/>
5194 <location filename="../../src/yuzu/main.cpp" line="3913"/>
5195 <location filename="../../src/yuzu/main.cpp" line="3926"/>
5196 <source>yuzu</source>
5197 <translation>yuzu</translation>
5198 </message>
5199 <message>
5200 <location filename="../../src/yuzu/main.cpp" line="3914"/>
5201 <source>Are you sure you want to stop the emulation? Any unsaved progress will be lost.</source>
5202 <translation>Ви впевнені, що хочете зупинити емуляцію? Будь-який незбережений прогрес буде втрачено.</translation>
5203 </message>
5204 <message>
5205 <location filename="../../src/yuzu/main.cpp" line="3923"/>
5206 <source>The currently running application has requested yuzu to not exit.
5207
5208Would you like to bypass this and exit anyway?</source>
5209 <translation>Запущений на даний момент додаток просить yuzu не завершувати роботу.
5210
5211Чи хочете ви обійти це і вийти в будь-якому випадку?</translation>
5212 </message>
5213</context>
5214<context>
5215 <name>GRenderWindow</name>
5216 <message>
5217 <location filename="../../src/yuzu/bootmanager.cpp" line="1047"/>
5218 <source>OpenGL not available!</source>
5219 <translation>OpenGL недоступний!</translation>
5220 </message>
5221 <message>
5222 <location filename="../../src/yuzu/bootmanager.cpp" line="1048"/>
5223 <source>yuzu has not been compiled with OpenGL support.</source>
5224 <translation>yuzu не було зібрано з підтримкою OpenGL.</translation>
5225 </message>
5226 <message>
5227 <location filename="../../src/yuzu/bootmanager.cpp" line="1067"/>
5228 <location filename="../../src/yuzu/bootmanager.cpp" line="1087"/>
5229 <source>Error while initializing OpenGL!</source>
5230 <translation>Помилка під час ініціалізації OpenGL!</translation>
5231 </message>
5232 <message>
5233 <location filename="../../src/yuzu/bootmanager.cpp" line="1068"/>
5234 <source>Your GPU may not support OpenGL, or you do not have the latest graphics driver.</source>
5235 <translation>Ваш ГП може не підтримувати OpenGL, або у вас встановлено застарілий графічний драйвер.</translation>
5236 </message>
5237 <message>
5238 <location filename="../../src/yuzu/bootmanager.cpp" line="1077"/>
5239 <source>Error while initializing OpenGL 4.6!</source>
5240 <translation>Помилка під час ініціалізації OpenGL 4.6!</translation>
5241 </message>
5242 <message>
5243 <location filename="../../src/yuzu/bootmanager.cpp" line="1078"/>
5244 <source>Your GPU may not support OpenGL 4.6, or you do not have the latest graphics driver.&lt;br&gt;&lt;br&gt;GL Renderer:&lt;br&gt;%1</source>
5245 <translation>Ваш ГП може не підтримувати OpenGL 4.6, або у вас встановлено застарілий графічний драйвер.&lt;br&gt;&lt;br&gt;Рендерер GL:&lt;br&gt;%1</translation>
5246 </message>
5247 <message>
5248 <location filename="../../src/yuzu/bootmanager.cpp" line="1088"/>
5249 <source>Your GPU may not support one or more required OpenGL extensions. Please ensure you have the latest graphics driver.&lt;br&gt;&lt;br&gt;GL Renderer:&lt;br&gt;%1&lt;br&gt;&lt;br&gt;Unsupported extensions:&lt;br&gt;%2</source>
5250 <translation>Ваш ГП може не підтримувати одне або кілька необхідних розширень OpenGL. Будь ласка, переконайтеся в тому, що у вас встановлено останній графічний драйвер.&lt;br&gt;&lt;br&gt;Рендерер GL:&lt;br&gt;%1&lt;br&gt;&lt;br&gt;Розширення, що не підтримуються:&lt;br&gt;%2</translation>
5251 </message>
5252</context>
5253<context>
5254 <name>GameList</name>
5255 <message>
5256 <location filename="../../src/yuzu/game_list.cpp" line="532"/>
5257 <source>Favorite</source>
5258 <translation>Улюблені</translation>
5259 </message>
5260 <message>
5261 <location filename="../../src/yuzu/game_list.cpp" line="534"/>
5262 <source>Start Game</source>
5263 <translation>Запустити гру</translation>
5264 </message>
5265 <message>
5266 <location filename="../../src/yuzu/game_list.cpp" line="536"/>
5267 <source>Start Game without Custom Configuration</source>
5268 <translation>Запустити гру без користувацького налаштування</translation>
5269 </message>
5270 <message>
5271 <location filename="../../src/yuzu/game_list.cpp" line="538"/>
5272 <source>Open Save Data Location</source>
5273 <translation>Відкрити папку для збережень</translation>
5274 </message>
5275 <message>
5276 <location filename="../../src/yuzu/game_list.cpp" line="539"/>
5277 <source>Open Mod Data Location</source>
5278 <translation>Відкрити папку для модів</translation>
5279 </message>
5280 <message>
5281 <location filename="../../src/yuzu/game_list.cpp" line="541"/>
5282 <source>Open Transferable Pipeline Cache</source>
5283 <translation>Відкрити переносний кеш конвеєра</translation>
5284 </message>
5285 <message>
5286 <location filename="../../src/yuzu/game_list.cpp" line="543"/>
5287 <source>Remove</source>
5288 <translation>Видалити</translation>
5289 </message>
5290 <message>
5291 <location filename="../../src/yuzu/game_list.cpp" line="544"/>
5292 <source>Remove Installed Update</source>
5293 <translation>Видалити встановлене оновлення</translation>
5294 </message>
5295 <message>
5296 <location filename="../../src/yuzu/game_list.cpp" line="545"/>
5297 <source>Remove All Installed DLC</source>
5298 <translation>Видалити усі DLC</translation>
5299 </message>
5300 <message>
5301 <location filename="../../src/yuzu/game_list.cpp" line="546"/>
5302 <source>Remove Custom Configuration</source>
5303 <translation>Видалити користувацьке налаштування</translation>
5304 </message>
5305 <message>
5306 <location filename="../../src/yuzu/game_list.cpp" line="547"/>
5307 <source>Remove OpenGL Pipeline Cache</source>
5308 <translation>Видалити кеш конвеєра OpenGL</translation>
5309 </message>
5310 <message>
5311 <location filename="../../src/yuzu/game_list.cpp" line="548"/>
5312 <source>Remove Vulkan Pipeline Cache</source>
5313 <translation>Видалити кеш конвеєра Vulkan</translation>
5314 </message>
5315 <message>
5316 <location filename="../../src/yuzu/game_list.cpp" line="550"/>
5317 <source>Remove All Pipeline Caches</source>
5318 <translation>Видалити весь кеш конвеєра </translation>
5319 </message>
5320 <message>
5321 <location filename="../../src/yuzu/game_list.cpp" line="551"/>
5322 <source>Remove All Installed Contents</source>
5323 <translation>Видалити весь встановлений вміст</translation>
5324 </message>
5325 <message>
5326 <location filename="../../src/yuzu/game_list.cpp" line="552"/>
5327 <location filename="../../src/yuzu/game_list.cpp" line="553"/>
5328 <source>Dump RomFS</source>
5329 <translation>Дамп RomFS</translation>
5330 </message>
5331 <message>
5332 <location filename="../../src/yuzu/game_list.cpp" line="554"/>
5333 <source>Dump RomFS to SDMC</source>
5334 <translation>Здампити RomFS у SDMC</translation>
5335 </message>
5336 <message>
5337 <location filename="../../src/yuzu/game_list.cpp" line="555"/>
5338 <source>Copy Title ID to Clipboard</source>
5339 <translation>Скопіювати ідентифікатор додатку в буфер обміну</translation>
5340 </message>
5341 <message>
5342 <location filename="../../src/yuzu/game_list.cpp" line="556"/>
5343 <source>Navigate to GameDB entry</source>
5344 <translation>Перейти до сторінки GameDB</translation>
5345 </message>
5346 <message>
5347 <location filename="../../src/yuzu/game_list.cpp" line="558"/>
5348 <source>Properties</source>
5349 <translation>Властивості</translation>
5350 </message>
5351 <message>
5352 <location filename="../../src/yuzu/game_list.cpp" line="630"/>
5353 <source>Scan Subfolders</source>
5354 <translation>Сканувати підпапки</translation>
5355 </message>
5356 <message>
5357 <location filename="../../src/yuzu/game_list.cpp" line="631"/>
5358 <source>Remove Game Directory</source>
5359 <translation>Видалити директорію гри</translation>
5360 </message>
5361 <message>
5362 <location filename="../../src/yuzu/game_list.cpp" line="650"/>
5363 <source>▲ Move Up</source>
5364 <translation>▲ Перемістити вверх</translation>
5365 </message>
5366 <message>
5367 <location filename="../../src/yuzu/game_list.cpp" line="651"/>
5368 <source>▼ Move Down</source>
5369 <translation>▼ Перемістити вниз</translation>
5370 </message>
5371 <message>
5372 <location filename="../../src/yuzu/game_list.cpp" line="652"/>
5373 <source>Open Directory Location</source>
5374 <translation>Відкрити розташування папки</translation>
5375 </message>
5376 <message>
5377 <location filename="../../src/yuzu/game_list.cpp" line="697"/>
5378 <source>Clear</source>
5379 <translation>Очистити</translation>
5380 </message>
5381 <message>
5382 <location filename="../../src/yuzu/game_list.cpp" line="761"/>
5383 <source>Name</source>
5384 <translation>Назва</translation>
5385 </message>
5386 <message>
5387 <location filename="../../src/yuzu/game_list.cpp" line="762"/>
5388 <source>Compatibility</source>
5389 <translation>Сумісність</translation>
5390 </message>
5391 <message>
5392 <location filename="../../src/yuzu/game_list.cpp" line="763"/>
5393 <source>Add-ons</source>
5394 <translation>Доповнення</translation>
5395 </message>
5396 <message>
5397 <location filename="../../src/yuzu/game_list.cpp" line="764"/>
5398 <source>File type</source>
5399 <translation>Тип файлу</translation>
5400 </message>
5401 <message>
5402 <location filename="../../src/yuzu/game_list.cpp" line="765"/>
5403 <source>Size</source>
5404 <translation>Розмір</translation>
5405 </message>
5406</context>
5407<context>
5408 <name>GameListItemCompat</name>
5409 <message>
5410 <location filename="../../src/yuzu/game_list_p.h" line="149"/>
5411 <source>Perfect</source>
5412 <translation>Ідеально</translation>
5413 </message>
5414 <message>
5415 <location filename="../../src/yuzu/game_list_p.h" line="149"/>
5416 <source>Game functions flawless with no audio or graphical glitches, all tested functionality works as intended without
5417any workarounds needed.</source>
5418 <translation>Гра працює бездоганно, без звукових або графічних артефактів, усі протестовані функції працюють без обхідних шляхів.</translation>
5419 </message>
5420 <message>
5421 <location filename="../../src/yuzu/game_list_p.h" line="150"/>
5422 <source>Great</source>
5423 <translation>Чудово</translation>
5424 </message>
5425 <message>
5426 <location filename="../../src/yuzu/game_list_p.h" line="150"/>
5427 <source>Game functions with minor graphical or audio glitches and is playable from start to finish. May require some
5428workarounds.</source>
5429 <translation>Гра працює з невеликими графічними або звуковими артефактами і може бути пройдена від
5430 початку до кінця. Можуть знадобитися обхідні шляхи.</translation>
5431 </message>
5432 <message>
5433 <location filename="../../src/yuzu/game_list_p.h" line="151"/>
5434 <source>Okay</source>
5435 <translation>Добре</translation>
5436 </message>
5437 <message>
5438 <location filename="../../src/yuzu/game_list_p.h" line="151"/>
5439 <source>Game functions with major graphical or audio glitches, but game is playable from start to finish with
5440workarounds.</source>
5441 <translation>Гра працює з істотними графічними або звуковими артефактами, але може бути пройдена
5442з використанням обхідних шляхів.</translation>
5443 </message>
5444 <message>
5445 <location filename="../../src/yuzu/game_list_p.h" line="152"/>
5446 <source>Bad</source>
5447 <translation>Погано</translation>
5448 </message>
5449 <message>
5450 <location filename="../../src/yuzu/game_list_p.h" line="152"/>
5451 <source>Game functions, but with major graphical or audio glitches. Unable to progress in specific areas due to glitches
5452even with workarounds.</source>
5453 <translation>Гра працює, але з істотними графічними або звуковими артефактами.
5454У деяких частинах неможливо просунутися навіть з обхідними шляхами.</translation>
5455 </message>
5456 <message>
5457 <location filename="../../src/yuzu/game_list_p.h" line="153"/>
5458 <source>Intro/Menu</source>
5459 <translation>Вступ/Меню</translation>
5460 </message>
5461 <message>
5462 <location filename="../../src/yuzu/game_list_p.h" line="153"/>
5463 <source>Game is completely unplayable due to major graphical or audio glitches. Unable to progress past the Start
5464Screen.</source>
5465 <translation>У гру неможливо грати через графічні або звукові артефакти.
5466Неможливо просунутися далі стартового екрана.</translation>
5467 </message>
5468 <message>
5469 <location filename="../../src/yuzu/game_list_p.h" line="154"/>
5470 <source>Won&apos;t Boot</source>
5471 <translation>Не запускається</translation>
5472 </message>
5473 <message>
5474 <location filename="../../src/yuzu/game_list_p.h" line="154"/>
5475 <source>The game crashes when attempting to startup.</source>
5476 <translation>Гра вилітає під час запуску.</translation>
5477 </message>
5478 <message>
5479 <location filename="../../src/yuzu/game_list_p.h" line="155"/>
5480 <source>Not Tested</source>
5481 <translation>Не перевірено</translation>
5482 </message>
5483 <message>
5484 <location filename="../../src/yuzu/game_list_p.h" line="155"/>
5485 <source>The game has not yet been tested.</source>
5486 <translation>Гру ще не перевіряли на сумісність.</translation>
5487 </message>
5488</context>
5489<context>
5490 <name>GameListPlaceholder</name>
5491 <message>
5492 <location filename="../../src/yuzu/game_list.cpp" line="935"/>
5493 <source>Double-click to add a new folder to the game list</source>
5494 <translation>Натисніть двічі, щоб додати нову папку до списку ігор</translation>
5495 </message>
5496</context>
5497<context>
5498 <name>GameListSearchField</name>
5499 <message numerus="yes">
5500 <location filename="../../src/yuzu/game_list.cpp" line="86"/>
5501 <source>%1 of %n result(s)</source>
5502 <translation><numerusform>%1 із %n результат(ів)</numerusform><numerusform>%1 із %n результат(ів)</numerusform><numerusform>%1 із %n результат(ів)</numerusform><numerusform>%1 із %n результат(ів)</numerusform></translation>
5503 </message>
5504 <message>
5505 <location filename="../../src/yuzu/game_list.cpp" line="777"/>
5506 <source>Filter:</source>
5507 <translation>Пошук:</translation>
5508 </message>
5509 <message>
5510 <location filename="../../src/yuzu/game_list.cpp" line="778"/>
5511 <source>Enter pattern to filter</source>
5512 <translation>Введіть текст для пошуку</translation>
5513 </message>
5514</context>
5515<context>
5516 <name>HostRoom</name>
5517 <message>
5518 <location filename="../../src/yuzu/multiplayer/host_room.ui" line="14"/>
5519 <source>Create Room</source>
5520 <translation>Створити кімнату</translation>
5521 </message>
5522 <message>
5523 <location filename="../../src/yuzu/multiplayer/host_room.ui" line="37"/>
5524 <source>Room Name</source>
5525 <translation>Назва кімнати</translation>
5526 </message>
5527 <message>
5528 <location filename="../../src/yuzu/multiplayer/host_room.ui" line="51"/>
5529 <source>Preferred Game</source>
5530 <translation>Переважна гра</translation>
5531 </message>
5532 <message>
5533 <location filename="../../src/yuzu/multiplayer/host_room.ui" line="61"/>
5534 <source>Max Players</source>
5535 <translation>Максимальна кількість гравців</translation>
5536 </message>
5537 <message>
5538 <location filename="../../src/yuzu/multiplayer/host_room.ui" line="91"/>
5539 <source>Username</source>
5540 <translation>Ім&apos;я користувача</translation>
5541 </message>
5542 <message>
5543 <location filename="../../src/yuzu/multiplayer/host_room.ui" line="101"/>
5544 <source>(Leave blank for open game)</source>
5545 <translation>(Залиште порожнім для відкритої гри)</translation>
5546 </message>
5547 <message>
5548 <location filename="../../src/yuzu/multiplayer/host_room.ui" line="118"/>
5549 <source>Password</source>
5550 <translation>Пароль</translation>
5551 </message>
5552 <message>
5553 <location filename="../../src/yuzu/multiplayer/host_room.ui" line="125"/>
5554 <source>Port</source>
5555 <translation>Порт</translation>
5556 </message>
5557 <message>
5558 <location filename="../../src/yuzu/multiplayer/host_room.ui" line="139"/>
5559 <source>Room Description</source>
5560 <translation>Опис кімнати</translation>
5561 </message>
5562 <message>
5563 <location filename="../../src/yuzu/multiplayer/host_room.ui" line="153"/>
5564 <source>Load Previous Ban List</source>
5565 <translation>Завантажити попередній список заблокованих</translation>
5566 </message>
5567 <message>
5568 <location filename="../../src/yuzu/multiplayer/host_room.ui" line="184"/>
5569 <source>Public</source>
5570 <translation>Публічна</translation>
5571 </message>
5572 <message>
5573 <location filename="../../src/yuzu/multiplayer/host_room.ui" line="189"/>
5574 <source>Unlisted</source>
5575 <translation>Прихована</translation>
5576 </message>
5577 <message>
5578 <location filename="../../src/yuzu/multiplayer/host_room.ui" line="197"/>
5579 <source>Host Room</source>
5580 <translation>Створити кімнату</translation>
5581 </message>
5582</context>
5583<context>
5584 <name>HostRoomWindow</name>
5585 <message>
5586 <location filename="../../src/yuzu/multiplayer/host_room.cpp" line="182"/>
5587 <source>Error</source>
5588 <translation>Помилка</translation>
5589 </message>
5590 <message>
5591 <location filename="../../src/yuzu/multiplayer/host_room.cpp" line="183"/>
5592 <source>Failed to announce the room to the public lobby. In order to host a room publicly, you must have a valid yuzu account configured in Emulation -&gt; Configure -&gt; Web. If you do not want to publish a room in the public lobby, then select Unlisted instead.
5593Debug Message: </source>
5594 <translation>Не вдалося оголосити кімнату в публічному фойє. Щоб хостити публічну кімнату, у вас має бути діючий обліковий запис yuzu, налаштований в Емуляція -&gt; Налаштування -&gt; Мережа. Якщо ви не хочете оголошувати кімнату в публічному лобі, виберіть замість цього прихований тип.
5595Повідомлення налагодження:</translation>
5596 </message>
5597</context>
5598<context>
5599 <name>Hotkeys</name>
5600 <message>
5601 <location filename="../../src/yuzu/configuration/config.cpp" line="73"/>
5602 <source>Audio Mute/Unmute</source>
5603 <translation>Увімкнення/вимкнення звуку</translation>
5604 </message>
5605 <message>
5606 <location filename="../../src/yuzu/configuration/config.cpp" line="73"/>
5607 <location filename="../../src/yuzu/configuration/config.cpp" line="74"/>
5608 <location filename="../../src/yuzu/configuration/config.cpp" line="75"/>
5609 <location filename="../../src/yuzu/configuration/config.cpp" line="76"/>
5610 <location filename="../../src/yuzu/configuration/config.cpp" line="77"/>
5611 <location filename="../../src/yuzu/configuration/config.cpp" line="78"/>
5612 <location filename="../../src/yuzu/configuration/config.cpp" line="79"/>
5613 <location filename="../../src/yuzu/configuration/config.cpp" line="80"/>
5614 <location filename="../../src/yuzu/configuration/config.cpp" line="81"/>
5615 <location filename="../../src/yuzu/configuration/config.cpp" line="82"/>
5616 <location filename="../../src/yuzu/configuration/config.cpp" line="83"/>
5617 <location filename="../../src/yuzu/configuration/config.cpp" line="84"/>
5618 <location filename="../../src/yuzu/configuration/config.cpp" line="85"/>
5619 <location filename="../../src/yuzu/configuration/config.cpp" line="86"/>
5620 <location filename="../../src/yuzu/configuration/config.cpp" line="87"/>
5621 <location filename="../../src/yuzu/configuration/config.cpp" line="88"/>
5622 <location filename="../../src/yuzu/configuration/config.cpp" line="89"/>
5623 <location filename="../../src/yuzu/configuration/config.cpp" line="90"/>
5624 <location filename="../../src/yuzu/configuration/config.cpp" line="91"/>
5625 <location filename="../../src/yuzu/configuration/config.cpp" line="92"/>
5626 <location filename="../../src/yuzu/configuration/config.cpp" line="93"/>
5627 <location filename="../../src/yuzu/configuration/config.cpp" line="94"/>
5628 <source>Main Window</source>
5629 <translation>Основне вікно</translation>
5630 </message>
5631 <message>
5632 <location filename="../../src/yuzu/configuration/config.cpp" line="74"/>
5633 <source>Audio Volume Down</source>
5634 <translation>Зменшити гучність звуку</translation>
5635 </message>
5636 <message>
5637 <location filename="../../src/yuzu/configuration/config.cpp" line="75"/>
5638 <source>Audio Volume Up</source>
5639 <translation>Підвищити гучність звуку</translation>
5640 </message>
5641 <message>
5642 <location filename="../../src/yuzu/configuration/config.cpp" line="76"/>
5643 <source>Capture Screenshot</source>
5644 <translation>Зробити знімок екрану</translation>
5645 </message>
5646 <message>
5647 <location filename="../../src/yuzu/configuration/config.cpp" line="77"/>
5648 <source>Change Adapting Filter</source>
5649 <translation>Змінити адаптуючий фільтр</translation>
5650 </message>
5651 <message>
5652 <location filename="../../src/yuzu/configuration/config.cpp" line="78"/>
5653 <source>Change Docked Mode</source>
5654 <translation>Змінити режим консолі</translation>
5655 </message>
5656 <message>
5657 <location filename="../../src/yuzu/configuration/config.cpp" line="79"/>
5658 <source>Change GPU Accuracy</source>
5659 <translation>Змінити точність ГП</translation>
5660 </message>
5661 <message>
5662 <location filename="../../src/yuzu/configuration/config.cpp" line="80"/>
5663 <source>Continue/Pause Emulation</source>
5664 <translation>Продовження/Пауза емуляції</translation>
5665 </message>
5666 <message>
5667 <location filename="../../src/yuzu/configuration/config.cpp" line="81"/>
5668 <source>Exit Fullscreen</source>
5669 <translation>Вийти з повноекранного режиму</translation>
5670 </message>
5671 <message>
5672 <location filename="../../src/yuzu/configuration/config.cpp" line="82"/>
5673 <source>Exit yuzu</source>
5674 <translation>Вийти з yuzu</translation>
5675 </message>
5676 <message>
5677 <location filename="../../src/yuzu/configuration/config.cpp" line="83"/>
5678 <source>Fullscreen</source>
5679 <translation>Повний екран</translation>
5680 </message>
5681 <message>
5682 <location filename="../../src/yuzu/configuration/config.cpp" line="84"/>
5683 <source>Load File</source>
5684 <translation>Завантажити файл</translation>
5685 </message>
5686 <message>
5687 <location filename="../../src/yuzu/configuration/config.cpp" line="85"/>
5688 <source>Load/Remove Amiibo</source>
5689 <translation>Завантажити/видалити Amiibo</translation>
5690 </message>
5691 <message>
5692 <location filename="../../src/yuzu/configuration/config.cpp" line="86"/>
5693 <source>Restart Emulation</source>
5694 <translation>Перезапустити емуляцію</translation>
5695 </message>
5696 <message>
5697 <location filename="../../src/yuzu/configuration/config.cpp" line="87"/>
5698 <source>Stop Emulation</source>
5699 <translation>Зупинити емуляцію</translation>
5700 </message>
5701 <message>
5702 <location filename="../../src/yuzu/configuration/config.cpp" line="88"/>
5703 <source>TAS Record</source>
5704 <translation>Запис TAS</translation>
5705 </message>
5706 <message>
5707 <location filename="../../src/yuzu/configuration/config.cpp" line="89"/>
5708 <source>TAS Reset</source>
5709 <translation>Скидання TAS</translation>
5710 </message>
5711 <message>
5712 <location filename="../../src/yuzu/configuration/config.cpp" line="90"/>
5713 <source>TAS Start/Stop</source>
5714 <translation>Старт/Стоп TAS</translation>
5715 </message>
5716 <message>
5717 <location filename="../../src/yuzu/configuration/config.cpp" line="91"/>
5718 <source>Toggle Filter Bar</source>
5719 <translation>Переключити панель пошуку</translation>
5720 </message>
5721 <message>
5722 <location filename="../../src/yuzu/configuration/config.cpp" line="92"/>
5723 <source>Toggle Framerate Limit</source>
5724 <translation>Переключити обмеження частоти кадрів</translation>
5725 </message>
5726 <message>
5727 <location filename="../../src/yuzu/configuration/config.cpp" line="93"/>
5728 <source>Toggle Mouse Panning</source>
5729 <translation>Переключити панорамування миші</translation>
5730 </message>
5731 <message>
5732 <location filename="../../src/yuzu/configuration/config.cpp" line="94"/>
5733 <source>Toggle Status Bar</source>
5734 <translation>Переключити панель стану</translation>
5735 </message>
5736</context>
5737<context>
5738 <name>InstallDialog</name>
5739 <message>
5740 <location filename="../../src/yuzu/install_dialog.cpp" line="29"/>
5741 <source>Please confirm these are the files you wish to install.</source>
5742 <translation>Будь ласка, переконайтеся, що це ті файли, які ви хочете встановити.</translation>
5743 </message>
5744 <message>
5745 <location filename="../../src/yuzu/install_dialog.cpp" line="32"/>
5746 <source>Installing an Update or DLC will overwrite the previously installed one.</source>
5747 <translation>Встановлення оновлення або завантажуваного контенту перезапише раніше встановлене.</translation>
5748 </message>
5749 <message>
5750 <location filename="../../src/yuzu/install_dialog.cpp" line="36"/>
5751 <source>Install</source>
5752 <translation>Встановити</translation>
5753 </message>
5754 <message>
5755 <location filename="../../src/yuzu/install_dialog.cpp" line="50"/>
5756 <source>Install Files to NAND</source>
5757 <translation>Встановити файли в NAND</translation>
5758 </message>
5759</context>
5760<context>
5761 <name>LimitableInputDialog</name>
5762 <message>
5763 <location filename="../../src/yuzu/util/limitable_input_dialog.cpp" line="61"/>
5764 <source>The text can't contain any of the following characters:
5765%1</source>
5766 <translation>У тексті неприпустимі такі символи:
5767%1</translation>
5768 </message>
5769</context>
5770<context>
5771 <name>LoadingScreen</name>
5772 <message>
5773 <location filename="../../src/yuzu/loading_screen.ui" line="84"/>
5774 <source>Loading Shaders 387 / 1628</source>
5775 <translation>Завантаження шейдерів 387 / 1628</translation>
5776 </message>
5777 <message>
5778 <location filename="../../src/yuzu/loading_screen.ui" line="121"/>
5779 <source>Loading Shaders %v out of %m</source>
5780 <translation>Завантаження шейдерів %v із %m</translation>
5781 </message>
5782 <message>
5783 <location filename="../../src/yuzu/loading_screen.ui" line="135"/>
5784 <source>Estimated Time 5m 4s</source>
5785 <translation>Залишилося приблизно 5м 4с</translation>
5786 </message>
5787 <message>
5788 <location filename="../../src/yuzu/loading_screen.cpp" line="83"/>
5789 <source>Loading...</source>
5790 <translation>Завантаження...</translation>
5791 </message>
5792 <message>
5793 <location filename="../../src/yuzu/loading_screen.cpp" line="84"/>
5794 <source>Loading Shaders %1 / %2</source>
5795 <translation>Завантаження шейдерів %1 / %2</translation>
5796 </message>
5797 <message>
5798 <location filename="../../src/yuzu/loading_screen.cpp" line="85"/>
5799 <source>Launching...</source>
5800 <translation>Запуск...</translation>
5801 </message>
5802 <message>
5803 <location filename="../../src/yuzu/loading_screen.cpp" line="170"/>
5804 <source>Estimated Time %1</source>
5805 <translation>Залишилося приблизно %1</translation>
5806 </message>
5807</context>
5808<context>
5809 <name>Lobby</name>
5810 <message>
5811 <location filename="../../src/yuzu/multiplayer/lobby.ui" line="14"/>
5812 <source>Public Room Browser</source>
5813 <translation>Браузер публічних кімнат</translation>
5814 </message>
5815 <message>
5816 <location filename="../../src/yuzu/multiplayer/lobby.ui" line="32"/>
5817 <location filename="../../src/yuzu/multiplayer/lobby.ui" line="39"/>
5818 <source>Nickname</source>
5819 <translation>Псевдонім</translation>
5820 </message>
5821 <message>
5822 <location filename="../../src/yuzu/multiplayer/lobby.ui" line="59"/>
5823 <source>Filters</source>
5824 <translation>Фільтри</translation>
5825 </message>
5826 <message>
5827 <location filename="../../src/yuzu/multiplayer/lobby.ui" line="66"/>
5828 <source>Search</source>
5829 <translation>Пошук</translation>
5830 </message>
5831 <message>
5832 <location filename="../../src/yuzu/multiplayer/lobby.ui" line="76"/>
5833 <source>Games I Own</source>
5834 <translation>Ігри, якими я володію</translation>
5835 </message>
5836 <message>
5837 <location filename="../../src/yuzu/multiplayer/lobby.ui" line="83"/>
5838 <source>Hide Full Rooms</source>
5839 <translation>Приховати повні кімнати</translation>
5840 </message>
5841 <message>
5842 <location filename="../../src/yuzu/multiplayer/lobby.ui" line="103"/>
5843 <source>Refresh Lobby</source>
5844 <translation>Оновити фойє</translation>
5845 </message>
5846 <message>
5847 <location filename="../../src/yuzu/multiplayer/lobby.cpp" line="112"/>
5848 <source>Password Required to Join</source>
5849 <translation>Для входу необхідний пароль</translation>
5850 </message>
5851 <message>
5852 <location filename="../../src/yuzu/multiplayer/lobby.cpp" line="112"/>
5853 <source>Password:</source>
5854 <translation>Пароль:</translation>
5855 </message>
5856 <message>
5857 <location filename="../../src/yuzu/multiplayer/lobby.cpp" line="215"/>
5858 <source>Players</source>
5859 <translation>Гравці</translation>
5860 </message>
5861 <message>
5862 <location filename="../../src/yuzu/multiplayer/lobby.cpp" line="216"/>
5863 <source>Room Name</source>
5864 <translation>Назва кімнати</translation>
5865 </message>
5866 <message>
5867 <location filename="../../src/yuzu/multiplayer/lobby.cpp" line="217"/>
5868 <source>Preferred Game</source>
5869 <translation>Переважна гра</translation>
5870 </message>
5871 <message>
5872 <location filename="../../src/yuzu/multiplayer/lobby.cpp" line="218"/>
5873 <source>Host</source>
5874 <translation>Хост</translation>
5875 </message>
5876 <message>
5877 <location filename="../../src/yuzu/multiplayer/lobby.cpp" line="225"/>
5878 <source>Refreshing</source>
5879 <translation>Оновлення</translation>
5880 </message>
5881 <message>
5882 <location filename="../../src/yuzu/multiplayer/lobby.cpp" line="282"/>
5883 <source>Refresh List</source>
5884 <translation>Оновити список</translation>
5885 </message>
5886</context>
5887<context>
5888 <name>MainWindow</name>
5889 <message>
5890 <location filename="../../src/yuzu/main.ui" line="14"/>
5891 <source>yuzu</source>
5892 <translation>yuzu</translation>
5893 </message>
5894 <message>
5895 <location filename="../../src/yuzu/main.ui" line="44"/>
5896 <source>&amp;File</source>
5897 <translation>[&amp;F] Файл</translation>
5898 </message>
5899 <message>
5900 <location filename="../../src/yuzu/main.ui" line="48"/>
5901 <source>&amp;Recent Files</source>
5902 <translation>[&amp;R] Нещодавні файли</translation>
5903 </message>
5904 <message>
5905 <location filename="../../src/yuzu/main.ui" line="67"/>
5906 <source>&amp;Emulation</source>
5907 <translation>[&amp;E] Емуляція</translation>
5908 </message>
5909 <message>
5910 <location filename="../../src/yuzu/main.ui" line="78"/>
5911 <source>&amp;View</source>
5912 <translation>[&amp;V] Вигляд</translation>
5913 </message>
5914 <message>
5915 <location filename="../../src/yuzu/main.ui" line="82"/>
5916 <source>&amp;Reset Window Size</source>
5917 <translation>[&amp;R] Скинути розмір вікна</translation>
5918 </message>
5919 <message>
5920 <location filename="../../src/yuzu/main.ui" line="87"/>
5921 <source>&amp;Debugging</source>
5922 <translation>[&amp;D] Налагодження</translation>
5923 </message>
5924 <message>
5925 <location filename="../../src/yuzu/main.ui" line="92"/>
5926 <source>Reset Window Size to &amp;720p</source>
5927 <translation>Скинути розмір вікна до &amp;720p</translation>
5928 </message>
5929 <message>
5930 <location filename="../../src/yuzu/main.ui" line="95"/>
5931 <source>Reset Window Size to 720p</source>
5932 <translation>Скинути розмір вікна до 720p</translation>
5933 </message>
5934 <message>
5935 <location filename="../../src/yuzu/main.ui" line="100"/>
5936 <source>Reset Window Size to &amp;900p</source>
5937 <translation>Скинути розмір вікна до &amp;900p</translation>
5938 </message>
5939 <message>
5940 <location filename="../../src/yuzu/main.ui" line="103"/>
5941 <source>Reset Window Size to 900p</source>
5942 <translation>Скинути розмір вікна до 900p</translation>
5943 </message>
5944 <message>
5945 <location filename="../../src/yuzu/main.ui" line="108"/>
5946 <source>Reset Window Size to &amp;1080p</source>
5947 <translation>Скинути розмір вікна до &amp;1080p</translation>
5948 </message>
5949 <message>
5950 <location filename="../../src/yuzu/main.ui" line="111"/>
5951 <source>Reset Window Size to 1080p</source>
5952 <translation>Скинути розмір вікна до 1080p</translation>
5953 </message>
5954 <message>
5955 <location filename="../../src/yuzu/main.ui" line="128"/>
5956 <source>&amp;Multiplayer</source>
5957 <translation>[&amp;M] Мультиплеєр</translation>
5958 </message>
5959 <message>
5960 <location filename="../../src/yuzu/main.ui" line="139"/>
5961 <source>&amp;Tools</source>
5962 <translation>[&amp;T] Інструменти</translation>
5963 </message>
5964 <message>
5965 <location filename="../../src/yuzu/main.ui" line="143"/>
5966 <source>&amp;TAS</source>
5967 <translation>[&amp;T] TAS</translation>
5968 </message>
5969 <message>
5970 <location filename="../../src/yuzu/main.ui" line="158"/>
5971 <source>&amp;Help</source>
5972 <translation>[&amp;H] Допомога</translation>
5973 </message>
5974 <message>
5975 <location filename="../../src/yuzu/main.ui" line="179"/>
5976 <source>&amp;Install Files to NAND...</source>
5977 <translation>[&amp;I] Встановити файли в NAND...</translation>
5978 </message>
5979 <message>
5980 <location filename="../../src/yuzu/main.ui" line="184"/>
5981 <source>L&amp;oad File...</source>
5982 <translation>[&amp;O] Завантажити файл...</translation>
5983 </message>
5984 <message>
5985 <location filename="../../src/yuzu/main.ui" line="189"/>
5986 <source>Load &amp;Folder...</source>
5987 <translation>[&amp;F] Завантажити папку...</translation>
5988 </message>
5989 <message>
5990 <location filename="../../src/yuzu/main.ui" line="194"/>
5991 <source>E&amp;xit</source>
5992 <translation>[&amp;X] Вихід</translation>
5993 </message>
5994 <message>
5995 <location filename="../../src/yuzu/main.ui" line="202"/>
5996 <source>&amp;Pause</source>
5997 <translation>[&amp;P] Пауза</translation>
5998 </message>
5999 <message>
6000 <location filename="../../src/yuzu/main.ui" line="210"/>
6001 <source>&amp;Stop</source>
6002 <translation>[&amp;S] Стоп</translation>
6003 </message>
6004 <message>
6005 <location filename="../../src/yuzu/main.ui" line="215"/>
6006 <source>&amp;Reinitialize keys...</source>
6007 <translation>[&amp;R] Переініціалізувати ключі...</translation>
6008 </message>
6009 <message>
6010 <location filename="../../src/yuzu/main.ui" line="220"/>
6011 <source>&amp;About yuzu</source>
6012 <translation>[&amp;A] Про yuzu</translation>
6013 </message>
6014 <message>
6015 <location filename="../../src/yuzu/main.ui" line="228"/>
6016 <source>Single &amp;Window Mode</source>
6017 <translation>[&amp;W] Режим одного вікна</translation>
6018 </message>
6019 <message>
6020 <location filename="../../src/yuzu/main.ui" line="233"/>
6021 <source>Con&amp;figure...</source>
6022 <translation>[&amp;F] Налаштування...</translation>
6023 </message>
6024 <message>
6025 <location filename="../../src/yuzu/main.ui" line="241"/>
6026 <source>Display D&amp;ock Widget Headers</source>
6027 <translation>[&amp;O] Відображати заголовки віджетів дока</translation>
6028 </message>
6029 <message>
6030 <location filename="../../src/yuzu/main.ui" line="249"/>
6031 <source>Show &amp;Filter Bar</source>
6032 <translation>[&amp;F] Показати панель пошуку</translation>
6033 </message>
6034 <message>
6035 <location filename="../../src/yuzu/main.ui" line="257"/>
6036 <source>Show &amp;Status Bar</source>
6037 <translation>[&amp;S] Показати панель статусу</translation>
6038 </message>
6039 <message>
6040 <location filename="../../src/yuzu/main.ui" line="260"/>
6041 <source>Show Status Bar</source>
6042 <translation>Показати панель статусу</translation>
6043 </message>
6044 <message>
6045 <location filename="../../src/yuzu/main.ui" line="268"/>
6046 <source>&amp;Browse Public Game Lobby</source>
6047 <translation>[&amp;B] Переглянути публічні ігрові фойє</translation>
6048 </message>
6049 <message>
6050 <location filename="../../src/yuzu/main.ui" line="276"/>
6051 <source>&amp;Create Room</source>
6052 <translation>[&amp;C] Створити кімнату</translation>
6053 </message>
6054 <message>
6055 <location filename="../../src/yuzu/main.ui" line="284"/>
6056 <source>&amp;Leave Room</source>
6057 <translation>[&amp;L] Залишити кімнату</translation>
6058 </message>
6059 <message>
6060 <location filename="../../src/yuzu/main.ui" line="289"/>
6061 <source>&amp;Direct Connect to Room</source>
6062 <translation>[&amp;D] Пряме під&apos;єднання до кімнати</translation>
6063 </message>
6064 <message>
6065 <location filename="../../src/yuzu/main.ui" line="297"/>
6066 <source>&amp;Show Current Room</source>
6067 <translation>[&amp;S] Показати поточну кімнату</translation>
6068 </message>
6069 <message>
6070 <location filename="../../src/yuzu/main.ui" line="305"/>
6071 <source>F&amp;ullscreen</source>
6072 <translation>[&amp;U] Повноекранний</translation>
6073 </message>
6074 <message>
6075 <location filename="../../src/yuzu/main.ui" line="313"/>
6076 <source>&amp;Restart</source>
6077 <translation>[&amp;R] Перезапустити</translation>
6078 </message>
6079 <message>
6080 <location filename="../../src/yuzu/main.ui" line="321"/>
6081 <source>Load/Remove &amp;Amiibo...</source>
6082 <translation>[&amp;A] Завантажити/Видалити Amiibo...</translation>
6083 </message>
6084 <message>
6085 <location filename="../../src/yuzu/main.ui" line="329"/>
6086 <source>&amp;Report Compatibility</source>
6087 <translation>[&amp;R] Повідомити про сумісність</translation>
6088 </message>
6089 <message>
6090 <location filename="../../src/yuzu/main.ui" line="337"/>
6091 <source>Open &amp;Mods Page</source>
6092 <translation>[&amp;M] Відкрити сторінку модів</translation>
6093 </message>
6094 <message>
6095 <location filename="../../src/yuzu/main.ui" line="342"/>
6096 <source>Open &amp;Quickstart Guide</source>
6097 <translation>[&amp;Q] Відкрити посібник користувача</translation>
6098 </message>
6099 <message>
6100 <location filename="../../src/yuzu/main.ui" line="347"/>
6101 <source>&amp;FAQ</source>
6102 <translation>[&amp;F] ЧАП</translation>
6103 </message>
6104 <message>
6105 <location filename="../../src/yuzu/main.ui" line="352"/>
6106 <source>Open &amp;yuzu Folder</source>
6107 <translation>[&amp;Y] Відкрити папку yuzu</translation>
6108 </message>
6109 <message>
6110 <location filename="../../src/yuzu/main.ui" line="360"/>
6111 <source>&amp;Capture Screenshot</source>
6112 <translation>[&amp;C] Зробити знімок екрану</translation>
6113 </message>
6114 <message>
6115 <location filename="../../src/yuzu/main.ui" line="365"/>
6116 <source>&amp;Configure TAS...</source>
6117 <translation>[&amp;C] Налаштування TAS...</translation>
6118 </message>
6119 <message>
6120 <location filename="../../src/yuzu/main.ui" line="373"/>
6121 <source>Configure C&amp;urrent Game...</source>
6122 <translation>[&amp;U] Налаштувати поточну гру...</translation>
6123 </message>
6124 <message>
6125 <location filename="../../src/yuzu/main.ui" line="381"/>
6126 <source>&amp;Start</source>
6127 <translation>[&amp;S] Почати</translation>
6128 </message>
6129 <message>
6130 <location filename="../../src/yuzu/main.ui" line="389"/>
6131 <source>&amp;Reset</source>
6132 <translation>[&amp;S] Скинути</translation>
6133 </message>
6134 <message>
6135 <location filename="../../src/yuzu/main.ui" line="397"/>
6136 <source>R&amp;ecord</source>
6137 <translation>[&amp;E] Запис</translation>
6138 </message>
6139</context>
6140<context>
6141 <name>MicroProfileDialog</name>
6142 <message>
6143 <location filename="../../src/yuzu/debugger/profiler.cpp" line="50"/>
6144 <source>&amp;MicroProfile</source>
6145 <translation>[&amp;M] MicroProfile</translation>
6146 </message>
6147</context>
6148<context>
6149 <name>ModerationDialog</name>
6150 <message>
6151 <location filename="../../src/yuzu/multiplayer/moderation_dialog.ui" line="6"/>
6152 <source>Moderation</source>
6153 <translation>Модерація</translation>
6154 </message>
6155 <message>
6156 <location filename="../../src/yuzu/multiplayer/moderation_dialog.ui" line="20"/>
6157 <source>Ban List</source>
6158 <translation>Список заблокованих</translation>
6159 </message>
6160 <message>
6161 <location filename="../../src/yuzu/multiplayer/moderation_dialog.ui" line="41"/>
6162 <location filename="../../src/yuzu/multiplayer/moderation_dialog.cpp" line="73"/>
6163 <source>Refreshing</source>
6164 <translation>Оновлення</translation>
6165 </message>
6166 <message>
6167 <location filename="../../src/yuzu/multiplayer/moderation_dialog.ui" line="51"/>
6168 <source>Unban</source>
6169 <translation>Розблокувати</translation>
6170 </message>
6171 <message>
6172 <location filename="../../src/yuzu/multiplayer/moderation_dialog.cpp" line="40"/>
6173 <source>Subject</source>
6174 <translation>Суб&apos;єкт</translation>
6175 </message>
6176 <message>
6177 <location filename="../../src/yuzu/multiplayer/moderation_dialog.cpp" line="41"/>
6178 <source>Type</source>
6179 <translation>Тип</translation>
6180 </message>
6181 <message>
6182 <location filename="../../src/yuzu/multiplayer/moderation_dialog.cpp" line="83"/>
6183 <source>Forum Username</source>
6184 <translation>Ім&apos;я користувача на форумі</translation>
6185 </message>
6186 <message>
6187 <location filename="../../src/yuzu/multiplayer/moderation_dialog.cpp" line="88"/>
6188 <source>IP Address</source>
6189 <translation>IP-адреса</translation>
6190 </message>
6191 <message>
6192 <location filename="../../src/yuzu/multiplayer/moderation_dialog.cpp" line="95"/>
6193 <source>Refresh</source>
6194 <translation>Оновити</translation>
6195 </message>
6196</context>
6197<context>
6198 <name>MultiplayerState</name>
6199 <message>
6200 <location filename="../../src/yuzu/multiplayer/state.cpp" line="90"/>
6201 <source>Current connection status</source>
6202 <translation>Поточний стан з&apos;єднання</translation>
6203 </message>
6204 <message>
6205 <location filename="../../src/yuzu/multiplayer/state.cpp" line="117"/>
6206 <source>Not Connected. Click here to find a room!</source>
6207 <translation>Не з&apos;єднано. Натисніть тут, щоб знайти кімнату!</translation>
6208 </message>
6209 <message>
6210 <location filename="../../src/yuzu/multiplayer/state.cpp" line="123"/>
6211 <source>Not Connected</source>
6212 <translation>Не з&apos;єднано</translation>
6213 </message>
6214 <message>
6215 <location filename="../../src/yuzu/multiplayer/state.cpp" line="129"/>
6216 <source>Connected</source>
6217 <translation>З&apos;єднано</translation>
6218 </message>
6219 <message>
6220 <location filename="../../src/yuzu/multiplayer/state.cpp" line="136"/>
6221 <source>New Messages Received</source>
6222 <translation>Отримано нові повідомлення</translation>
6223 </message>
6224 <message>
6225 <location filename="../../src/yuzu/multiplayer/state.cpp" line="207"/>
6226 <source>Error</source>
6227 <translation>Помилка</translation>
6228 </message>
6229 <message>
6230 <location filename="../../src/yuzu/multiplayer/state.cpp" line="208"/>
6231 <source>Failed to update the room information. Please check your Internet connection and try hosting the room again.
6232Debug Message: </source>
6233 <translation>Не вдалося оновити інформацію про кімнату. Будь ласка, перевірте підключення до Інтернету та спробуйте знову зайти в кімнату.
6234Повідомлення налагодження:</translation>
6235 </message>
6236</context>
6237<context>
6238 <name>NetworkMessage</name>
6239 <message>
6240 <location filename="../../src/yuzu/multiplayer/message.cpp" line="11"/>
6241 <source>Username is not valid. Must be 4 to 20 alphanumeric characters.</source>
6242 <translation>Ім&apos;я користувача неприпустиме. Має бути від 4 до 20 буквено-цифрових символів.</translation>
6243 </message>
6244 <message>
6245 <location filename="../../src/yuzu/multiplayer/message.cpp" line="13"/>
6246 <source>Room name is not valid. Must be 4 to 20 alphanumeric characters.</source>
6247 <translation>Назва кімнати неприпустима. Має бути від 4 до 20 буквено-цифрових символів.</translation>
6248 </message>
6249 <message>
6250 <location filename="../../src/yuzu/multiplayer/message.cpp" line="15"/>
6251 <source>Username is already in use or not valid. Please choose another.</source>
6252 <translation>Ім&apos;я користувача вже використовується або недійсне. Будь ласка, виберіть інше.</translation>
6253 </message>
6254 <message>
6255 <location filename="../../src/yuzu/multiplayer/message.cpp" line="17"/>
6256 <source>IP is not a valid IPv4 address.</source>
6257 <translation>IP-адреса не є дійсною адресою IPv4.</translation>
6258 </message>
6259 <message>
6260 <location filename="../../src/yuzu/multiplayer/message.cpp" line="19"/>
6261 <source>Port must be a number between 0 to 65535.</source>
6262 <translation>Порт повинен бути числом від 0 до 65535.</translation>
6263 </message>
6264 <message>
6265 <location filename="../../src/yuzu/multiplayer/message.cpp" line="20"/>
6266 <source>You must choose a Preferred Game to host a room. If you do not have any games in your game list yet, add a game folder by clicking on the plus icon in the game list.</source>
6267 <translation>Ви повинні вибрати бажану гру, щоб хостити кімнату. Якщо у вашому списку ігор ще немає жодної гри, додайте папку з грою, натиснувши на значок плюса у списку ігор.</translation>
6268 </message>
6269 <message>
6270 <location filename="../../src/yuzu/multiplayer/message.cpp" line="24"/>
6271 <source>Unable to find an internet connection. Check your internet settings.</source>
6272 <translation>Неможливо знайти підключення до Інтернету. Перевірте налаштування інтернету.</translation>
6273 </message>
6274 <message>
6275 <location filename="../../src/yuzu/multiplayer/message.cpp" line="26"/>
6276 <source>Unable to connect to the host. Verify that the connection settings are correct. If you still cannot connect, contact the room host and verify that the host is properly configured with the external port forwarded.</source>
6277 <translation>Неможливо підключитися до хоста. Перевірте правильність налаштувань підключення. Якщо під&apos;єднання, як і раніше, неможливе, зв&apos;яжіться з хостом кімнати та переконайтеся, що хост правильно налаштований із прокинутим зовнішнім портом.</translation>
6278 </message>
6279 <message>
6280 <location filename="../../src/yuzu/multiplayer/message.cpp" line="30"/>
6281 <source>Unable to connect to the room because it is already full.</source>
6282 <translation>Неможливо підключитися до кімнати, оскільки вона вже заповнена.</translation>
6283 </message>
6284 <message>
6285 <location filename="../../src/yuzu/multiplayer/message.cpp" line="32"/>
6286 <source>Creating a room failed. Please retry. Restarting yuzu might be necessary.</source>
6287 <translation>Створення кімнати не вдалося. Будь ласка, повторіть спробу. Можливо, потрібно перезапустити yuzu.</translation>
6288 </message>
6289 <message>
6290 <location filename="../../src/yuzu/multiplayer/message.cpp" line="34"/>
6291 <source>The host of the room has banned you. Speak with the host to unban you or try a different room.</source>
6292 <translation>Хост кімнати заблокував вас. Поговоріть із хостом, щоб він розблокував вас, або спробуйте іншу кімнату.</translation>
6293 </message>
6294 <message>
6295 <location filename="../../src/yuzu/multiplayer/message.cpp" line="37"/>
6296 <source>Version mismatch! Please update to the latest version of yuzu. If the problem persists, contact the room host and ask them to update the server.</source>
6297 <translation>Невідповідність версій! Будь ласка, оновіть yuzu до останньої версії. Якщо проблема не зникне, зверніться до хосту кімнати і попросіть його оновити сервер.</translation>
6298 </message>
6299 <message>
6300 <location filename="../../src/yuzu/multiplayer/message.cpp" line="39"/>
6301 <source>Incorrect password.</source>
6302 <translation>Невірний пароль.</translation>
6303 </message>
6304 <message>
6305 <location filename="../../src/yuzu/multiplayer/message.cpp" line="40"/>
6306 <source>An unknown error occurred. If this error continues to occur, please open an issue</source>
6307 <translation>Сталася невідома помилка. Якщо ця помилка продовжує виникати, будь ласка, відкрийте проблему</translation>
6308 </message>
6309 <message>
6310 <location filename="../../src/yuzu/multiplayer/message.cpp" line="43"/>
6311 <source>Connection to room lost. Try to reconnect.</source>
6312 <translation>З&apos;єднання з кімнатою втрачено. Спробуйте підключитися знову.</translation>
6313 </message>
6314 <message>
6315 <location filename="../../src/yuzu/multiplayer/message.cpp" line="45"/>
6316 <source>You have been kicked by the room host.</source>
6317 <translation>Вас вигнав хост кімнати.</translation>
6318 </message>
6319 <message>
6320 <location filename="../../src/yuzu/multiplayer/message.cpp" line="47"/>
6321 <source>IP address is already in use. Please choose another.</source>
6322 <translation>IP-адреса вже використовується. Будь ласка, виберіть іншу.</translation>
6323 </message>
6324 <message>
6325 <location filename="../../src/yuzu/multiplayer/message.cpp" line="49"/>
6326 <source>You do not have enough permission to perform this action.</source>
6327 <translation>У вас немає достатніх дозволів для виконання цієї дії.</translation>
6328 </message>
6329 <message>
6330 <location filename="../../src/yuzu/multiplayer/message.cpp" line="50"/>
6331 <source>The user you are trying to kick/ban could not be found.
6332They may have left the room.</source>
6333 <translation>Користувача, якого ви намагаєтеся вигнати/заблокувати, не знайдено.
6334Можливо, вони покинули кімнату.</translation>
6335 </message>
6336 <message>
6337 <location filename="../../src/yuzu/multiplayer/message.cpp" line="52"/>
6338 <source>No valid network interface is selected.
6339Please go to Configure -&gt; System -&gt; Network and make a selection.</source>
6340 <translation>Не вибрано припустимий інтерфейс мережі.
6341Будь ласка, перейдіть у Налаштування -&gt; Система -&gt; Мережа та зробіть вибір.</translation>
6342 </message>
6343 <message>
6344 <location filename="../../src/yuzu/multiplayer/message.cpp" line="68"/>
6345 <source>Game already running</source>
6346 <translation>Гру вже запущено</translation>
6347 </message>
6348 <message>
6349 <location filename="../../src/yuzu/multiplayer/message.cpp" line="69"/>
6350 <source>Joining a room when the game is already running is discouraged and can cause the room feature not to work correctly.
6351Proceed anyway?</source>
6352 <translation>Приєднуватися до кімнати, коли гру вже запущено, не рекомендується, це може призвести до неправильної роботи функції кімнати.
6353Все одно продовжити?</translation>
6354 </message>
6355 <message>
6356 <location filename="../../src/yuzu/multiplayer/message.cpp" line="75"/>
6357 <source>Leave Room</source>
6358 <translation>Залишити кімнату</translation>
6359 </message>
6360 <message>
6361 <location filename="../../src/yuzu/multiplayer/message.cpp" line="76"/>
6362 <source>You are about to close the room. Any network connections will be closed.</source>
6363 <translation>Ви збираєтеся закрити кімнату. Усі мережеві підключення буде закрито.</translation>
6364 </message>
6365 <message>
6366 <location filename="../../src/yuzu/multiplayer/message.cpp" line="81"/>
6367 <source>Disconnect</source>
6368 <translation>Від&apos;єднатися</translation>
6369 </message>
6370 <message>
6371 <location filename="../../src/yuzu/multiplayer/message.cpp" line="82"/>
6372 <source>You are about to leave the room. Any network connections will be closed.</source>
6373 <translation>Ви збираєтеся покинути кімнату. Усі мережеві підключення буде закрито.</translation>
6374 </message>
6375</context>
6376<context>
6377 <name>NetworkMessage::ErrorManager</name>
6378 <message>
6379 <location filename="../../src/yuzu/multiplayer/message.cpp" line="63"/>
6380 <source>Error</source>
6381 <translation>Помилка</translation>
6382 </message>
6383</context>
6384<context>
6385 <name>OverlayDialog</name>
6386 <message>
6387 <location filename="../../src/yuzu/util/overlay_dialog.ui" line="14"/>
6388 <source>Dialog</source>
6389 <translation>Діалог</translation>
6390 </message>
6391 <message>
6392 <location filename="../../src/yuzu/util/overlay_dialog.ui" line="134"/>
6393 <location filename="../../src/yuzu/util/overlay_dialog.ui" line="353"/>
6394 <source>Cancel</source>
6395 <translation>Скасувати</translation>
6396 </message>
6397 <message>
6398 <location filename="../../src/yuzu/util/overlay_dialog.ui" line="152"/>
6399 <location filename="../../src/yuzu/util/overlay_dialog.ui" line="371"/>
6400 <source>OK</source>
6401 <translation>ОК</translation>
6402 </message>
6403 <message>
6404 <location filename="../../src/yuzu/util/overlay_dialog.ui" line="313"/>
6405 <source>&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.0//EN&quot; &quot;http://www.w3.org/TR/REC-html40/strict.dtd&quot;&gt;
6406&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
6407p, li { white-space: pre-wrap; }
6408&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:'MS Shell Dlg 2'; font-size:18pt; font-weight:400; font-style:normal;&quot;&gt;
6409&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;br /&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
6410 <translation>&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.0//EN&quot; &quot;http://www.w3.org/TR/REC-html40/strict.dtd&quot;&gt;
6411&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
6412p, li { white-space: pre-wrap; }
6413&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:&apos;MS Shell Dlg 2&apos;; font-size:18pt; font-weight:400; font-style:normal;&quot;&gt;
6414&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;br /&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</translation>
6415 </message>
6416</context>
6417<context>
6418 <name>PlayerControlPreview</name>
6419 <message>
6420 <location filename="../../src/yuzu/configuration/configure_input_player_widget.cpp" line="1575"/>
6421 <source>START/PAUSE</source>
6422 <translation>СТАРТ/ПАУЗА</translation>
6423 </message>
6424</context>
6425<context>
6426 <name>QObject</name>
6427 <message>
6428 <location filename="../../src/yuzu/multiplayer/lobby_p.h" line="236"/>
6429 <source>%1 is not playing a game</source>
6430 <translation>%1 не грає у гру</translation>
6431 </message>
6432 <message>
6433 <location filename="../../src/yuzu/multiplayer/lobby_p.h" line="238"/>
6434 <source>%1 is playing %2</source>
6435 <translation>%1 грає в %2</translation>
6436 </message>
6437 <message>
6438 <location filename="../../src/yuzu/multiplayer/chat_room.cpp" line="142"/>
6439 <source>Not playing a game</source>
6440 <translation>Не грає в гру</translation>
6441 </message>
6442 <message>
6443 <location filename="../../src/yuzu/game_list_p.h" line="242"/>
6444 <source>Installed SD Titles</source>
6445 <translation>Встановлені SD ігри</translation>
6446 </message>
6447 <message>
6448 <location filename="../../src/yuzu/game_list_p.h" line="250"/>
6449 <source>Installed NAND Titles</source>
6450 <translation>Встановлені NAND ігри</translation>
6451 </message>
6452 <message>
6453 <location filename="../../src/yuzu/game_list_p.h" line="258"/>
6454 <source>System Titles</source>
6455 <translation>Системні ігри</translation>
6456 </message>
6457 <message>
6458 <location filename="../../src/yuzu/game_list_p.h" line="301"/>
6459 <source>Add New Game Directory</source>
6460 <translation>Додати нову папку з іграми</translation>
6461 </message>
6462 <message>
6463 <location filename="../../src/yuzu/game_list_p.h" line="324"/>
6464 <source>Favorites</source>
6465 <translation>Улюблені</translation>
6466 </message>
6467 <message>
6468 <location filename="../../src/yuzu/configuration/configure_touch_from_button.cpp" line="21"/>
6469 <location filename="../../src/yuzu/configuration/configure_ringcon.cpp" line="30"/>
6470 <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="41"/>
6471 <source>Shift</source>
6472 <translation>Shift</translation>
6473 </message>
6474 <message>
6475 <location filename="../../src/yuzu/configuration/configure_touch_from_button.cpp" line="23"/>
6476 <location filename="../../src/yuzu/configuration/configure_ringcon.cpp" line="32"/>
6477 <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="43"/>
6478 <source>Ctrl</source>
6479 <translation>Ctrl</translation>
6480 </message>
6481 <message>
6482 <location filename="../../src/yuzu/configuration/configure_touch_from_button.cpp" line="25"/>
6483 <location filename="../../src/yuzu/configuration/configure_ringcon.cpp" line="34"/>
6484 <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="45"/>
6485 <source>Alt</source>
6486 <translation>Alt</translation>
6487 </message>
6488 <message>
6489 <location filename="../../src/yuzu/configuration/configure_touch_from_button.cpp" line="35"/>
6490 <location filename="../../src/yuzu/configuration/configure_ringcon.cpp" line="318"/>
6491 <location filename="../../src/yuzu/configuration/configure_ringcon.cpp" line="384"/>
6492 <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="159"/>
6493 <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="226"/>
6494 <source>[not set]</source>
6495 <translation>[не задано]</translation>
6496 </message>
6497 <message>
6498 <location filename="../../src/yuzu/configuration/configure_touch_from_button.cpp" line="47"/>
6499 <source>Hat %1 %2</source>
6500 <translation>Напр. %1 %2</translation>
6501 </message>
6502 <message>
6503 <location filename="../../src/yuzu/configuration/configure_touch_from_button.cpp" line="54"/>
6504 <location filename="../../src/yuzu/configuration/configure_ringcon.cpp" line="407"/>
6505 <location filename="../../src/yuzu/configuration/configure_ringcon.cpp" line="411"/>
6506 <location filename="../../src/yuzu/configuration/configure_ringcon.cpp" line="415"/>
6507 <location filename="../../src/yuzu/configuration/configure_ringcon.cpp" line="419"/>
6508 <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="249"/>
6509 <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="253"/>
6510 <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="257"/>
6511 <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="261"/>
6512 <source>Axis %1%2</source>
6513 <translation>Ось %1%2</translation>
6514 </message>
6515 <message>
6516 <location filename="../../src/yuzu/configuration/configure_touch_from_button.cpp" line="60"/>
6517 <source>Button %1</source>
6518 <translation>Кнопка %1</translation>
6519 </message>
6520 <message>
6521 <location filename="../../src/yuzu/configuration/configure_touch_from_button.cpp" line="66"/>
6522 <location filename="../../src/yuzu/configuration/configure_ringcon.cpp" line="378"/>
6523 <location filename="../../src/yuzu/configuration/configure_ringcon.cpp" line="392"/>
6524 <location filename="../../src/yuzu/configuration/configure_ringcon.cpp" line="422"/>
6525 <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="220"/>
6526 <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="234"/>
6527 <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="264"/>
6528 <source>[unknown]</source>
6529 <translation>[невідомо]</translation>
6530 </message>
6531 <message>
6532 <location filename="../../src/yuzu/configuration/configure_ringcon.cpp" line="45"/>
6533 <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="56"/>
6534 <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="124"/>
6535 <source>Left</source>
6536 <translation>Вліво</translation>
6537 </message>
6538 <message>
6539 <location filename="../../src/yuzu/configuration/configure_ringcon.cpp" line="47"/>
6540 <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="58"/>
6541 <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="127"/>
6542 <source>Right</source>
6543 <translation>Вправо</translation>
6544 </message>
6545 <message>
6546 <location filename="../../src/yuzu/configuration/configure_ringcon.cpp" line="49"/>
6547 <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="60"/>
6548 <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="133"/>
6549 <source>Down</source>
6550 <translation>Вниз</translation>
6551 </message>
6552 <message>
6553 <location filename="../../src/yuzu/configuration/configure_ringcon.cpp" line="51"/>
6554 <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="62"/>
6555 <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="130"/>
6556 <source>Up</source>
6557 <translation>Вгору</translation>
6558 </message>
6559 <message>
6560 <location filename="../../src/yuzu/configuration/configure_ringcon.cpp" line="53"/>
6561 <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="64"/>
6562 <source>Z</source>
6563 <translation>Z</translation>
6564 </message>
6565 <message>
6566 <location filename="../../src/yuzu/configuration/configure_ringcon.cpp" line="55"/>
6567 <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="66"/>
6568 <source>R</source>
6569 <translation>R</translation>
6570 </message>
6571 <message>
6572 <location filename="../../src/yuzu/configuration/configure_ringcon.cpp" line="57"/>
6573 <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="68"/>
6574 <source>L</source>
6575 <translation>L</translation>
6576 </message>
6577 <message>
6578 <location filename="../../src/yuzu/configuration/configure_ringcon.cpp" line="59"/>
6579 <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="70"/>
6580 <source>A</source>
6581 <translation>A</translation>
6582 </message>
6583 <message>
6584 <location filename="../../src/yuzu/configuration/configure_ringcon.cpp" line="61"/>
6585 <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="72"/>
6586 <source>B</source>
6587 <translation>B</translation>
6588 </message>
6589 <message>
6590 <location filename="../../src/yuzu/configuration/configure_ringcon.cpp" line="63"/>
6591 <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="74"/>
6592 <source>X</source>
6593 <translation>X</translation>
6594 </message>
6595 <message>
6596 <location filename="../../src/yuzu/configuration/configure_ringcon.cpp" line="65"/>
6597 <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="76"/>
6598 <source>Y</source>
6599 <translation>Y</translation>
6600 </message>
6601 <message>
6602 <location filename="../../src/yuzu/configuration/configure_ringcon.cpp" line="67"/>
6603 <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="78"/>
6604 <source>Start</source>
6605 <translation>Start</translation>
6606 </message>
6607 <message>
6608 <location filename="../../src/yuzu/configuration/configure_ringcon.cpp" line="69"/>
6609 <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="80"/>
6610 <source>L1</source>
6611 <translation>L1</translation>
6612 </message>
6613 <message>
6614 <location filename="../../src/yuzu/configuration/configure_ringcon.cpp" line="71"/>
6615 <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="82"/>
6616 <source>L2</source>
6617 <translation>L2</translation>
6618 </message>
6619 <message>
6620 <location filename="../../src/yuzu/configuration/configure_ringcon.cpp" line="73"/>
6621 <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="84"/>
6622 <source>L3</source>
6623 <translation>L3</translation>
6624 </message>
6625 <message>
6626 <location filename="../../src/yuzu/configuration/configure_ringcon.cpp" line="75"/>
6627 <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="86"/>
6628 <source>R1</source>
6629 <translation>R1</translation>
6630 </message>
6631 <message>
6632 <location filename="../../src/yuzu/configuration/configure_ringcon.cpp" line="77"/>
6633 <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="88"/>
6634 <source>R2</source>
6635 <translation>R2</translation>
6636 </message>
6637 <message>
6638 <location filename="../../src/yuzu/configuration/configure_ringcon.cpp" line="79"/>
6639 <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="90"/>
6640 <source>R3</source>
6641 <translation>R3</translation>
6642 </message>
6643 <message>
6644 <location filename="../../src/yuzu/configuration/configure_ringcon.cpp" line="81"/>
6645 <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="92"/>
6646 <source>Circle</source>
6647 <translation>Кружечок</translation>
6648 </message>
6649 <message>
6650 <location filename="../../src/yuzu/configuration/configure_ringcon.cpp" line="83"/>
6651 <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="94"/>
6652 <source>Cross</source>
6653 <translation>Хрестик</translation>
6654 </message>
6655 <message>
6656 <location filename="../../src/yuzu/configuration/configure_ringcon.cpp" line="85"/>
6657 <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="96"/>
6658 <source>Square</source>
6659 <translation>Квадратик</translation>
6660 </message>
6661 <message>
6662 <location filename="../../src/yuzu/configuration/configure_ringcon.cpp" line="87"/>
6663 <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="98"/>
6664 <source>Triangle</source>
6665 <translation>Трикутничок</translation>
6666 </message>
6667 <message>
6668 <location filename="../../src/yuzu/configuration/configure_ringcon.cpp" line="89"/>
6669 <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="100"/>
6670 <source>Share</source>
6671 <translation>Share</translation>
6672 </message>
6673 <message>
6674 <location filename="../../src/yuzu/configuration/configure_ringcon.cpp" line="91"/>
6675 <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="102"/>
6676 <source>Options</source>
6677 <translation>Options</translation>
6678 </message>
6679 <message>
6680 <location filename="../../src/yuzu/configuration/configure_ringcon.cpp" line="93"/>
6681 <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="118"/>
6682 <source>[undefined]</source>
6683 <translation>[невизначено]</translation>
6684 </message>
6685 <message>
6686 <location filename="../../src/yuzu/configuration/configure_ringcon.cpp" line="328"/>
6687 <source>%1%2</source>
6688 <translation>%1%2</translation>
6689 </message>
6690 <message>
6691 <location filename="../../src/yuzu/configuration/configure_ringcon.cpp" line="332"/>
6692 <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="174"/>
6693 <source>[invalid]</source>
6694 <translation>[неприпустимо]</translation>
6695 </message>
6696 <message>
6697 <location filename="../../src/yuzu/configuration/configure_ringcon.cpp" line="342"/>
6698 <location filename="../../src/yuzu/configuration/configure_ringcon.cpp" line="366"/>
6699 <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="184"/>
6700 <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="208"/>
6701 <source>%1%2Hat %3</source>
6702 <translation>%1%2Напр. %3</translation>
6703 </message>
6704 <message>
6705 <location filename="../../src/yuzu/configuration/configure_ringcon.cpp" line="346"/>
6706 <location filename="../../src/yuzu/configuration/configure_ringcon.cpp" line="369"/>
6707 <location filename="../../src/yuzu/configuration/configure_ringcon.cpp" line="372"/>
6708 <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="188"/>
6709 <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="211"/>
6710 <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="214"/>
6711 <source>%1%2Axis %3</source>
6712 <translation>%1%2Ось %3</translation>
6713 </message>
6714 <message>
6715 <location filename="../../src/yuzu/configuration/configure_ringcon.cpp" line="352"/>
6716 <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="194"/>
6717 <source>%1%2Axis %3,%4,%5</source>
6718 <translation>%1%2Ось %3,%4,%5</translation>
6719 </message>
6720 <message>
6721 <location filename="../../src/yuzu/configuration/configure_ringcon.cpp" line="356"/>
6722 <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="198"/>
6723 <source>%1%2Motion %3</source>
6724 <translation>%1%2Рух %3</translation>
6725 </message>
6726 <message>
6727 <location filename="../../src/yuzu/configuration/configure_ringcon.cpp" line="360"/>
6728 <location filename="../../src/yuzu/configuration/configure_ringcon.cpp" line="375"/>
6729 <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="202"/>
6730 <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="217"/>
6731 <source>%1%2Button %3</source>
6732 <translation>%1%2Кнопка %3</translation>
6733 </message>
6734 <message>
6735 <location filename="../../src/yuzu/configuration/configure_ringcon.cpp" line="402"/>
6736 <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="244"/>
6737 <source>[unused]</source>
6738 <translation>[не використаний]</translation>
6739 </message>
6740 <message>
6741 <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="104"/>
6742 <source>Home</source>
6743 <translation>Home</translation>
6744 </message>
6745 <message>
6746 <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="106"/>
6747 <source>Touch</source>
6748 <translation>Сенсор</translation>
6749 </message>
6750 <message>
6751 <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="108"/>
6752 <source>Wheel</source>
6753 <comment>Indicates the mouse wheel</comment>
6754 <translation>Коліщатко</translation>
6755 </message>
6756 <message>
6757 <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="110"/>
6758 <source>Backward</source>
6759 <translation>Назад</translation>
6760 </message>
6761 <message>
6762 <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="112"/>
6763 <source>Forward</source>
6764 <translation>Вперед</translation>
6765 </message>
6766 <message>
6767 <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="114"/>
6768 <source>Task</source>
6769 <translation>Задача</translation>
6770 </message>
6771 <message>
6772 <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="116"/>
6773 <source>Extra</source>
6774 <translation>Додаткова</translation>
6775 </message>
6776 <message>
6777 <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="170"/>
6778 <source>%1%2%3</source>
6779 <translation>%1%2%3</translation>
6780 </message>
6781</context>
6782<context>
6783 <name>QtControllerSelectorDialog</name>
6784 <message>
6785 <location filename="../../src/yuzu/applets/qt_controller.ui" line="14"/>
6786 <source>Controller Applet</source>
6787 <translation>Аплет контролера</translation>
6788 </message>
6789 <message>
6790 <location filename="../../src/yuzu/applets/qt_controller.ui" line="129"/>
6791 <source>Supported Controller Types:</source>
6792 <translation>Підтримувані типи контролерів:</translation>
6793 </message>
6794 <message>
6795 <location filename="../../src/yuzu/applets/qt_controller.ui" line="282"/>
6796 <source>Players:</source>
6797 <translation>Гравці:</translation>
6798 </message>
6799 <message>
6800 <location filename="../../src/yuzu/applets/qt_controller.ui" line="300"/>
6801 <source>1 - 8</source>
6802 <translation>1 - 8</translation>
6803 </message>
6804 <message>
6805 <location filename="../../src/yuzu/applets/qt_controller.ui" line="418"/>
6806 <source>P4</source>
6807 <translation>P4</translation>
6808 </message>
6809 <message>
6810 <location filename="../../src/yuzu/applets/qt_controller.ui" line="514"/>
6811 <location filename="../../src/yuzu/applets/qt_controller.ui" line="711"/>
6812 <location filename="../../src/yuzu/applets/qt_controller.ui" line="912"/>
6813 <location filename="../../src/yuzu/applets/qt_controller.ui" line="1222"/>
6814 <location filename="../../src/yuzu/applets/qt_controller.ui" line="1459"/>
6815 <location filename="../../src/yuzu/applets/qt_controller.ui" line="1656"/>
6816 <location filename="../../src/yuzu/applets/qt_controller.ui" line="1857"/>
6817 <location filename="../../src/yuzu/applets/qt_controller.ui" line="2054"/>
6818 <location filename="../../src/yuzu/applets/qt_controller.cpp" line="414"/>
6819 <source>Pro Controller</source>
6820 <translation>Контролер Pro</translation>
6821 </message>
6822 <message>
6823 <location filename="../../src/yuzu/applets/qt_controller.ui" line="519"/>
6824 <location filename="../../src/yuzu/applets/qt_controller.ui" line="716"/>
6825 <location filename="../../src/yuzu/applets/qt_controller.ui" line="917"/>
6826 <location filename="../../src/yuzu/applets/qt_controller.ui" line="1227"/>
6827 <location filename="../../src/yuzu/applets/qt_controller.ui" line="1464"/>
6828 <location filename="../../src/yuzu/applets/qt_controller.ui" line="1661"/>
6829 <location filename="../../src/yuzu/applets/qt_controller.ui" line="1862"/>
6830 <location filename="../../src/yuzu/applets/qt_controller.ui" line="2059"/>
6831 <location filename="../../src/yuzu/applets/qt_controller.cpp" line="418"/>
6832 <source>Dual Joycons</source>
6833 <translation>Подвійні Joy-Con&apos;и</translation>
6834 </message>
6835 <message>
6836 <location filename="../../src/yuzu/applets/qt_controller.ui" line="524"/>
6837 <location filename="../../src/yuzu/applets/qt_controller.ui" line="721"/>
6838 <location filename="../../src/yuzu/applets/qt_controller.ui" line="922"/>
6839 <location filename="../../src/yuzu/applets/qt_controller.ui" line="1232"/>
6840 <location filename="../../src/yuzu/applets/qt_controller.ui" line="1469"/>
6841 <location filename="../../src/yuzu/applets/qt_controller.ui" line="1666"/>
6842 <location filename="../../src/yuzu/applets/qt_controller.ui" line="1867"/>
6843 <location filename="../../src/yuzu/applets/qt_controller.ui" line="2064"/>
6844 <location filename="../../src/yuzu/applets/qt_controller.cpp" line="422"/>
6845 <source>Left Joycon</source>
6846 <translation>Лівий Joy-Con</translation>
6847 </message>
6848 <message>
6849 <location filename="../../src/yuzu/applets/qt_controller.ui" line="529"/>
6850 <location filename="../../src/yuzu/applets/qt_controller.ui" line="726"/>
6851 <location filename="../../src/yuzu/applets/qt_controller.ui" line="927"/>
6852 <location filename="../../src/yuzu/applets/qt_controller.ui" line="1237"/>
6853 <location filename="../../src/yuzu/applets/qt_controller.ui" line="1474"/>
6854 <location filename="../../src/yuzu/applets/qt_controller.ui" line="1671"/>
6855 <location filename="../../src/yuzu/applets/qt_controller.ui" line="1872"/>
6856 <location filename="../../src/yuzu/applets/qt_controller.ui" line="2069"/>
6857 <location filename="../../src/yuzu/applets/qt_controller.cpp" line="426"/>
6858 <source>Right Joycon</source>
6859 <translation>Правий Joy-Con</translation>
6860 </message>
6861 <message>
6862 <location filename="../../src/yuzu/applets/qt_controller.ui" line="538"/>
6863 <location filename="../../src/yuzu/applets/qt_controller.ui" line="735"/>
6864 <location filename="../../src/yuzu/applets/qt_controller.ui" line="941"/>
6865 <location filename="../../src/yuzu/applets/qt_controller.ui" line="1246"/>
6866 <location filename="../../src/yuzu/applets/qt_controller.ui" line="1483"/>
6867 <location filename="../../src/yuzu/applets/qt_controller.ui" line="1680"/>
6868 <location filename="../../src/yuzu/applets/qt_controller.ui" line="1881"/>
6869 <location filename="../../src/yuzu/applets/qt_controller.ui" line="2078"/>
6870 <source>Use Current Config</source>
6871 <translation>Використовувати поточну конфігурацію</translation>
6872 </message>
6873 <message>
6874 <location filename="../../src/yuzu/applets/qt_controller.ui" line="615"/>
6875 <source>P2</source>
6876 <translation>P2</translation>
6877 </message>
6878 <message>
6879 <location filename="../../src/yuzu/applets/qt_controller.ui" line="812"/>
6880 <source>P1</source>
6881 <translation>P1</translation>
6882 </message>
6883 <message>
6884 <location filename="../../src/yuzu/applets/qt_controller.ui" line="932"/>
6885 <location filename="../../src/yuzu/applets/qt_controller.ui" line="2303"/>
6886 <location filename="../../src/yuzu/applets/qt_controller.cpp" line="430"/>
6887 <source>Handheld</source>
6888 <translation>Портативний</translation>
6889 </message>
6890 <message>
6891 <location filename="../../src/yuzu/applets/qt_controller.ui" line="1126"/>
6892 <source>P3</source>
6893 <translation>P3</translation>
6894 </message>
6895 <message>
6896 <location filename="../../src/yuzu/applets/qt_controller.ui" line="1363"/>
6897 <source>P7</source>
6898 <translation>P7</translation>
6899 </message>
6900 <message>
6901 <location filename="../../src/yuzu/applets/qt_controller.ui" line="1560"/>
6902 <source>P8</source>
6903 <translation>P8</translation>
6904 </message>
6905 <message>
6906 <location filename="../../src/yuzu/applets/qt_controller.ui" line="1757"/>
6907 <source>P5</source>
6908 <translation>P5</translation>
6909 </message>
6910 <message>
6911 <location filename="../../src/yuzu/applets/qt_controller.ui" line="1958"/>
6912 <source>P6</source>
6913 <translation>P6</translation>
6914 </message>
6915 <message>
6916 <location filename="../../src/yuzu/applets/qt_controller.ui" line="2272"/>
6917 <source>Console Mode</source>
6918 <translation>Режим консолі</translation>
6919 </message>
6920 <message>
6921 <location filename="../../src/yuzu/applets/qt_controller.ui" line="2293"/>
6922 <source>Docked</source>
6923 <translation>У док-станції</translation>
6924 </message>
6925 <message>
6926 <location filename="../../src/yuzu/applets/qt_controller.ui" line="2313"/>
6927 <source>Vibration</source>
6928 <translation>Вібрація</translation>
6929 </message>
6930 <message>
6931 <location filename="../../src/yuzu/applets/qt_controller.ui" line="2349"/>
6932 <location filename="../../src/yuzu/applets/qt_controller.ui" line="2395"/>
6933 <source>Configure</source>
6934 <translation>Налаштувати</translation>
6935 </message>
6936 <message>
6937 <location filename="../../src/yuzu/applets/qt_controller.ui" line="2359"/>
6938 <source>Motion</source>
6939 <translation>Рух</translation>
6940 </message>
6941 <message>
6942 <location filename="../../src/yuzu/applets/qt_controller.ui" line="2405"/>
6943 <source>Profiles</source>
6944 <translation>Профілі</translation>
6945 </message>
6946 <message>
6947 <location filename="../../src/yuzu/applets/qt_controller.ui" line="2432"/>
6948 <source>Create</source>
6949 <translation>Створити</translation>
6950 </message>
6951 <message>
6952 <location filename="../../src/yuzu/applets/qt_controller.ui" line="2467"/>
6953 <source>Controllers</source>
6954 <translation>Контролери</translation>
6955 </message>
6956 <message>
6957 <location filename="../../src/yuzu/applets/qt_controller.ui" line="2481"/>
6958 <source>1</source>
6959 <translation>1</translation>
6960 </message>
6961 <message>
6962 <location filename="../../src/yuzu/applets/qt_controller.ui" line="2508"/>
6963 <source>2</source>
6964 <translation>2</translation>
6965 </message>
6966 <message>
6967 <location filename="../../src/yuzu/applets/qt_controller.ui" line="2518"/>
6968 <source>4</source>
6969 <translation>4</translation>
6970 </message>
6971 <message>
6972 <location filename="../../src/yuzu/applets/qt_controller.ui" line="2528"/>
6973 <source>3</source>
6974 <translation>3</translation>
6975 </message>
6976 <message>
6977 <location filename="../../src/yuzu/applets/qt_controller.ui" line="2538"/>
6978 <source>Connected</source>
6979 <translation>З&apos;єднано</translation>
6980 </message>
6981 <message>
6982 <location filename="../../src/yuzu/applets/qt_controller.ui" line="2552"/>
6983 <source>5</source>
6984 <translation>5</translation>
6985 </message>
6986 <message>
6987 <location filename="../../src/yuzu/applets/qt_controller.ui" line="2569"/>
6988 <source>7</source>
6989 <translation>7</translation>
6990 </message>
6991 <message>
6992 <location filename="../../src/yuzu/applets/qt_controller.ui" line="2586"/>
6993 <source>6</source>
6994 <translation>6</translation>
6995 </message>
6996 <message>
6997 <location filename="../../src/yuzu/applets/qt_controller.ui" line="2596"/>
6998 <source>8</source>
6999 <translation>8</translation>
7000 </message>
7001 <message>
7002 <location filename="../../src/yuzu/applets/qt_controller.cpp" line="434"/>
7003 <source>GameCube Controller</source>
7004 <translation>Контролер GameCube</translation>
7005 </message>
7006 <message>
7007 <location filename="../../src/yuzu/applets/qt_controller.cpp" line="443"/>
7008 <source>Poke Ball Plus</source>
7009 <translation>Poke Ball Plus</translation>
7010 </message>
7011 <message>
7012 <location filename="../../src/yuzu/applets/qt_controller.cpp" line="447"/>
7013 <source>NES Controller</source>
7014 <translation>Контролер NES</translation>
7015 </message>
7016 <message>
7017 <location filename="../../src/yuzu/applets/qt_controller.cpp" line="451"/>
7018 <source>SNES Controller</source>
7019 <translation>Контролер SNES</translation>
7020 </message>
7021 <message>
7022 <location filename="../../src/yuzu/applets/qt_controller.cpp" line="455"/>
7023 <source>N64 Controller</source>
7024 <translation>Контролер N64</translation>
7025 </message>
7026 <message>
7027 <location filename="../../src/yuzu/applets/qt_controller.cpp" line="459"/>
7028 <source>Sega Genesis</source>
7029 <translation>Sega Genesis</translation>
7030 </message>
7031</context>
7032<context>
7033 <name>QtErrorDisplay</name>
7034 <message>
7035 <location filename="../../src/yuzu/applets/qt_error.cpp" line="20"/>
7036 <location filename="../../src/yuzu/applets/qt_error.cpp" line="33"/>
7037 <location filename="../../src/yuzu/applets/qt_error.cpp" line="48"/>
7038 <source>Error Code: %1-%2 (0x%3)</source>
7039 <translation>Код помилки: %1-%2 (0x%3)</translation>
7040 </message>
7041 <message>
7042 <location filename="../../src/yuzu/applets/qt_error.cpp" line="24"/>
7043 <source>An error has occurred.
7044Please try again or contact the developer of the software.</source>
7045 <translation>Сталася помилка.
7046Будь ласка, спробуйте ще раз або зв&apos;яжіться з розробником ПЗ.</translation>
7047 </message>
7048 <message>
7049 <location filename="../../src/yuzu/applets/qt_error.cpp" line="37"/>
7050 <source>An error occurred on %1 at %2.
7051Please try again or contact the developer of the software.</source>
7052 <translation>Сталася помилка на %1 у %2.
7053Будь ласка, спробуйте ще раз або зв&apos;яжіться з розробником ПЗ.</translation>
7054 </message>
7055 <message>
7056 <location filename="../../src/yuzu/applets/qt_error.cpp" line="52"/>
7057 <source>An error has occurred.
7058
7059%1
7060
7061%2</source>
7062 <translation>Сталася помилка.
7063
7064%1
7065
7066%2</translation>
7067 </message>
7068</context>
7069<context>
7070 <name>QtProfileSelectionDialog</name>
7071 <message>
7072 <location filename="../../src/yuzu/applets/qt_profile_select.cpp" line="23"/>
7073 <source>%1
7074%2</source>
7075 <comment>%1 is the profile username, %2 is the formatted UUID (e.g. 00112233-4455-6677-8899-AABBCCDDEEFF))</comment>
7076 <translation>%1
7077%2</translation>
7078 </message>
7079 <message>
7080 <location filename="../../src/yuzu/applets/qt_profile_select.cpp" line="53"/>
7081 <source>Select a user:</source>
7082 <translation>Оберить користувача</translation>
7083 </message>
7084 <message>
7085 <location filename="../../src/yuzu/applets/qt_profile_select.cpp" line="83"/>
7086 <source>Users</source>
7087 <translation>Користувачі</translation>
7088 </message>
7089 <message>
7090 <location filename="../../src/yuzu/applets/qt_profile_select.cpp" line="123"/>
7091 <source>Profile Selector</source>
7092 <translation>Вибір профілю</translation>
7093 </message>
7094</context>
7095<context>
7096 <name>QtSoftwareKeyboardDialog</name>
7097 <message>
7098 <location filename="../../src/yuzu/applets/qt_software_keyboard.ui" line="14"/>
7099 <source>Software Keyboard</source>
7100 <translation>Віртуальна клавіатура</translation>
7101 </message>
7102 <message>
7103 <location filename="../../src/yuzu/applets/qt_software_keyboard.ui" line="199"/>
7104 <source>Enter Text</source>
7105 <translation>Введіть текст</translation>
7106 </message>
7107 <message>
7108 <location filename="../../src/yuzu/applets/qt_software_keyboard.ui" line="479"/>
7109 <source>&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.0//EN&quot; &quot;http://www.w3.org/TR/REC-html40/strict.dtd&quot;&gt;
7110&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
7111p, li { white-space: pre-wrap; }
7112&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:'MS Shell Dlg 2'; font-size:26pt; font-weight:400; font-style:normal;&quot;&gt;
7113&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;br /&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
7114 <translation>&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.0//EN&quot; &quot;http://www.w3.org/TR/REC-html40/strict.dtd&quot;&gt;
7115&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
7116p, li { white-space: pre-wrap; }
7117&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:&apos;MS Shell Dlg 2&apos;; font-size:26pt; font-weight:400; font-style:normal;&quot;&gt;
7118&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;br /&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</translation>
7119 </message>
7120 <message>
7121 <location filename="../../src/yuzu/applets/qt_software_keyboard.cpp" line="403"/>
7122 <location filename="../../src/yuzu/applets/qt_software_keyboard.cpp" line="413"/>
7123 <source>OK</source>
7124 <translation>ОК</translation>
7125 </message>
7126 <message>
7127 <location filename="../../src/yuzu/applets/qt_software_keyboard.cpp" line="413"/>
7128 <source>Cancel</source>
7129 <translation>Скасувати</translation>
7130 </message>
7131</context>
7132<context>
7133 <name>SequenceDialog</name>
7134 <message>
7135 <location filename="../../src/yuzu/util/sequence_dialog/sequence_dialog.cpp" line="10"/>
7136 <source>Enter a hotkey</source>
7137 <translation>Введіть комбінацію</translation>
7138 </message>
7139</context>
7140<context>
7141 <name>WaitTreeCallstack</name>
7142 <message>
7143 <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="147"/>
7144 <source>Call stack</source>
7145 <translation>Стек викликів</translation>
7146 </message>
7147</context>
7148<context>
7149 <name>WaitTreeMutexInfo</name>
7150 <message>
7151 <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="126"/>
7152 <source>waiting for mutex 0x%1</source>
7153 <translation type="unfinished"/>
7154 </message>
7155 <message>
7156 <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="133"/>
7157 <source>has waiters: %1</source>
7158 <translation type="unfinished"/>
7159 </message>
7160 <message>
7161 <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="135"/>
7162 <source>owner handle: 0x%1</source>
7163 <translation type="unfinished"/>
7164 </message>
7165</context>
7166<context>
7167 <name>WaitTreeObjectList</name>
7168 <message>
7169 <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="228"/>
7170 <source>waiting for all objects</source>
7171 <translation type="unfinished"/>
7172 </message>
7173 <message>
7174 <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="229"/>
7175 <source>waiting for one of the following objects</source>
7176 <translation type="unfinished"/>
7177 </message>
7178</context>
7179<context>
7180 <name>WaitTreeSynchronizationObject</name>
7181 <message>
7182 <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="185"/>
7183 <source>[%1] %2 %3</source>
7184 <translation>[%1] %2 %3</translation>
7185 </message>
7186 <message>
7187 <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="212"/>
7188 <source>waited by no thread</source>
7189 <translation type="unfinished"/>
7190 </message>
7191</context>
7192<context>
7193 <name>WaitTreeThread</name>
7194 <message>
7195 <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="250"/>
7196 <source>runnable</source>
7197 <translation type="unfinished"/>
7198 </message>
7199 <message>
7200 <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="252"/>
7201 <source>paused</source>
7202 <translation type="unfinished"/>
7203 </message>
7204 <message>
7205 <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="258"/>
7206 <source>sleeping</source>
7207 <translation type="unfinished"/>
7208 </message>
7209 <message>
7210 <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="261"/>
7211 <source>waiting for IPC reply</source>
7212 <translation type="unfinished"/>
7213 </message>
7214 <message>
7215 <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="264"/>
7216 <source>waiting for objects</source>
7217 <translation type="unfinished"/>
7218 </message>
7219 <message>
7220 <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="267"/>
7221 <source>waiting for condition variable</source>
7222 <translation type="unfinished"/>
7223 </message>
7224 <message>
7225 <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="270"/>
7226 <source>waiting for address arbiter</source>
7227 <translation type="unfinished"/>
7228 </message>
7229 <message>
7230 <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="273"/>
7231 <source>waiting for suspend resume</source>
7232 <translation type="unfinished"/>
7233 </message>
7234 <message>
7235 <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="276"/>
7236 <source>waiting</source>
7237 <translation type="unfinished"/>
7238 </message>
7239 <message>
7240 <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="281"/>
7241 <source>initialized</source>
7242 <translation type="unfinished"/>
7243 </message>
7244 <message>
7245 <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="284"/>
7246 <source>terminated</source>
7247 <translation type="unfinished"/>
7248 </message>
7249 <message>
7250 <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="287"/>
7251 <source>unknown</source>
7252 <translation type="unfinished"/>
7253 </message>
7254 <message>
7255 <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="292"/>
7256 <source> PC = 0x%1 LR = 0x%2</source>
7257 <translation> PC = 0x%1 LR = 0x%2</translation>
7258 </message>
7259 <message>
7260 <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="342"/>
7261 <source>ideal</source>
7262 <translation type="unfinished"/>
7263 </message>
7264 <message>
7265 <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="345"/>
7266 <source>core %1</source>
7267 <translation type="unfinished"/>
7268 </message>
7269 <message>
7270 <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="349"/>
7271 <source>processor = %1</source>
7272 <translation type="unfinished"/>
7273 </message>
7274 <message>
7275 <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="351"/>
7276 <source>ideal core = %1</source>
7277 <translation type="unfinished"/>
7278 </message>
7279 <message>
7280 <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="353"/>
7281 <source>affinity mask = %1</source>
7282 <translation type="unfinished"/>
7283 </message>
7284 <message>
7285 <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="354"/>
7286 <source>thread id = %1</source>
7287 <translation type="unfinished"/>
7288 </message>
7289 <message>
7290 <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="355"/>
7291 <source>priority = %1(current) / %2(normal)</source>
7292 <translation type="unfinished"/>
7293 </message>
7294 <message>
7295 <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="359"/>
7296 <source>last running ticks = %1</source>
7297 <translation type="unfinished"/>
7298 </message>
7299 <message>
7300 <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="367"/>
7301 <source>not waiting for mutex</source>
7302 <translation type="unfinished"/>
7303 </message>
7304</context>
7305<context>
7306 <name>WaitTreeThreadList</name>
7307 <message>
7308 <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="391"/>
7309 <source>waited by thread</source>
7310 <translation type="unfinished"/>
7311 </message>
7312</context>
7313<context>
7314 <name>WaitTreeWidget</name>
7315 <message>
7316 <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="465"/>
7317 <source>&amp;Wait Tree</source>
7318 <translation>[&amp;W] Дерево очікування</translation>
7319 </message>
7320</context>
7321</TS> \ No newline at end of file
diff --git a/externals/CMakeLists.txt b/externals/CMakeLists.txt
index e80fd124e..7f0a6d069 100644
--- a/externals/CMakeLists.txt
+++ b/externals/CMakeLists.txt
@@ -7,15 +7,14 @@ include(DownloadExternals)
7 7
8# xbyak 8# xbyak
9if (ARCHITECTURE_x86 OR ARCHITECTURE_x86_64) 9if (ARCHITECTURE_x86 OR ARCHITECTURE_x86_64)
10 add_library(xbyak INTERFACE) 10 add_subdirectory(xbyak)
11 file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/xbyak/include)
12 file(COPY ${CMAKE_CURRENT_SOURCE_DIR}/xbyak/xbyak DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/xbyak/include)
13 target_include_directories(xbyak SYSTEM INTERFACE ${CMAKE_CURRENT_BINARY_DIR}/xbyak/include)
14 target_compile_definitions(xbyak INTERFACE XBYAK_NO_OP_NAMES)
15endif() 11endif()
16 12
17# Dynarmic 13# Dynarmic
18if (ARCHITECTURE_x86_64) 14if (ARCHITECTURE_x86_64 OR ARCHITECTURE_arm64)
15 if (ARCHITECTURE_arm64)
16 set(DYNARMIC_FRONTENDS "A32")
17 endif()
19 set(DYNARMIC_NO_BUNDLED_FMT ON) 18 set(DYNARMIC_NO_BUNDLED_FMT ON)
20 set(DYNARMIC_IGNORE_ASSERTS ON CACHE BOOL "" FORCE) 19 set(DYNARMIC_IGNORE_ASSERTS ON CACHE BOOL "" FORCE)
21 add_subdirectory(dynarmic) 20 add_subdirectory(dynarmic)
diff --git a/externals/dynarmic b/externals/dynarmic
Subproject 2d4602a6516c67d547000d4c80bcc5f74976abd Subproject 424fdb5c5026ec5bdd7553271190397f63fb503
diff --git a/externals/xbyak b/externals/xbyak
Subproject c306b8e5786eeeb87b8925a8af5c3bf057ff5a9 Subproject 348e3e548ebac06d243e5881caec8440e249f65
diff --git a/src/audio_core/CMakeLists.txt b/src/audio_core/CMakeLists.txt
index 0a1f3bf18..8e3a8f5a8 100644
--- a/src/audio_core/CMakeLists.txt
+++ b/src/audio_core/CMakeLists.txt
@@ -217,7 +217,7 @@ else()
217endif() 217endif()
218 218
219target_link_libraries(audio_core PUBLIC common core) 219target_link_libraries(audio_core PUBLIC common core)
220if (ARCHITECTURE_x86_64) 220if (ARCHITECTURE_x86_64 OR ARCHITECTURE_arm64)
221 target_link_libraries(audio_core PRIVATE dynarmic) 221 target_link_libraries(audio_core PRIVATE dynarmic)
222endif() 222endif()
223 223
diff --git a/src/audio_core/in/audio_in_system.cpp b/src/audio_core/in/audio_in_system.cpp
index 6b7e6715c..4324cafd8 100644
--- a/src/audio_core/in/audio_in_system.cpp
+++ b/src/audio_core/in/audio_in_system.cpp
@@ -56,7 +56,7 @@ Result System::IsConfigValid(const std::string_view device_name,
56 return ResultSuccess; 56 return ResultSuccess;
57} 57}
58 58
59Result System::Initialize(std::string& device_name, const AudioInParameter& in_params, 59Result System::Initialize(std::string device_name, const AudioInParameter& in_params,
60 const u32 handle_, const u64 applet_resource_user_id_) { 60 const u32 handle_, const u64 applet_resource_user_id_) {
61 auto result{IsConfigValid(device_name, in_params)}; 61 auto result{IsConfigValid(device_name, in_params)};
62 if (result.IsError()) { 62 if (result.IsError()) {
diff --git a/src/audio_core/in/audio_in_system.h b/src/audio_core/in/audio_in_system.h
index b9dc0e60f..1c5154638 100644
--- a/src/audio_core/in/audio_in_system.h
+++ b/src/audio_core/in/audio_in_system.h
@@ -97,7 +97,7 @@ public:
97 * @param applet_resource_user_id - Unused. 97 * @param applet_resource_user_id - Unused.
98 * @return Result code. 98 * @return Result code.
99 */ 99 */
100 Result Initialize(std::string& device_name, const AudioInParameter& in_params, u32 handle, 100 Result Initialize(std::string device_name, const AudioInParameter& in_params, u32 handle,
101 u64 applet_resource_user_id); 101 u64 applet_resource_user_id);
102 102
103 /** 103 /**
diff --git a/src/audio_core/out/audio_out_system.cpp b/src/audio_core/out/audio_out_system.cpp
index 48a801923..a66208ed9 100644
--- a/src/audio_core/out/audio_out_system.cpp
+++ b/src/audio_core/out/audio_out_system.cpp
@@ -49,8 +49,8 @@ Result System::IsConfigValid(std::string_view device_name,
49 return Service::Audio::ERR_INVALID_CHANNEL_COUNT; 49 return Service::Audio::ERR_INVALID_CHANNEL_COUNT;
50} 50}
51 51
52Result System::Initialize(std::string& device_name, const AudioOutParameter& in_params, u32 handle_, 52Result System::Initialize(std::string device_name, const AudioOutParameter& in_params, u32 handle_,
53 u64& applet_resource_user_id_) { 53 u64 applet_resource_user_id_) {
54 auto result = IsConfigValid(device_name, in_params); 54 auto result = IsConfigValid(device_name, in_params);
55 if (result.IsError()) { 55 if (result.IsError()) {
56 return result; 56 return result;
diff --git a/src/audio_core/out/audio_out_system.h b/src/audio_core/out/audio_out_system.h
index 0817b2f37..b95cb91be 100644
--- a/src/audio_core/out/audio_out_system.h
+++ b/src/audio_core/out/audio_out_system.h
@@ -88,8 +88,8 @@ public:
88 * @param applet_resource_user_id - Unused. 88 * @param applet_resource_user_id - Unused.
89 * @return Result code. 89 * @return Result code.
90 */ 90 */
91 Result Initialize(std::string& device_name, const AudioOutParameter& in_params, u32 handle, 91 Result Initialize(std::string device_name, const AudioOutParameter& in_params, u32 handle,
92 u64& applet_resource_user_id); 92 u64 applet_resource_user_id);
93 93
94 /** 94 /**
95 * Start this system. 95 * Start this system.
diff --git a/src/common/common_funcs.h b/src/common/common_funcs.h
index e1e2a90fc..0dad9338a 100644
--- a/src/common/common_funcs.h
+++ b/src/common/common_funcs.h
@@ -31,8 +31,10 @@
31 31
32#ifndef _MSC_VER 32#ifndef _MSC_VER
33 33
34#ifdef ARCHITECTURE_x86_64 34#if defined(ARCHITECTURE_x86_64)
35#define Crash() __asm__ __volatile__("int $3") 35#define Crash() __asm__ __volatile__("int $3")
36#elif defined(ARCHITECTURE_arm64)
37#define Crash() __asm__ __volatile__("brk #0")
36#else 38#else
37#define Crash() exit(1) 39#define Crash() exit(1)
38#endif 40#endif
diff --git a/src/common/concepts.h b/src/common/concepts.h
index e8ce30dfe..a9acff3e7 100644
--- a/src/common/concepts.h
+++ b/src/common/concepts.h
@@ -3,24 +3,14 @@
3 3
4#pragma once 4#pragma once
5 5
6#include <iterator>
6#include <type_traits> 7#include <type_traits>
7 8
8namespace Common { 9namespace Common {
9 10
10// Check if type is like an STL container 11// Check if type satisfies the ContiguousContainer named requirement.
11template <typename T> 12template <typename T>
12concept IsSTLContainer = requires(T t) { 13concept IsContiguousContainer = std::contiguous_iterator<typename T::iterator>;
13 typename T::value_type;
14 typename T::iterator;
15 typename T::const_iterator;
16 // TODO(ogniK): Replace below is std::same_as<void> when MSVC supports it.
17 t.begin();
18 t.end();
19 t.cbegin();
20 t.cend();
21 t.data();
22 t.size();
23};
24 14
25// TODO: Replace with std::derived_from when the <concepts> header 15// TODO: Replace with std::derived_from when the <concepts> header
26// is available on all supported platforms. 16// is available on all supported platforms.
diff --git a/src/common/fs/file.h b/src/common/fs/file.h
index 69b53384c..167c4d826 100644
--- a/src/common/fs/file.h
+++ b/src/common/fs/file.h
@@ -209,8 +209,8 @@ public:
209 209
210 /** 210 /**
211 * Helper function which deduces the value type of a contiguous STL container used in ReadSpan. 211 * Helper function which deduces the value type of a contiguous STL container used in ReadSpan.
212 * If T is not a contiguous STL container as defined by the concept IsSTLContainer, this calls 212 * If T is not a contiguous container as defined by the concept IsContiguousContainer, this
213 * ReadObject and T must be a trivially copyable object. 213 * calls ReadObject and T must be a trivially copyable object.
214 * 214 *
215 * See ReadSpan for more details if T is a contiguous container. 215 * See ReadSpan for more details if T is a contiguous container.
216 * See ReadObject for more details if T is a trivially copyable object. 216 * See ReadObject for more details if T is a trivially copyable object.
@@ -223,7 +223,7 @@ public:
223 */ 223 */
224 template <typename T> 224 template <typename T>
225 [[nodiscard]] size_t Read(T& data) const { 225 [[nodiscard]] size_t Read(T& data) const {
226 if constexpr (IsSTLContainer<T>) { 226 if constexpr (IsContiguousContainer<T>) {
227 using ContiguousType = typename T::value_type; 227 using ContiguousType = typename T::value_type;
228 static_assert(std::is_trivially_copyable_v<ContiguousType>, 228 static_assert(std::is_trivially_copyable_v<ContiguousType>,
229 "Data type must be trivially copyable."); 229 "Data type must be trivially copyable.");
@@ -235,8 +235,8 @@ public:
235 235
236 /** 236 /**
237 * Helper function which deduces the value type of a contiguous STL container used in WriteSpan. 237 * Helper function which deduces the value type of a contiguous STL container used in WriteSpan.
238 * If T is not a contiguous STL container as defined by the concept IsSTLContainer, this calls 238 * If T is not a contiguous STL container as defined by the concept IsContiguousContainer, this
239 * WriteObject and T must be a trivially copyable object. 239 * calls WriteObject and T must be a trivially copyable object.
240 * 240 *
241 * See WriteSpan for more details if T is a contiguous container. 241 * See WriteSpan for more details if T is a contiguous container.
242 * See WriteObject for more details if T is a trivially copyable object. 242 * See WriteObject for more details if T is a trivially copyable object.
@@ -249,7 +249,7 @@ public:
249 */ 249 */
250 template <typename T> 250 template <typename T>
251 [[nodiscard]] size_t Write(const T& data) const { 251 [[nodiscard]] size_t Write(const T& data) const {
252 if constexpr (IsSTLContainer<T>) { 252 if constexpr (IsContiguousContainer<T>) {
253 using ContiguousType = typename T::value_type; 253 using ContiguousType = typename T::value_type;
254 static_assert(std::is_trivially_copyable_v<ContiguousType>, 254 static_assert(std::is_trivially_copyable_v<ContiguousType>,
255 "Data type must be trivially copyable."); 255 "Data type must be trivially copyable.");
diff --git a/src/common/host_memory.cpp b/src/common/host_memory.cpp
index 7f9659612..909f6cf3f 100644
--- a/src/common/host_memory.cpp
+++ b/src/common/host_memory.cpp
@@ -359,6 +359,12 @@ public:
359 } 359 }
360 }); 360 });
361 361
362 long page_size = sysconf(_SC_PAGESIZE);
363 if (page_size != 0x1000) {
364 LOG_CRITICAL(HW_Memory, "page size {:#x} is incompatible with 4K paging", page_size);
365 throw std::bad_alloc{};
366 }
367
362 // Backing memory initialization 368 // Backing memory initialization
363#if defined(__FreeBSD__) && __FreeBSD__ < 13 369#if defined(__FreeBSD__) && __FreeBSD__ < 13
364 // XXX Drop after FreeBSD 12.* reaches EOL on 2024-06-30 370 // XXX Drop after FreeBSD 12.* reaches EOL on 2024-06-30
diff --git a/src/common/input.h b/src/common/input.h
index b533f3844..cb30b7254 100644
--- a/src/common/input.h
+++ b/src/common/input.h
@@ -100,7 +100,6 @@ enum class CameraError {
100enum class VibrationAmplificationType { 100enum class VibrationAmplificationType {
101 Linear, 101 Linear,
102 Exponential, 102 Exponential,
103 Test,
104}; 103};
105 104
106// Analog properties for calibration 105// Analog properties for calibration
@@ -325,6 +324,10 @@ public:
325 return VibrationError::NotSupported; 324 return VibrationError::NotSupported;
326 } 325 }
327 326
327 virtual bool IsVibrationEnabled() {
328 return false;
329 }
330
328 virtual PollingError SetPollingMode([[maybe_unused]] PollingMode polling_mode) { 331 virtual PollingError SetPollingMode([[maybe_unused]] PollingMode polling_mode) {
329 return PollingError::NotSupported; 332 return PollingError::NotSupported;
330 } 333 }
diff --git a/src/common/settings.cpp b/src/common/settings.cpp
index 0a560ebb7..8173462cb 100644
--- a/src/common/settings.cpp
+++ b/src/common/settings.cpp
@@ -151,6 +151,7 @@ void UpdateRescalingInfo() {
151 ASSERT(false); 151 ASSERT(false);
152 info.up_scale = 1; 152 info.up_scale = 1;
153 info.down_shift = 0; 153 info.down_shift = 0;
154 break;
154 } 155 }
155 info.up_factor = static_cast<f32>(info.up_scale) / (1U << info.down_shift); 156 info.up_factor = static_cast<f32>(info.up_scale) / (1U << info.down_shift);
156 info.down_factor = static_cast<f32>(1U << info.down_shift) / info.up_scale; 157 info.down_factor = static_cast<f32>(1U << info.down_shift) / info.up_scale;
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt
index 113e663b5..f67f1ce92 100644
--- a/src/core/CMakeLists.txt
+++ b/src/core/CMakeLists.txt
@@ -190,11 +190,13 @@ add_library(core STATIC
190 hle/kernel/k_code_memory.h 190 hle/kernel/k_code_memory.h
191 hle/kernel/k_condition_variable.cpp 191 hle/kernel/k_condition_variable.cpp
192 hle/kernel/k_condition_variable.h 192 hle/kernel/k_condition_variable.h
193 hle/kernel/k_debug.h
193 hle/kernel/k_dynamic_page_manager.h 194 hle/kernel/k_dynamic_page_manager.h
194 hle/kernel/k_dynamic_resource_manager.h 195 hle/kernel/k_dynamic_resource_manager.h
195 hle/kernel/k_dynamic_slab_heap.h 196 hle/kernel/k_dynamic_slab_heap.h
196 hle/kernel/k_event.cpp 197 hle/kernel/k_event.cpp
197 hle/kernel/k_event.h 198 hle/kernel/k_event.h
199 hle/kernel/k_event_info.h
198 hle/kernel/k_handle_table.cpp 200 hle/kernel/k_handle_table.cpp
199 hle/kernel/k_handle_table.h 201 hle/kernel/k_handle_table.h
200 hle/kernel/k_interrupt_manager.cpp 202 hle/kernel/k_interrupt_manager.cpp
@@ -222,6 +224,8 @@ add_library(core STATIC
222 hle/kernel/k_page_group.h 224 hle/kernel/k_page_group.h
223 hle/kernel/k_page_table.cpp 225 hle/kernel/k_page_table.cpp
224 hle/kernel/k_page_table.h 226 hle/kernel/k_page_table.h
227 hle/kernel/k_page_table_manager.h
228 hle/kernel/k_page_table_slab_heap.h
225 hle/kernel/k_port.cpp 229 hle/kernel/k_port.cpp
226 hle/kernel/k_port.h 230 hle/kernel/k_port.h
227 hle/kernel/k_priority_queue.h 231 hle/kernel/k_priority_queue.h
@@ -254,6 +258,8 @@ add_library(core STATIC
254 hle/kernel/k_synchronization_object.cpp 258 hle/kernel/k_synchronization_object.cpp
255 hle/kernel/k_synchronization_object.h 259 hle/kernel/k_synchronization_object.h
256 hle/kernel/k_system_control.h 260 hle/kernel/k_system_control.h
261 hle/kernel/k_system_resource.cpp
262 hle/kernel/k_system_resource.h
257 hle/kernel/k_thread.cpp 263 hle/kernel/k_thread.cpp
258 hle/kernel/k_thread.h 264 hle/kernel/k_thread.h
259 hle/kernel/k_thread_local_page.cpp 265 hle/kernel/k_thread_local_page.cpp
@@ -491,10 +497,6 @@ add_library(core STATIC
491 hle/service/hid/irsensor/processor_base.h 497 hle/service/hid/irsensor/processor_base.h
492 hle/service/hid/irsensor/tera_plugin_processor.cpp 498 hle/service/hid/irsensor/tera_plugin_processor.cpp
493 hle/service/hid/irsensor/tera_plugin_processor.h 499 hle/service/hid/irsensor/tera_plugin_processor.h
494 hle/service/jit/jit_context.cpp
495 hle/service/jit/jit_context.h
496 hle/service/jit/jit.cpp
497 hle/service/jit/jit.h
498 hle/service/lbl/lbl.cpp 500 hle/service/lbl/lbl.cpp
499 hle/service/lbl/lbl.h 501 hle/service/lbl/lbl.h
500 hle/service/ldn/lan_discovery.cpp 502 hle/service/ldn/lan_discovery.cpp
@@ -799,14 +801,18 @@ if (ENABLE_WEB_SERVICE)
799 target_link_libraries(core PRIVATE web_service) 801 target_link_libraries(core PRIVATE web_service)
800endif() 802endif()
801 803
802if (ARCHITECTURE_x86_64) 804if (ARCHITECTURE_x86_64 OR ARCHITECTURE_arm64)
803 target_sources(core PRIVATE 805 target_sources(core PRIVATE
804 arm/dynarmic/arm_dynarmic_32.cpp
805 arm/dynarmic/arm_dynarmic_32.h
806 arm/dynarmic/arm_dynarmic_64.cpp 806 arm/dynarmic/arm_dynarmic_64.cpp
807 arm/dynarmic/arm_dynarmic_64.h 807 arm/dynarmic/arm_dynarmic_64.h
808 arm/dynarmic/arm_dynarmic_32.cpp
809 arm/dynarmic/arm_dynarmic_32.h
808 arm/dynarmic/arm_dynarmic_cp15.cpp 810 arm/dynarmic/arm_dynarmic_cp15.cpp
809 arm/dynarmic/arm_dynarmic_cp15.h 811 arm/dynarmic/arm_dynarmic_cp15.h
812 hle/service/jit/jit_context.cpp
813 hle/service/jit/jit_context.h
814 hle/service/jit/jit.cpp
815 hle/service/jit/jit.h
810 ) 816 )
811 target_link_libraries(core PRIVATE dynarmic) 817 target_link_libraries(core PRIVATE dynarmic)
812endif() 818endif()
diff --git a/src/core/arm/dynarmic/arm_dynarmic_32.cpp b/src/core/arm/dynarmic/arm_dynarmic_32.cpp
index d1e70f19d..227e06ea1 100644
--- a/src/core/arm/dynarmic/arm_dynarmic_32.cpp
+++ b/src/core/arm/dynarmic/arm_dynarmic_32.cpp
@@ -301,6 +301,11 @@ std::shared_ptr<Dynarmic::A32::Jit> ARM_Dynarmic_32::MakeJit(Common::PageTable*
301 } 301 }
302 } 302 }
303 303
304#ifdef ARCHITECTURE_arm64
305 // TODO: remove when fixed in dynarmic
306 config.optimizations &= ~Dynarmic::OptimizationFlag::BlockLinking;
307#endif
308
304 return std::make_unique<Dynarmic::A32::Jit>(config); 309 return std::make_unique<Dynarmic::A32::Jit>(config);
305} 310}
306 311
@@ -450,7 +455,7 @@ std::vector<ARM_Interface::BacktraceEntry> ARM_Dynarmic_32::GetBacktrace(Core::S
450 // Frame records are two words long: 455 // Frame records are two words long:
451 // fp+0 : pointer to previous frame record 456 // fp+0 : pointer to previous frame record
452 // fp+4 : value of lr for frame 457 // fp+4 : value of lr for frame
453 while (true) { 458 for (size_t i = 0; i < 256; i++) {
454 out.push_back({"", 0, lr, 0, ""}); 459 out.push_back({"", 0, lr, 0, ""});
455 if (!fp || (fp % 4 != 0) || !memory.IsValidVirtualAddressRange(fp, 8)) { 460 if (!fp || (fp % 4 != 0) || !memory.IsValidVirtualAddressRange(fp, 8)) {
456 break; 461 break;
diff --git a/src/core/arm/dynarmic/arm_dynarmic_64.cpp b/src/core/arm/dynarmic/arm_dynarmic_64.cpp
index 22b5d5656..afb7fb3a0 100644
--- a/src/core/arm/dynarmic/arm_dynarmic_64.cpp
+++ b/src/core/arm/dynarmic/arm_dynarmic_64.cpp
@@ -517,7 +517,7 @@ std::vector<ARM_Interface::BacktraceEntry> ARM_Dynarmic_64::GetBacktrace(Core::S
517 // Frame records are two words long: 517 // Frame records are two words long:
518 // fp+0 : pointer to previous frame record 518 // fp+0 : pointer to previous frame record
519 // fp+8 : value of lr for frame 519 // fp+8 : value of lr for frame
520 while (true) { 520 for (size_t i = 0; i < 256; i++) {
521 out.push_back({"", 0, lr, 0, ""}); 521 out.push_back({"", 0, lr, 0, ""});
522 if (!fp || (fp % 4 != 0) || !memory.IsValidVirtualAddressRange(fp, 16)) { 522 if (!fp || (fp % 4 != 0) || !memory.IsValidVirtualAddressRange(fp, 16)) {
523 break; 523 break;
diff --git a/src/core/arm/dynarmic/arm_dynarmic_cp15.cpp b/src/core/arm/dynarmic/arm_dynarmic_cp15.cpp
index 200efe4db..5a4eba3eb 100644
--- a/src/core/arm/dynarmic/arm_dynarmic_cp15.cpp
+++ b/src/core/arm/dynarmic/arm_dynarmic_cp15.cpp
@@ -52,12 +52,16 @@ CallbackOrAccessOneWord DynarmicCP15::CompileSendOneWord(bool two, unsigned opc1
52 case 4: 52 case 4:
53 // CP15_DATA_SYNC_BARRIER 53 // CP15_DATA_SYNC_BARRIER
54 return Callback{ 54 return Callback{
55 [](Dynarmic::A32::Jit*, void*, std::uint32_t, std::uint32_t) -> std::uint64_t { 55 [](void*, std::uint32_t, std::uint32_t) -> std::uint64_t {
56#ifdef _MSC_VER 56#if defined(_MSC_VER) && defined(ARCHITECTURE_x86_64)
57 _mm_mfence(); 57 _mm_mfence();
58 _mm_lfence(); 58 _mm_lfence();
59#else 59#elif defined(ARCHITECTURE_x86_64)
60 asm volatile("mfence\n\tlfence\n\t" : : : "memory"); 60 asm volatile("mfence\n\tlfence\n\t" : : : "memory");
61#elif defined(ARCHITECTURE_arm64)
62 asm volatile("dsb sy\n\t" : : : "memory");
63#else
64#error Unsupported architecture
61#endif 65#endif
62 return 0; 66 return 0;
63 }, 67 },
@@ -66,11 +70,15 @@ CallbackOrAccessOneWord DynarmicCP15::CompileSendOneWord(bool two, unsigned opc1
66 case 5: 70 case 5:
67 // CP15_DATA_MEMORY_BARRIER 71 // CP15_DATA_MEMORY_BARRIER
68 return Callback{ 72 return Callback{
69 [](Dynarmic::A32::Jit*, void*, std::uint32_t, std::uint32_t) -> std::uint64_t { 73 [](void*, std::uint32_t, std::uint32_t) -> std::uint64_t {
70#ifdef _MSC_VER 74#if defined(_MSC_VER) && defined(ARCHITECTURE_x86_64)
71 _mm_mfence(); 75 _mm_mfence();
72#else 76#elif defined(ARCHITECTURE_x86_64)
73 asm volatile("mfence\n\t" : : : "memory"); 77 asm volatile("mfence\n\t" : : : "memory");
78#elif defined(ARCHITECTURE_arm64)
79 asm volatile("dmb sy\n\t" : : : "memory");
80#else
81#error Unsupported architecture
74#endif 82#endif
75 return 0; 83 return 0;
76 }, 84 },
@@ -115,7 +123,7 @@ CallbackOrAccessOneWord DynarmicCP15::CompileGetOneWord(bool two, unsigned opc1,
115CallbackOrAccessTwoWords DynarmicCP15::CompileGetTwoWords(bool two, unsigned opc, CoprocReg CRm) { 123CallbackOrAccessTwoWords DynarmicCP15::CompileGetTwoWords(bool two, unsigned opc, CoprocReg CRm) {
116 if (!two && opc == 0 && CRm == CoprocReg::C14) { 124 if (!two && opc == 0 && CRm == CoprocReg::C14) {
117 // CNTPCT 125 // CNTPCT
118 const auto callback = [](Dynarmic::A32::Jit*, void* arg, u32, u32) -> u64 { 126 const auto callback = [](void* arg, u32, u32) -> u64 {
119 const auto& parent_arg = *static_cast<ARM_Dynarmic_32*>(arg); 127 const auto& parent_arg = *static_cast<ARM_Dynarmic_32*>(arg);
120 return parent_arg.system.CoreTiming().GetClockTicks(); 128 return parent_arg.system.CoreTiming().GetClockTicks();
121 }; 129 };
diff --git a/src/core/arm/exclusive_monitor.cpp b/src/core/arm/exclusive_monitor.cpp
index 2db0b035d..20550faeb 100644
--- a/src/core/arm/exclusive_monitor.cpp
+++ b/src/core/arm/exclusive_monitor.cpp
@@ -1,7 +1,7 @@
1// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project 1// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later 2// SPDX-License-Identifier: GPL-2.0-or-later
3 3
4#ifdef ARCHITECTURE_x86_64 4#if defined(ARCHITECTURE_x86_64) || defined(ARCHITECTURE_arm64)
5#include "core/arm/dynarmic/arm_exclusive_monitor.h" 5#include "core/arm/dynarmic/arm_exclusive_monitor.h"
6#endif 6#endif
7#include "core/arm/exclusive_monitor.h" 7#include "core/arm/exclusive_monitor.h"
@@ -13,7 +13,7 @@ ExclusiveMonitor::~ExclusiveMonitor() = default;
13 13
14std::unique_ptr<Core::ExclusiveMonitor> MakeExclusiveMonitor(Memory::Memory& memory, 14std::unique_ptr<Core::ExclusiveMonitor> MakeExclusiveMonitor(Memory::Memory& memory,
15 std::size_t num_cores) { 15 std::size_t num_cores) {
16#ifdef ARCHITECTURE_x86_64 16#if defined(ARCHITECTURE_x86_64) || defined(ARCHITECTURE_arm64)
17 return std::make_unique<Core::DynarmicExclusiveMonitor>(memory, num_cores); 17 return std::make_unique<Core::DynarmicExclusiveMonitor>(memory, num_cores);
18#else 18#else
19 // TODO(merry): Passthrough exclusive monitor 19 // TODO(merry): Passthrough exclusive monitor
diff --git a/src/core/core.cpp b/src/core/core.cpp
index 40a610435..d8934be52 100644
--- a/src/core/core.cpp
+++ b/src/core/core.cpp
@@ -137,6 +137,7 @@ struct System::Impl {
137 device_memory = std::make_unique<Core::DeviceMemory>(); 137 device_memory = std::make_unique<Core::DeviceMemory>();
138 138
139 is_multicore = Settings::values.use_multi_core.GetValue(); 139 is_multicore = Settings::values.use_multi_core.GetValue();
140 extended_memory_layout = Settings::values.use_extended_memory_layout.GetValue();
140 141
141 core_timing.SetMulticore(is_multicore); 142 core_timing.SetMulticore(is_multicore);
142 core_timing.Initialize([&system]() { system.RegisterHostThread(); }); 143 core_timing.Initialize([&system]() { system.RegisterHostThread(); });
@@ -166,13 +167,18 @@ struct System::Impl {
166 } 167 }
167 168
168 void ReinitializeIfNecessary(System& system) { 169 void ReinitializeIfNecessary(System& system) {
169 if (is_multicore == Settings::values.use_multi_core.GetValue()) { 170 const bool must_reinitialize =
171 is_multicore != Settings::values.use_multi_core.GetValue() ||
172 extended_memory_layout != Settings::values.use_extended_memory_layout.GetValue();
173
174 if (!must_reinitialize) {
170 return; 175 return;
171 } 176 }
172 177
173 LOG_DEBUG(Kernel, "Re-initializing"); 178 LOG_DEBUG(Kernel, "Re-initializing");
174 179
175 is_multicore = Settings::values.use_multi_core.GetValue(); 180 is_multicore = Settings::values.use_multi_core.GetValue();
181 extended_memory_layout = Settings::values.use_extended_memory_layout.GetValue();
176 182
177 Initialize(system); 183 Initialize(system);
178 } 184 }
@@ -521,6 +527,7 @@ struct System::Impl {
521 527
522 bool is_multicore{}; 528 bool is_multicore{};
523 bool is_async_gpu{}; 529 bool is_async_gpu{};
530 bool extended_memory_layout{};
524 531
525 ExecuteProgramCallback execute_program_callback; 532 ExecuteProgramCallback execute_program_callback;
526 ExitCallback exit_callback; 533 ExitCallback exit_callback;
diff --git a/src/core/file_sys/control_metadata.cpp b/src/core/file_sys/control_metadata.cpp
index be25da2f6..50f44f598 100644
--- a/src/core/file_sys/control_metadata.cpp
+++ b/src/core/file_sys/control_metadata.cpp
@@ -1,6 +1,7 @@
1// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project 1// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later 2// SPDX-License-Identifier: GPL-2.0-or-later
3 3
4#include "common/settings.h"
4#include "common/string_util.h" 5#include "common/string_util.h"
5#include "common/swap.h" 6#include "common/swap.h"
6#include "core/file_sys/control_metadata.h" 7#include "core/file_sys/control_metadata.h"
@@ -37,6 +38,27 @@ std::string LanguageEntry::GetDeveloperName() const {
37 developer_name.size()); 38 developer_name.size());
38} 39}
39 40
41constexpr std::array<Language, 18> language_to_codes = {{
42 Language::Japanese,
43 Language::AmericanEnglish,
44 Language::French,
45 Language::German,
46 Language::Italian,
47 Language::Spanish,
48 Language::Chinese,
49 Language::Korean,
50 Language::Dutch,
51 Language::Portuguese,
52 Language::Russian,
53 Language::Taiwanese,
54 Language::BritishEnglish,
55 Language::CanadianFrench,
56 Language::LatinAmericanSpanish,
57 Language::Chinese,
58 Language::Taiwanese,
59 Language::BrazilianPortuguese,
60}};
61
40NACP::NACP() = default; 62NACP::NACP() = default;
41 63
42NACP::NACP(VirtualFile file) { 64NACP::NACP(VirtualFile file) {
@@ -45,9 +67,13 @@ NACP::NACP(VirtualFile file) {
45 67
46NACP::~NACP() = default; 68NACP::~NACP() = default;
47 69
48const LanguageEntry& NACP::GetLanguageEntry(Language language) const { 70const LanguageEntry& NACP::GetLanguageEntry() const {
49 if (language != Language::Default) { 71 Language language = language_to_codes[Settings::values.language_index.GetValue()];
50 return raw.language_entries.at(static_cast<u8>(language)); 72
73 {
74 const auto& language_entry = raw.language_entries.at(static_cast<u8>(language));
75 if (!language_entry.GetApplicationName().empty())
76 return language_entry;
51 } 77 }
52 78
53 for (const auto& language_entry : raw.language_entries) { 79 for (const auto& language_entry : raw.language_entries) {
@@ -55,16 +81,15 @@ const LanguageEntry& NACP::GetLanguageEntry(Language language) const {
55 return language_entry; 81 return language_entry;
56 } 82 }
57 83
58 // Fallback to English 84 return raw.language_entries.at(static_cast<u8>(Language::AmericanEnglish));
59 return GetLanguageEntry(Language::AmericanEnglish);
60} 85}
61 86
62std::string NACP::GetApplicationName(Language language) const { 87std::string NACP::GetApplicationName() const {
63 return GetLanguageEntry(language).GetApplicationName(); 88 return GetLanguageEntry().GetApplicationName();
64} 89}
65 90
66std::string NACP::GetDeveloperName(Language language) const { 91std::string NACP::GetDeveloperName() const {
67 return GetLanguageEntry(language).GetDeveloperName(); 92 return GetLanguageEntry().GetDeveloperName();
68} 93}
69 94
70u64 NACP::GetTitleId() const { 95u64 NACP::GetTitleId() const {
diff --git a/src/core/file_sys/control_metadata.h b/src/core/file_sys/control_metadata.h
index 75295519c..6a81873b1 100644
--- a/src/core/file_sys/control_metadata.h
+++ b/src/core/file_sys/control_metadata.h
@@ -101,9 +101,9 @@ public:
101 explicit NACP(VirtualFile file); 101 explicit NACP(VirtualFile file);
102 ~NACP(); 102 ~NACP();
103 103
104 const LanguageEntry& GetLanguageEntry(Language language = Language::Default) const; 104 const LanguageEntry& GetLanguageEntry() const;
105 std::string GetApplicationName(Language language = Language::Default) const; 105 std::string GetApplicationName() const;
106 std::string GetDeveloperName(Language language = Language::Default) const; 106 std::string GetDeveloperName() const;
107 u64 GetTitleId() const; 107 u64 GetTitleId() const;
108 u64 GetDLCBaseTitleId() const; 108 u64 GetDLCBaseTitleId() const;
109 std::string GetVersionString() const; 109 std::string GetVersionString() const;
diff --git a/src/core/hid/emulated_controller.cpp b/src/core/hid/emulated_controller.cpp
index 57eff72fe..ec1364452 100644
--- a/src/core/hid/emulated_controller.cpp
+++ b/src/core/hid/emulated_controller.cpp
@@ -970,14 +970,7 @@ bool EmulatedController::SetVibration(std::size_t device_index, VibrationValue v
970 Common::Input::VibrationError::None; 970 Common::Input::VibrationError::None;
971} 971}
972 972
973bool EmulatedController::TestVibration(std::size_t device_index) { 973bool EmulatedController::IsVibrationEnabled(std::size_t device_index) {
974 if (device_index >= output_devices.size()) {
975 return false;
976 }
977 if (!output_devices[device_index]) {
978 return false;
979 }
980
981 const auto player_index = NpadIdTypeToIndex(npad_id_type); 974 const auto player_index = NpadIdTypeToIndex(npad_id_type);
982 const auto& player = Settings::values.players.GetValue()[player_index]; 975 const auto& player = Settings::values.players.GetValue()[player_index];
983 976
@@ -985,31 +978,15 @@ bool EmulatedController::TestVibration(std::size_t device_index) {
985 return false; 978 return false;
986 } 979 }
987 980
988 const Common::Input::VibrationStatus test_vibration = { 981 if (device_index >= output_devices.size()) {
989 .low_amplitude = 0.001f, 982 return false;
990 .low_frequency = DEFAULT_VIBRATION_VALUE.low_frequency, 983 }
991 .high_amplitude = 0.001f,
992 .high_frequency = DEFAULT_VIBRATION_VALUE.high_frequency,
993 .type = Common::Input::VibrationAmplificationType::Test,
994 };
995
996 const Common::Input::VibrationStatus zero_vibration = {
997 .low_amplitude = DEFAULT_VIBRATION_VALUE.low_amplitude,
998 .low_frequency = DEFAULT_VIBRATION_VALUE.low_frequency,
999 .high_amplitude = DEFAULT_VIBRATION_VALUE.high_amplitude,
1000 .high_frequency = DEFAULT_VIBRATION_VALUE.high_frequency,
1001 .type = Common::Input::VibrationAmplificationType::Test,
1002 };
1003
1004 // Send a slight vibration to test for rumble support
1005 output_devices[device_index]->SetVibration(test_vibration);
1006 984
1007 // Wait for about 15ms to ensure the controller is ready for the stop command 985 if (!output_devices[device_index]) {
1008 std::this_thread::sleep_for(std::chrono::milliseconds(15)); 986 return false;
987 }
1009 988
1010 // Stop any vibration and return the result 989 return output_devices[device_index]->IsVibrationEnabled();
1011 return output_devices[device_index]->SetVibration(zero_vibration) ==
1012 Common::Input::VibrationError::None;
1013} 990}
1014 991
1015bool EmulatedController::SetPollingMode(Common::Input::PollingMode polling_mode) { 992bool EmulatedController::SetPollingMode(Common::Input::PollingMode polling_mode) {
@@ -1048,6 +1025,7 @@ bool EmulatedController::HasNfc() const {
1048 case NpadStyleIndex::JoyconRight: 1025 case NpadStyleIndex::JoyconRight:
1049 case NpadStyleIndex::JoyconDual: 1026 case NpadStyleIndex::JoyconDual:
1050 case NpadStyleIndex::ProController: 1027 case NpadStyleIndex::ProController:
1028 case NpadStyleIndex::Handheld:
1051 break; 1029 break;
1052 default: 1030 default:
1053 return false; 1031 return false;
@@ -1234,12 +1212,6 @@ bool EmulatedController::IsConnected(bool get_temporary_value) const {
1234 return is_connected; 1212 return is_connected;
1235} 1213}
1236 1214
1237bool EmulatedController::IsVibrationEnabled() const {
1238 const auto player_index = NpadIdTypeToIndex(npad_id_type);
1239 const auto& player = Settings::values.players.GetValue()[player_index];
1240 return player.vibration_enabled;
1241}
1242
1243NpadIdType EmulatedController::GetNpadIdType() const { 1215NpadIdType EmulatedController::GetNpadIdType() const {
1244 std::scoped_lock lock{mutex}; 1216 std::scoped_lock lock{mutex};
1245 return npad_id_type; 1217 return npad_id_type;
diff --git a/src/core/hid/emulated_controller.h b/src/core/hid/emulated_controller.h
index 319226bf8..d004ca56a 100644
--- a/src/core/hid/emulated_controller.h
+++ b/src/core/hid/emulated_controller.h
@@ -206,9 +206,6 @@ public:
206 */ 206 */
207 bool IsConnected(bool get_temporary_value = false) const; 207 bool IsConnected(bool get_temporary_value = false) const;
208 208
209 /// Returns true if vibration is enabled
210 bool IsVibrationEnabled() const;
211
212 /// Removes all callbacks created from input devices 209 /// Removes all callbacks created from input devices
213 void UnloadInput(); 210 void UnloadInput();
214 211
@@ -339,7 +336,7 @@ public:
339 * Sends a small vibration to the output device 336 * Sends a small vibration to the output device
340 * @return true if SetVibration was successfull 337 * @return true if SetVibration was successfull
341 */ 338 */
342 bool TestVibration(std::size_t device_index); 339 bool IsVibrationEnabled(std::size_t device_index);
343 340
344 /** 341 /**
345 * Sets the desired data to be polled from a controller 342 * Sets the desired data to be polled from a controller
diff --git a/src/core/hle/ipc_helpers.h b/src/core/hle/ipc_helpers.h
index 18fde8bd6..a86bec252 100644
--- a/src/core/hle/ipc_helpers.h
+++ b/src/core/hle/ipc_helpers.h
@@ -86,13 +86,13 @@ public:
86 u32 num_domain_objects{}; 86 u32 num_domain_objects{};
87 const bool always_move_handles{ 87 const bool always_move_handles{
88 (static_cast<u32>(flags) & static_cast<u32>(Flags::AlwaysMoveHandles)) != 0}; 88 (static_cast<u32>(flags) & static_cast<u32>(Flags::AlwaysMoveHandles)) != 0};
89 if (!ctx.Session()->GetSessionRequestManager()->IsDomain() || always_move_handles) { 89 if (!ctx.GetManager()->IsDomain() || always_move_handles) {
90 num_handles_to_move = num_objects_to_move; 90 num_handles_to_move = num_objects_to_move;
91 } else { 91 } else {
92 num_domain_objects = num_objects_to_move; 92 num_domain_objects = num_objects_to_move;
93 } 93 }
94 94
95 if (ctx.Session()->GetSessionRequestManager()->IsDomain()) { 95 if (ctx.GetManager()->IsDomain()) {
96 raw_data_size += 96 raw_data_size +=
97 static_cast<u32>(sizeof(DomainMessageHeader) / sizeof(u32) + num_domain_objects); 97 static_cast<u32>(sizeof(DomainMessageHeader) / sizeof(u32) + num_domain_objects);
98 ctx.write_size += num_domain_objects; 98 ctx.write_size += num_domain_objects;
@@ -125,8 +125,7 @@ public:
125 if (!ctx.IsTipc()) { 125 if (!ctx.IsTipc()) {
126 AlignWithPadding(); 126 AlignWithPadding();
127 127
128 if (ctx.Session()->GetSessionRequestManager()->IsDomain() && 128 if (ctx.GetManager()->IsDomain() && ctx.HasDomainMessageHeader()) {
129 ctx.HasDomainMessageHeader()) {
130 IPC::DomainMessageHeader domain_header{}; 129 IPC::DomainMessageHeader domain_header{};
131 domain_header.num_objects = num_domain_objects; 130 domain_header.num_objects = num_domain_objects;
132 PushRaw(domain_header); 131 PushRaw(domain_header);
@@ -146,18 +145,18 @@ public:
146 145
147 template <class T> 146 template <class T>
148 void PushIpcInterface(std::shared_ptr<T> iface) { 147 void PushIpcInterface(std::shared_ptr<T> iface) {
149 if (context->Session()->GetSessionRequestManager()->IsDomain()) { 148 if (context->GetManager()->IsDomain()) {
150 context->AddDomainObject(std::move(iface)); 149 context->AddDomainObject(std::move(iface));
151 } else { 150 } else {
152 kernel.CurrentProcess()->GetResourceLimit()->Reserve( 151 kernel.CurrentProcess()->GetResourceLimit()->Reserve(
153 Kernel::LimitableResource::Sessions, 1); 152 Kernel::LimitableResource::SessionCountMax, 1);
154 153
155 auto* session = Kernel::KSession::Create(kernel); 154 auto* session = Kernel::KSession::Create(kernel);
156 session->Initialize(nullptr, iface->GetServiceName(), 155 session->Initialize(nullptr, iface->GetServiceName());
157 std::make_shared<Kernel::SessionRequestManager>(kernel)); 156 iface->RegisterSession(&session->GetServerSession(),
157 std::make_shared<Kernel::SessionRequestManager>(kernel));
158 158
159 context->AddMoveObject(&session->GetClientSession()); 159 context->AddMoveObject(&session->GetClientSession());
160 iface->ClientConnected(&session->GetServerSession());
161 } 160 }
162 } 161 }
163 162
@@ -387,7 +386,7 @@ public:
387 386
388 template <class T> 387 template <class T>
389 std::weak_ptr<T> PopIpcInterface() { 388 std::weak_ptr<T> PopIpcInterface() {
390 ASSERT(context->Session()->GetSessionRequestManager()->IsDomain()); 389 ASSERT(context->GetManager()->IsDomain());
391 ASSERT(context->GetDomainMessageHeader().input_object_count > 0); 390 ASSERT(context->GetDomainMessageHeader().input_object_count > 0);
392 return context->GetDomainHandler<T>(Pop<u32>() - 1); 391 return context->GetDomainHandler<T>(Pop<u32>() - 1);
393 } 392 }
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 fe375769e..4b717d091 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
@@ -9,6 +9,10 @@ namespace Kernel::Board::Nintendo::Nx {
9 9
10class KSystemControl { 10class KSystemControl {
11public: 11public:
12 // This can be overridden as needed.
13 static constexpr size_t SecureAppletMemorySize = 4 * 1024 * 1024; // 4_MB
14
15public:
12 class Init { 16 class Init {
13 public: 17 public:
14 // Initialization. 18 // Initialization.
diff --git a/src/core/hle/kernel/global_scheduler_context.cpp b/src/core/hle/kernel/global_scheduler_context.cpp
index 65576b8c4..fd911a3a5 100644
--- a/src/core/hle/kernel/global_scheduler_context.cpp
+++ b/src/core/hle/kernel/global_scheduler_context.cpp
@@ -49,4 +49,26 @@ bool GlobalSchedulerContext::IsLocked() const {
49 return scheduler_lock.IsLockedByCurrentThread(); 49 return scheduler_lock.IsLockedByCurrentThread();
50} 50}
51 51
52void GlobalSchedulerContext::RegisterDummyThreadForWakeup(KThread* thread) {
53 ASSERT(IsLocked());
54
55 woken_dummy_threads.insert(thread);
56}
57
58void GlobalSchedulerContext::UnregisterDummyThreadForWakeup(KThread* thread) {
59 ASSERT(IsLocked());
60
61 woken_dummy_threads.erase(thread);
62}
63
64void GlobalSchedulerContext::WakeupWaitingDummyThreads() {
65 ASSERT(IsLocked());
66
67 for (auto* thread : woken_dummy_threads) {
68 thread->DummyThreadEndWait();
69 }
70
71 woken_dummy_threads.clear();
72}
73
52} // namespace Kernel 74} // namespace Kernel
diff --git a/src/core/hle/kernel/global_scheduler_context.h b/src/core/hle/kernel/global_scheduler_context.h
index 67bb9852d..220ed6192 100644
--- a/src/core/hle/kernel/global_scheduler_context.h
+++ b/src/core/hle/kernel/global_scheduler_context.h
@@ -4,6 +4,7 @@
4#pragma once 4#pragma once
5 5
6#include <atomic> 6#include <atomic>
7#include <set>
7#include <vector> 8#include <vector>
8 9
9#include "common/common_types.h" 10#include "common/common_types.h"
@@ -58,6 +59,10 @@ public:
58 /// Returns true if the global scheduler lock is acquired 59 /// Returns true if the global scheduler lock is acquired
59 bool IsLocked() const; 60 bool IsLocked() const;
60 61
62 void UnregisterDummyThreadForWakeup(KThread* thread);
63 void RegisterDummyThreadForWakeup(KThread* thread);
64 void WakeupWaitingDummyThreads();
65
61 [[nodiscard]] LockType& SchedulerLock() { 66 [[nodiscard]] LockType& SchedulerLock() {
62 return scheduler_lock; 67 return scheduler_lock;
63 } 68 }
@@ -76,6 +81,9 @@ private:
76 KSchedulerPriorityQueue priority_queue; 81 KSchedulerPriorityQueue priority_queue;
77 LockType scheduler_lock; 82 LockType scheduler_lock;
78 83
84 /// Lists dummy threads pending wakeup on lock release
85 std::set<KThread*> woken_dummy_threads;
86
79 /// Lists all thread ids that aren't deleted/etc. 87 /// Lists all thread ids that aren't deleted/etc.
80 std::vector<KThread*> thread_list; 88 std::vector<KThread*> thread_list;
81 std::mutex global_list_guard; 89 std::mutex global_list_guard;
diff --git a/src/core/hle/kernel/hle_ipc.cpp b/src/core/hle/kernel/hle_ipc.cpp
index e4f43a053..06010b8d1 100644
--- a/src/core/hle/kernel/hle_ipc.cpp
+++ b/src/core/hle/kernel/hle_ipc.cpp
@@ -16,6 +16,7 @@
16#include "core/hle/kernel/k_auto_object.h" 16#include "core/hle/kernel/k_auto_object.h"
17#include "core/hle/kernel/k_handle_table.h" 17#include "core/hle/kernel/k_handle_table.h"
18#include "core/hle/kernel/k_process.h" 18#include "core/hle/kernel/k_process.h"
19#include "core/hle/kernel/k_server_port.h"
19#include "core/hle/kernel/k_server_session.h" 20#include "core/hle/kernel/k_server_session.h"
20#include "core/hle/kernel/k_thread.h" 21#include "core/hle/kernel/k_thread.h"
21#include "core/hle/kernel/kernel.h" 22#include "core/hle/kernel/kernel.h"
@@ -26,18 +27,28 @@ namespace Kernel {
26 27
27SessionRequestHandler::SessionRequestHandler(KernelCore& kernel_, const char* service_name_, 28SessionRequestHandler::SessionRequestHandler(KernelCore& kernel_, const char* service_name_,
28 ServiceThreadType thread_type) 29 ServiceThreadType thread_type)
29 : kernel{kernel_} { 30 : kernel{kernel_}, service_thread{thread_type == ServiceThreadType::CreateNew
30 if (thread_type == ServiceThreadType::CreateNew) { 31 ? kernel.CreateServiceThread(service_name_)
31 service_thread = kernel.CreateServiceThread(service_name_); 32 : kernel.GetDefaultServiceThread()} {}
32 } else {
33 service_thread = kernel.GetDefaultServiceThread();
34 }
35}
36 33
37SessionRequestHandler::~SessionRequestHandler() { 34SessionRequestHandler::~SessionRequestHandler() {
38 kernel.ReleaseServiceThread(service_thread); 35 kernel.ReleaseServiceThread(service_thread);
39} 36}
40 37
38void SessionRequestHandler::AcceptSession(KServerPort* server_port) {
39 auto* server_session = server_port->AcceptSession();
40 ASSERT(server_session != nullptr);
41
42 RegisterSession(server_session, std::make_shared<SessionRequestManager>(kernel));
43}
44
45void SessionRequestHandler::RegisterSession(KServerSession* server_session,
46 std::shared_ptr<SessionRequestManager> manager) {
47 manager->SetSessionHandler(shared_from_this());
48 service_thread.RegisterServerSession(server_session, manager);
49 server_session->Close();
50}
51
41SessionRequestManager::SessionRequestManager(KernelCore& kernel_) : kernel{kernel_} {} 52SessionRequestManager::SessionRequestManager(KernelCore& kernel_) : kernel{kernel_} {}
42 53
43SessionRequestManager::~SessionRequestManager() = default; 54SessionRequestManager::~SessionRequestManager() = default;
@@ -92,7 +103,7 @@ Result SessionRequestManager::HandleDomainSyncRequest(KServerSession* server_ses
92 } 103 }
93 104
94 // Set domain handlers in HLE context, used for domain objects (IPC interfaces) as inputs 105 // Set domain handlers in HLE context, used for domain objects (IPC interfaces) as inputs
95 context.SetSessionRequestManager(server_session->GetSessionRequestManager()); 106 ASSERT(context.GetManager().get() == this);
96 107
97 // If there is a DomainMessageHeader, then this is CommandType "Request" 108 // If there is a DomainMessageHeader, then this is CommandType "Request"
98 const auto& domain_message_header = context.GetDomainMessageHeader(); 109 const auto& domain_message_header = context.GetDomainMessageHeader();
@@ -130,31 +141,6 @@ Result SessionRequestManager::HandleDomainSyncRequest(KServerSession* server_ses
130 return ResultSuccess; 141 return ResultSuccess;
131} 142}
132 143
133Result SessionRequestManager::QueueSyncRequest(KSession* parent,
134 std::shared_ptr<HLERequestContext>&& context) {
135 // Ensure we have a session request handler
136 if (this->HasSessionRequestHandler(*context)) {
137 if (auto strong_ptr = this->GetServiceThread().lock()) {
138 strong_ptr->QueueSyncRequest(*parent, std::move(context));
139 } else {
140 ASSERT_MSG(false, "strong_ptr is nullptr!");
141 }
142 } else {
143 ASSERT_MSG(false, "handler is invalid!");
144 }
145
146 return ResultSuccess;
147}
148
149void SessionRequestHandler::ClientConnected(KServerSession* session) {
150 session->GetSessionRequestManager()->SetSessionHandler(shared_from_this());
151
152 // Ensure our server session is tracked globally.
153 kernel.RegisterServerObject(session);
154}
155
156void SessionRequestHandler::ClientDisconnected(KServerSession* session) {}
157
158HLERequestContext::HLERequestContext(KernelCore& kernel_, Core::Memory::Memory& memory_, 144HLERequestContext::HLERequestContext(KernelCore& kernel_, Core::Memory::Memory& memory_,
159 KServerSession* server_session_, KThread* thread_) 145 KServerSession* server_session_, KThread* thread_)
160 : server_session(server_session_), thread(thread_), kernel{kernel_}, memory{memory_} { 146 : server_session(server_session_), thread(thread_), kernel{kernel_}, memory{memory_} {
@@ -214,7 +200,7 @@ void HLERequestContext::ParseCommandBuffer(const KHandleTable& handle_table, u32
214 // Padding to align to 16 bytes 200 // Padding to align to 16 bytes
215 rp.AlignWithPadding(); 201 rp.AlignWithPadding();
216 202
217 if (Session()->GetSessionRequestManager()->IsDomain() && 203 if (GetManager()->IsDomain() &&
218 ((command_header->type == IPC::CommandType::Request || 204 ((command_header->type == IPC::CommandType::Request ||
219 command_header->type == IPC::CommandType::RequestWithContext) || 205 command_header->type == IPC::CommandType::RequestWithContext) ||
220 !incoming)) { 206 !incoming)) {
@@ -223,7 +209,7 @@ void HLERequestContext::ParseCommandBuffer(const KHandleTable& handle_table, u32
223 if (incoming || domain_message_header) { 209 if (incoming || domain_message_header) {
224 domain_message_header = rp.PopRaw<IPC::DomainMessageHeader>(); 210 domain_message_header = rp.PopRaw<IPC::DomainMessageHeader>();
225 } else { 211 } else {
226 if (Session()->GetSessionRequestManager()->IsDomain()) { 212 if (GetManager()->IsDomain()) {
227 LOG_WARNING(IPC, "Domain request has no DomainMessageHeader!"); 213 LOG_WARNING(IPC, "Domain request has no DomainMessageHeader!");
228 } 214 }
229 } 215 }
@@ -316,12 +302,11 @@ Result HLERequestContext::WriteToOutgoingCommandBuffer(KThread& requesting_threa
316 // Write the domain objects to the command buffer, these go after the raw untranslated data. 302 // Write the domain objects to the command buffer, these go after the raw untranslated data.
317 // TODO(Subv): This completely ignores C buffers. 303 // TODO(Subv): This completely ignores C buffers.
318 304
319 if (server_session->GetSessionRequestManager()->IsDomain()) { 305 if (GetManager()->IsDomain()) {
320 current_offset = domain_offset - static_cast<u32>(outgoing_domain_objects.size()); 306 current_offset = domain_offset - static_cast<u32>(outgoing_domain_objects.size());
321 for (auto& object : outgoing_domain_objects) { 307 for (auto& object : outgoing_domain_objects) {
322 server_session->GetSessionRequestManager()->AppendDomainHandler(std::move(object)); 308 GetManager()->AppendDomainHandler(std::move(object));
323 cmd_buf[current_offset++] = static_cast<u32_le>( 309 cmd_buf[current_offset++] = static_cast<u32_le>(GetManager()->DomainHandlerCount());
324 server_session->GetSessionRequestManager()->DomainHandlerCount());
325 } 310 }
326 } 311 }
327 312
diff --git a/src/core/hle/kernel/hle_ipc.h b/src/core/hle/kernel/hle_ipc.h
index a0522bca0..d87be72d6 100644
--- a/src/core/hle/kernel/hle_ipc.h
+++ b/src/core/hle/kernel/hle_ipc.h
@@ -45,11 +45,13 @@ class KAutoObject;
45class KernelCore; 45class KernelCore;
46class KEvent; 46class KEvent;
47class KHandleTable; 47class KHandleTable;
48class KServerPort;
48class KProcess; 49class KProcess;
49class KServerSession; 50class KServerSession;
50class KThread; 51class KThread;
51class KReadableEvent; 52class KReadableEvent;
52class KSession; 53class KSession;
54class SessionRequestManager;
53class ServiceThread; 55class ServiceThread;
54 56
55enum class ThreadWakeupReason; 57enum class ThreadWakeupReason;
@@ -76,27 +78,17 @@ public:
76 virtual Result HandleSyncRequest(Kernel::KServerSession& session, 78 virtual Result HandleSyncRequest(Kernel::KServerSession& session,
77 Kernel::HLERequestContext& context) = 0; 79 Kernel::HLERequestContext& context) = 0;
78 80
79 /** 81 void AcceptSession(KServerPort* server_port);
80 * Signals that a client has just connected to this HLE handler and keeps the 82 void RegisterSession(KServerSession* server_session,
81 * associated ServerSession alive for the duration of the connection. 83 std::shared_ptr<SessionRequestManager> manager);
82 * @param server_session Owning pointer to the ServerSession associated with the connection.
83 */
84 void ClientConnected(KServerSession* session);
85 84
86 /** 85 ServiceThread& GetServiceThread() const {
87 * Signals that a client has just disconnected from this HLE handler and releases the
88 * associated ServerSession.
89 * @param server_session ServerSession associated with the connection.
90 */
91 void ClientDisconnected(KServerSession* session);
92
93 std::weak_ptr<ServiceThread> GetServiceThread() const {
94 return service_thread; 86 return service_thread;
95 } 87 }
96 88
97protected: 89protected:
98 KernelCore& kernel; 90 KernelCore& kernel;
99 std::weak_ptr<ServiceThread> service_thread; 91 ServiceThread& service_thread;
100}; 92};
101 93
102using SessionRequestHandlerWeakPtr = std::weak_ptr<SessionRequestHandler>; 94using SessionRequestHandlerWeakPtr = std::weak_ptr<SessionRequestHandler>;
@@ -162,7 +154,7 @@ public:
162 session_handler = std::move(handler); 154 session_handler = std::move(handler);
163 } 155 }
164 156
165 std::weak_ptr<ServiceThread> GetServiceThread() const { 157 ServiceThread& GetServiceThread() const {
166 return session_handler->GetServiceThread(); 158 return session_handler->GetServiceThread();
167 } 159 }
168 160
@@ -170,7 +162,6 @@ public:
170 162
171 Result HandleDomainSyncRequest(KServerSession* server_session, HLERequestContext& context); 163 Result HandleDomainSyncRequest(KServerSession* server_session, HLERequestContext& context);
172 Result CompleteSyncRequest(KServerSession* server_session, HLERequestContext& context); 164 Result CompleteSyncRequest(KServerSession* server_session, HLERequestContext& context);
173 Result QueueSyncRequest(KSession* parent, std::shared_ptr<HLERequestContext>&& context);
174 165
175private: 166private:
176 bool convert_to_domain{}; 167 bool convert_to_domain{};
@@ -304,7 +295,7 @@ public:
304 */ 295 */
305 template <typename T, typename = std::enable_if_t<!std::is_pointer_v<T>>> 296 template <typename T, typename = std::enable_if_t<!std::is_pointer_v<T>>>
306 std::size_t WriteBuffer(const T& data, std::size_t buffer_index = 0) const { 297 std::size_t WriteBuffer(const T& data, std::size_t buffer_index = 0) const {
307 if constexpr (Common::IsSTLContainer<T>) { 298 if constexpr (Common::IsContiguousContainer<T>) {
308 using ContiguousType = typename T::value_type; 299 using ContiguousType = typename T::value_type;
309 static_assert(std::is_trivially_copyable_v<ContiguousType>, 300 static_assert(std::is_trivially_copyable_v<ContiguousType>,
310 "Container to WriteBuffer must contain trivially copyable objects"); 301 "Container to WriteBuffer must contain trivially copyable objects");
@@ -350,11 +341,11 @@ public:
350 341
351 template <typename T> 342 template <typename T>
352 std::shared_ptr<T> GetDomainHandler(std::size_t index) const { 343 std::shared_ptr<T> GetDomainHandler(std::size_t index) const {
353 return std::static_pointer_cast<T>(manager.lock()->DomainHandler(index).lock()); 344 return std::static_pointer_cast<T>(GetManager()->DomainHandler(index).lock());
354 } 345 }
355 346
356 void SetSessionRequestManager(std::weak_ptr<SessionRequestManager> manager_) { 347 void SetSessionRequestManager(std::weak_ptr<SessionRequestManager> manager_) {
357 manager = std::move(manager_); 348 manager = manager_;
358 } 349 }
359 350
360 std::string Description() const; 351 std::string Description() const;
@@ -363,6 +354,10 @@ public:
363 return *thread; 354 return *thread;
364 } 355 }
365 356
357 std::shared_ptr<SessionRequestManager> GetManager() const {
358 return manager.lock();
359 }
360
366private: 361private:
367 friend class IPC::ResponseBuilder; 362 friend class IPC::ResponseBuilder;
368 363
@@ -396,7 +391,7 @@ private:
396 u32 handles_offset{}; 391 u32 handles_offset{};
397 u32 domain_offset{}; 392 u32 domain_offset{};
398 393
399 std::weak_ptr<SessionRequestManager> manager; 394 std::weak_ptr<SessionRequestManager> manager{};
400 395
401 KernelCore& kernel; 396 KernelCore& kernel;
402 Core::Memory::Memory& memory; 397 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 477e4e407..bda098511 100644
--- a/src/core/hle/kernel/init/init_slab_setup.cpp
+++ b/src/core/hle/kernel/init/init_slab_setup.cpp
@@ -10,7 +10,9 @@
10#include "core/hardware_properties.h" 10#include "core/hardware_properties.h"
11#include "core/hle/kernel/init/init_slab_setup.h" 11#include "core/hle/kernel/init/init_slab_setup.h"
12#include "core/hle/kernel/k_code_memory.h" 12#include "core/hle/kernel/k_code_memory.h"
13#include "core/hle/kernel/k_debug.h"
13#include "core/hle/kernel/k_event.h" 14#include "core/hle/kernel/k_event.h"
15#include "core/hle/kernel/k_event_info.h"
14#include "core/hle/kernel/k_memory_layout.h" 16#include "core/hle/kernel/k_memory_layout.h"
15#include "core/hle/kernel/k_memory_manager.h" 17#include "core/hle/kernel/k_memory_manager.h"
16#include "core/hle/kernel/k_page_buffer.h" 18#include "core/hle/kernel/k_page_buffer.h"
@@ -22,6 +24,7 @@
22#include "core/hle/kernel/k_shared_memory.h" 24#include "core/hle/kernel/k_shared_memory.h"
23#include "core/hle/kernel/k_shared_memory_info.h" 25#include "core/hle/kernel/k_shared_memory_info.h"
24#include "core/hle/kernel/k_system_control.h" 26#include "core/hle/kernel/k_system_control.h"
27#include "core/hle/kernel/k_system_resource.h"
25#include "core/hle/kernel/k_thread.h" 28#include "core/hle/kernel/k_thread.h"
26#include "core/hle/kernel/k_thread_local_page.h" 29#include "core/hle/kernel/k_thread_local_page.h"
27#include "core/hle/kernel/k_transfer_memory.h" 30#include "core/hle/kernel/k_transfer_memory.h"
@@ -44,7 +47,10 @@ namespace Kernel::Init {
44 HANDLER(KThreadLocalPage, \ 47 HANDLER(KThreadLocalPage, \
45 (SLAB_COUNT(KProcess) + (SLAB_COUNT(KProcess) + SLAB_COUNT(KThread)) / 8), \ 48 (SLAB_COUNT(KProcess) + (SLAB_COUNT(KProcess) + SLAB_COUNT(KThread)) / 8), \
46 ##__VA_ARGS__) \ 49 ##__VA_ARGS__) \
47 HANDLER(KResourceLimit, (SLAB_COUNT(KResourceLimit)), ##__VA_ARGS__) 50 HANDLER(KResourceLimit, (SLAB_COUNT(KResourceLimit)), ##__VA_ARGS__) \
51 HANDLER(KEventInfo, (SLAB_COUNT(KThread) + SLAB_COUNT(KDebug)), ##__VA_ARGS__) \
52 HANDLER(KDebug, (SLAB_COUNT(KDebug)), ##__VA_ARGS__) \
53 HANDLER(KSecureSystemResource, (SLAB_COUNT(KProcess)), ##__VA_ARGS__)
48 54
49namespace { 55namespace {
50 56
@@ -73,8 +79,20 @@ constexpr size_t SlabCountKResourceLimit = 5;
73constexpr size_t SlabCountKDebug = Core::Hardware::NUM_CPU_CORES; 79constexpr size_t SlabCountKDebug = Core::Hardware::NUM_CPU_CORES;
74constexpr size_t SlabCountKIoPool = 1; 80constexpr size_t SlabCountKIoPool = 1;
75constexpr size_t SlabCountKIoRegion = 6; 81constexpr size_t SlabCountKIoRegion = 6;
82constexpr size_t SlabcountKSessionRequestMappings = 40;
76 83
77constexpr size_t SlabCountExtraKThread = 160; 84constexpr size_t SlabCountExtraKThread = (1024 + 256 + 256) - SlabCountKThread;
85
86namespace test {
87
88static_assert(KernelPageBufferHeapSize ==
89 2 * PageSize + (SlabCountKProcess + SlabCountKThread +
90 (SlabCountKProcess + SlabCountKThread) / 8) *
91 PageSize);
92static_assert(KernelPageBufferAdditionalSize ==
93 (SlabCountExtraKThread + (SlabCountExtraKThread / 8)) * PageSize);
94
95} // namespace test
78 96
79/// Helper function to translate from the slab virtual address to the reserved location in physical 97/// Helper function to translate from the slab virtual address to the reserved location in physical
80/// memory. 98/// memory.
@@ -109,7 +127,7 @@ VAddr InitializeSlabHeap(Core::System& system, KMemoryLayout& memory_layout, VAd
109} 127}
110 128
111size_t CalculateSlabHeapGapSize() { 129size_t CalculateSlabHeapGapSize() {
112 constexpr size_t KernelSlabHeapGapSize = 2_MiB - 296_KiB; 130 constexpr size_t KernelSlabHeapGapSize = 2_MiB - 320_KiB;
113 static_assert(KernelSlabHeapGapSize <= KernelSlabHeapGapsSizeMax); 131 static_assert(KernelSlabHeapGapSize <= KernelSlabHeapGapsSizeMax);
114 return KernelSlabHeapGapSize; 132 return KernelSlabHeapGapSize;
115} 133}
@@ -134,6 +152,7 @@ KSlabResourceCounts KSlabResourceCounts::CreateDefault() {
134 .num_KDebug = SlabCountKDebug, 152 .num_KDebug = SlabCountKDebug,
135 .num_KIoPool = SlabCountKIoPool, 153 .num_KIoPool = SlabCountKIoPool,
136 .num_KIoRegion = SlabCountKIoRegion, 154 .num_KIoRegion = SlabCountKIoRegion,
155 .num_KSessionRequestMappings = SlabcountKSessionRequestMappings,
137 }; 156 };
138} 157}
139 158
@@ -164,29 +183,6 @@ size_t CalculateTotalSlabHeapSize(const KernelCore& kernel) {
164 return size; 183 return size;
165} 184}
166 185
167void InitializeKPageBufferSlabHeap(Core::System& system) {
168 auto& kernel = system.Kernel();
169
170 const auto& counts = kernel.SlabResourceCounts();
171 const size_t num_pages =
172 counts.num_KProcess + counts.num_KThread + (counts.num_KProcess + counts.num_KThread) / 8;
173 const size_t slab_size = num_pages * PageSize;
174
175 // Reserve memory from the system resource limit.
176 ASSERT(kernel.GetSystemResourceLimit()->Reserve(LimitableResource::PhysicalMemory, slab_size));
177
178 // Allocate memory for the slab.
179 constexpr auto AllocateOption = KMemoryManager::EncodeOption(
180 KMemoryManager::Pool::System, KMemoryManager::Direction::FromFront);
181 const PAddr slab_address =
182 kernel.MemoryManager().AllocateAndOpenContinuous(num_pages, 1, AllocateOption);
183 ASSERT(slab_address != 0);
184
185 // Initialize the slabheap.
186 KPageBuffer::InitializeSlabHeap(kernel, system.DeviceMemory().GetPointer<void>(slab_address),
187 slab_size);
188}
189
190void InitializeSlabHeaps(Core::System& system, KMemoryLayout& memory_layout) { 186void InitializeSlabHeaps(Core::System& system, KMemoryLayout& memory_layout) {
191 auto& kernel = system.Kernel(); 187 auto& kernel = system.Kernel();
192 188
@@ -258,3 +254,30 @@ void InitializeSlabHeaps(Core::System& system, KMemoryLayout& memory_layout) {
258} 254}
259 255
260} // namespace Kernel::Init 256} // namespace Kernel::Init
257
258namespace Kernel {
259
260void KPageBufferSlabHeap::Initialize(Core::System& system) {
261 auto& kernel = system.Kernel();
262 const auto& counts = kernel.SlabResourceCounts();
263 const size_t num_pages =
264 counts.num_KProcess + counts.num_KThread + (counts.num_KProcess + counts.num_KThread) / 8;
265 const size_t slab_size = num_pages * PageSize;
266
267 // Reserve memory from the system resource limit.
268 ASSERT(
269 kernel.GetSystemResourceLimit()->Reserve(LimitableResource::PhysicalMemoryMax, slab_size));
270
271 // Allocate memory for the slab.
272 constexpr auto AllocateOption = KMemoryManager::EncodeOption(
273 KMemoryManager::Pool::System, KMemoryManager::Direction::FromFront);
274 const PAddr slab_address =
275 kernel.MemoryManager().AllocateAndOpenContinuous(num_pages, 1, AllocateOption);
276 ASSERT(slab_address != 0);
277
278 // Initialize the slabheap.
279 KPageBuffer::InitializeSlabHeap(kernel, system.DeviceMemory().GetPointer<void>(slab_address),
280 slab_size);
281}
282
283} // namespace Kernel
diff --git a/src/core/hle/kernel/init/init_slab_setup.h b/src/core/hle/kernel/init/init_slab_setup.h
index 13be63c87..5e22821bc 100644
--- a/src/core/hle/kernel/init/init_slab_setup.h
+++ b/src/core/hle/kernel/init/init_slab_setup.h
@@ -33,11 +33,11 @@ struct KSlabResourceCounts {
33 size_t num_KDebug; 33 size_t num_KDebug;
34 size_t num_KIoPool; 34 size_t num_KIoPool;
35 size_t num_KIoRegion; 35 size_t num_KIoRegion;
36 size_t num_KSessionRequestMappings;
36}; 37};
37 38
38void InitializeSlabResourceCounts(KernelCore& kernel); 39void InitializeSlabResourceCounts(KernelCore& kernel);
39size_t CalculateTotalSlabHeapSize(const KernelCore& kernel); 40size_t CalculateTotalSlabHeapSize(const KernelCore& kernel);
40void InitializeKPageBufferSlabHeap(Core::System& system);
41void InitializeSlabHeaps(Core::System& system, KMemoryLayout& memory_layout); 41void InitializeSlabHeaps(Core::System& system, KMemoryLayout& memory_layout);
42 42
43} // namespace Kernel::Init 43} // namespace Kernel::Init
diff --git a/src/core/hle/kernel/k_class_token.cpp b/src/core/hle/kernel/k_class_token.cpp
index 10265c23c..a850db3c4 100644
--- a/src/core/hle/kernel/k_class_token.cpp
+++ b/src/core/hle/kernel/k_class_token.cpp
@@ -16,6 +16,7 @@
16#include "core/hle/kernel/k_session.h" 16#include "core/hle/kernel/k_session.h"
17#include "core/hle/kernel/k_shared_memory.h" 17#include "core/hle/kernel/k_shared_memory.h"
18#include "core/hle/kernel/k_synchronization_object.h" 18#include "core/hle/kernel/k_synchronization_object.h"
19#include "core/hle/kernel/k_system_resource.h"
19#include "core/hle/kernel/k_thread.h" 20#include "core/hle/kernel/k_thread.h"
20#include "core/hle/kernel/k_transfer_memory.h" 21#include "core/hle/kernel/k_transfer_memory.h"
21 22
@@ -119,4 +120,6 @@ static_assert(std::is_final_v<KTransferMemory> && std::is_base_of_v<KAutoObject,
119// static_assert(std::is_final_v<KCodeMemory> && 120// static_assert(std::is_final_v<KCodeMemory> &&
120// std::is_base_of_v<KAutoObject, KCodeMemory>); 121// std::is_base_of_v<KAutoObject, KCodeMemory>);
121 122
123static_assert(std::is_base_of_v<KAutoObject, KSystemResource>);
124
122} // namespace Kernel 125} // namespace Kernel
diff --git a/src/core/hle/kernel/k_class_token.h b/src/core/hle/kernel/k_class_token.h
index ab20e00ff..e75b1c035 100644
--- a/src/core/hle/kernel/k_class_token.h
+++ b/src/core/hle/kernel/k_class_token.h
@@ -10,6 +10,8 @@ namespace Kernel {
10 10
11class KAutoObject; 11class KAutoObject;
12 12
13class KSystemResource;
14
13class KClassTokenGenerator { 15class KClassTokenGenerator {
14public: 16public:
15 using TokenBaseType = u16; 17 using TokenBaseType = u16;
@@ -58,7 +60,7 @@ private:
58 if constexpr (std::is_same<T, KAutoObject>::value) { 60 if constexpr (std::is_same<T, KAutoObject>::value) {
59 static_assert(T::ObjectType == ObjectType::KAutoObject); 61 static_assert(T::ObjectType == ObjectType::KAutoObject);
60 return 0; 62 return 0;
61 } else if constexpr (!std::is_final<T>::value) { 63 } else if constexpr (!std::is_final<T>::value && !std::same_as<T, KSystemResource>) {
62 static_assert(ObjectType::BaseClassesStart <= T::ObjectType && 64 static_assert(ObjectType::BaseClassesStart <= T::ObjectType &&
63 T::ObjectType < ObjectType::BaseClassesEnd); 65 T::ObjectType < ObjectType::BaseClassesEnd);
64 constexpr auto ClassIndex = static_cast<TokenBaseType>(T::ObjectType) - 66 constexpr auto ClassIndex = static_cast<TokenBaseType>(T::ObjectType) -
@@ -108,6 +110,8 @@ public:
108 KSessionRequest, 110 KSessionRequest,
109 KCodeMemory, 111 KCodeMemory,
110 112
113 KSystemResource,
114
111 // NOTE: True order for these has not been determined yet. 115 // NOTE: True order for these has not been determined yet.
112 KAlpha, 116 KAlpha,
113 KBeta, 117 KBeta,
diff --git a/src/core/hle/kernel/k_client_port.cpp b/src/core/hle/kernel/k_client_port.cpp
index 3cb22ff4d..2ec623a58 100644
--- a/src/core/hle/kernel/k_client_port.cpp
+++ b/src/core/hle/kernel/k_client_port.cpp
@@ -58,11 +58,10 @@ bool KClientPort::IsSignaled() const {
58 return num_sessions < max_sessions; 58 return num_sessions < max_sessions;
59} 59}
60 60
61Result KClientPort::CreateSession(KClientSession** out, 61Result KClientPort::CreateSession(KClientSession** out) {
62 std::shared_ptr<SessionRequestManager> session_manager) {
63 // Reserve a new session from the resource limit. 62 // Reserve a new session from the resource limit.
64 KScopedResourceReservation session_reservation(kernel.CurrentProcess()->GetResourceLimit(), 63 KScopedResourceReservation session_reservation(kernel.CurrentProcess()->GetResourceLimit(),
65 LimitableResource::Sessions); 64 LimitableResource::SessionCountMax);
66 R_UNLESS(session_reservation.Succeeded(), ResultLimitReached); 65 R_UNLESS(session_reservation.Succeeded(), ResultLimitReached);
67 66
68 // Update the session counts. 67 // Update the session counts.
@@ -104,7 +103,7 @@ Result KClientPort::CreateSession(KClientSession** out,
104 } 103 }
105 104
106 // Initialize the session. 105 // Initialize the session.
107 session->Initialize(this, parent->GetName(), session_manager); 106 session->Initialize(this, parent->GetName());
108 107
109 // Commit the session reservation. 108 // Commit the session reservation.
110 session_reservation.Commit(); 109 session_reservation.Commit();
diff --git a/src/core/hle/kernel/k_client_port.h b/src/core/hle/kernel/k_client_port.h
index e17eff28f..81046fb86 100644
--- a/src/core/hle/kernel/k_client_port.h
+++ b/src/core/hle/kernel/k_client_port.h
@@ -52,8 +52,7 @@ public:
52 void Destroy() override; 52 void Destroy() override;
53 bool IsSignaled() const override; 53 bool IsSignaled() const override;
54 54
55 Result CreateSession(KClientSession** out, 55 Result CreateSession(KClientSession** out);
56 std::shared_ptr<SessionRequestManager> session_manager = nullptr);
57 56
58private: 57private:
59 std::atomic<s32> num_sessions{}; 58 std::atomic<s32> num_sessions{};
diff --git a/src/core/hle/kernel/k_debug.h b/src/core/hle/kernel/k_debug.h
new file mode 100644
index 000000000..e3a0689c8
--- /dev/null
+++ b/src/core/hle/kernel/k_debug.h
@@ -0,0 +1,20 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include "core/hle/kernel/k_auto_object.h"
7#include "core/hle/kernel/slab_helpers.h"
8
9namespace Kernel {
10
11class KDebug final : public KAutoObjectWithSlabHeapAndContainer<KDebug, KAutoObjectWithList> {
12 KERNEL_AUTOOBJECT_TRAITS(KDebug, KAutoObject);
13
14public:
15 explicit KDebug(KernelCore& kernel_) : KAutoObjectWithSlabHeapAndContainer{kernel_} {}
16
17 static void PostDestroy([[maybe_unused]] uintptr_t arg) {}
18};
19
20} // namespace Kernel
diff --git a/src/core/hle/kernel/k_dynamic_page_manager.h b/src/core/hle/kernel/k_dynamic_page_manager.h
index 9076c8fa3..ac80d60a1 100644
--- a/src/core/hle/kernel/k_dynamic_page_manager.h
+++ b/src/core/hle/kernel/k_dynamic_page_manager.h
@@ -3,6 +3,8 @@
3 3
4#pragma once 4#pragma once
5 5
6#include <vector>
7
6#include "common/alignment.h" 8#include "common/alignment.h"
7#include "common/common_types.h" 9#include "common/common_types.h"
8#include "core/hle/kernel/k_page_bitmap.h" 10#include "core/hle/kernel/k_page_bitmap.h"
@@ -33,28 +35,36 @@ public:
33 return reinterpret_cast<T*>(m_backing_memory.data() + (addr - m_address)); 35 return reinterpret_cast<T*>(m_backing_memory.data() + (addr - m_address));
34 } 36 }
35 37
36 Result Initialize(VAddr addr, size_t sz) { 38 Result Initialize(VAddr memory, size_t size, size_t align) {
37 // We need to have positive size. 39 // We need to have positive size.
38 R_UNLESS(sz > 0, ResultOutOfMemory); 40 R_UNLESS(size > 0, ResultOutOfMemory);
39 m_backing_memory.resize(sz); 41 m_backing_memory.resize(size);
42
43 // Set addresses.
44 m_address = memory;
45 m_aligned_address = Common::AlignDown(memory, align);
40 46
41 // Calculate management overhead. 47 // Calculate extents.
42 const size_t management_size = 48 const size_t managed_size = m_address + size - m_aligned_address;
43 KPageBitmap::CalculateManagementOverheadSize(sz / sizeof(PageBuffer)); 49 const size_t overhead_size = Common::AlignUp(
44 const size_t allocatable_size = sz - management_size; 50 KPageBitmap::CalculateManagementOverheadSize(managed_size / sizeof(PageBuffer)),
51 sizeof(PageBuffer));
52 R_UNLESS(overhead_size < size, ResultOutOfMemory);
45 53
46 // Set tracking fields. 54 // Set tracking fields.
47 m_address = addr; 55 m_size = Common::AlignDown(size - overhead_size, sizeof(PageBuffer));
48 m_size = Common::AlignDown(allocatable_size, sizeof(PageBuffer)); 56 m_count = m_size / sizeof(PageBuffer);
49 m_count = allocatable_size / sizeof(PageBuffer);
50 R_UNLESS(m_count > 0, ResultOutOfMemory);
51 57
52 // Clear the management region. 58 // Clear the management region.
53 u64* management_ptr = GetPointer<u64>(m_address + allocatable_size); 59 u64* management_ptr = GetPointer<u64>(m_address + size - overhead_size);
54 std::memset(management_ptr, 0, management_size); 60 std::memset(management_ptr, 0, overhead_size);
55 61
56 // Initialize the bitmap. 62 // Initialize the bitmap.
57 m_page_bitmap.Initialize(management_ptr, m_count); 63 const size_t allocatable_region_size =
64 (m_address + size - overhead_size) - m_aligned_address;
65 ASSERT(allocatable_region_size >= sizeof(PageBuffer));
66
67 m_page_bitmap.Initialize(management_ptr, allocatable_region_size / sizeof(PageBuffer));
58 68
59 // Free the pages to the bitmap. 69 // Free the pages to the bitmap.
60 for (size_t i = 0; i < m_count; i++) { 70 for (size_t i = 0; i < m_count; i++) {
@@ -62,7 +72,8 @@ public:
62 std::memset(GetPointer<PageBuffer>(m_address) + i, 0, PageSize); 72 std::memset(GetPointer<PageBuffer>(m_address) + i, 0, PageSize);
63 73
64 // Set the bit for the free page. 74 // Set the bit for the free page.
65 m_page_bitmap.SetBit(i); 75 m_page_bitmap.SetBit((m_address + (i * sizeof(PageBuffer)) - m_aligned_address) /
76 sizeof(PageBuffer));
66 } 77 }
67 78
68 R_SUCCEED(); 79 R_SUCCEED();
@@ -101,7 +112,28 @@ public:
101 m_page_bitmap.ClearBit(offset); 112 m_page_bitmap.ClearBit(offset);
102 m_peak = std::max(m_peak, (++m_used)); 113 m_peak = std::max(m_peak, (++m_used));
103 114
104 return GetPointer<PageBuffer>(m_address) + offset; 115 return GetPointer<PageBuffer>(m_aligned_address) + offset;
116 }
117
118 PageBuffer* Allocate(size_t count) {
119 // Take the lock.
120 // TODO(bunnei): We should disable interrupts here via KScopedInterruptDisable.
121 KScopedSpinLock lk(m_lock);
122
123 // Find a random free block.
124 s64 soffset = m_page_bitmap.FindFreeRange(count);
125 if (soffset < 0) [[likely]] {
126 return nullptr;
127 }
128
129 const size_t offset = static_cast<size_t>(soffset);
130
131 // Update our tracking.
132 m_page_bitmap.ClearRange(offset, count);
133 m_used += count;
134 m_peak = std::max(m_peak, m_used);
135
136 return GetPointer<PageBuffer>(m_aligned_address) + offset;
105 } 137 }
106 138
107 void Free(PageBuffer* pb) { 139 void Free(PageBuffer* pb) {
@@ -113,7 +145,7 @@ public:
113 KScopedSpinLock lk(m_lock); 145 KScopedSpinLock lk(m_lock);
114 146
115 // Set the bit for the free page. 147 // Set the bit for the free page.
116 size_t offset = (reinterpret_cast<uintptr_t>(pb) - m_address) / sizeof(PageBuffer); 148 size_t offset = (reinterpret_cast<uintptr_t>(pb) - m_aligned_address) / sizeof(PageBuffer);
117 m_page_bitmap.SetBit(offset); 149 m_page_bitmap.SetBit(offset);
118 150
119 // Decrement our used count. 151 // Decrement our used count.
@@ -127,6 +159,7 @@ private:
127 size_t m_peak{}; 159 size_t m_peak{};
128 size_t m_count{}; 160 size_t m_count{};
129 VAddr m_address{}; 161 VAddr m_address{};
162 VAddr m_aligned_address{};
130 size_t m_size{}; 163 size_t m_size{};
131 164
132 // TODO(bunnei): Back by host memory until we emulate kernel virtual address space. 165 // TODO(bunnei): Back by host memory until we emulate kernel virtual address space.
diff --git a/src/core/hle/kernel/k_dynamic_resource_manager.h b/src/core/hle/kernel/k_dynamic_resource_manager.h
index 1ce517e8e..b6a27d648 100644
--- a/src/core/hle/kernel/k_dynamic_resource_manager.h
+++ b/src/core/hle/kernel/k_dynamic_resource_manager.h
@@ -6,6 +6,7 @@
6#include "common/common_funcs.h" 6#include "common/common_funcs.h"
7#include "core/hle/kernel/k_dynamic_slab_heap.h" 7#include "core/hle/kernel/k_dynamic_slab_heap.h"
8#include "core/hle/kernel/k_memory_block.h" 8#include "core/hle/kernel/k_memory_block.h"
9#include "core/hle/kernel/k_page_group.h"
9 10
10namespace Kernel { 11namespace Kernel {
11 12
@@ -51,8 +52,10 @@ private:
51 DynamicSlabType* m_slab_heap{}; 52 DynamicSlabType* m_slab_heap{};
52}; 53};
53 54
55class KBlockInfoManager : public KDynamicResourceManager<KBlockInfo> {};
54class KMemoryBlockSlabManager : public KDynamicResourceManager<KMemoryBlock> {}; 56class KMemoryBlockSlabManager : public KDynamicResourceManager<KMemoryBlock> {};
55 57
58using KBlockInfoSlabHeap = typename KBlockInfoManager::DynamicSlabType;
56using KMemoryBlockSlabHeap = typename KMemoryBlockSlabManager::DynamicSlabType; 59using KMemoryBlockSlabHeap = typename KMemoryBlockSlabManager::DynamicSlabType;
57 60
58} // namespace Kernel 61} // namespace Kernel
diff --git a/src/core/hle/kernel/k_event.cpp b/src/core/hle/kernel/k_event.cpp
index 78ca59463..27f70e5c5 100644
--- a/src/core/hle/kernel/k_event.cpp
+++ b/src/core/hle/kernel/k_event.cpp
@@ -50,7 +50,7 @@ Result KEvent::Clear() {
50void KEvent::PostDestroy(uintptr_t arg) { 50void KEvent::PostDestroy(uintptr_t arg) {
51 // Release the event count resource the owner process holds. 51 // Release the event count resource the owner process holds.
52 KProcess* owner = reinterpret_cast<KProcess*>(arg); 52 KProcess* owner = reinterpret_cast<KProcess*>(arg);
53 owner->GetResourceLimit()->Release(LimitableResource::Events, 1); 53 owner->GetResourceLimit()->Release(LimitableResource::EventCountMax, 1);
54 owner->Close(); 54 owner->Close();
55} 55}
56 56
diff --git a/src/core/hle/kernel/k_event_info.h b/src/core/hle/kernel/k_event_info.h
new file mode 100644
index 000000000..25b3ff594
--- /dev/null
+++ b/src/core/hle/kernel/k_event_info.h
@@ -0,0 +1,64 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include <array>
7
8#include <boost/intrusive/list.hpp>
9
10#include "core/hle/kernel/slab_helpers.h"
11#include "core/hle/kernel/svc_types.h"
12
13namespace Kernel {
14
15class KEventInfo : public KSlabAllocated<KEventInfo>, public boost::intrusive::list_base_hook<> {
16public:
17 struct InfoCreateThread {
18 u32 thread_id{};
19 uintptr_t tls_address{};
20 };
21
22 struct InfoExitProcess {
23 Svc::ProcessExitReason reason{};
24 };
25
26 struct InfoExitThread {
27 Svc::ThreadExitReason reason{};
28 };
29
30 struct InfoException {
31 Svc::DebugException exception_type{};
32 s32 exception_data_count{};
33 uintptr_t exception_address{};
34 std::array<uintptr_t, 4> exception_data{};
35 };
36
37 struct InfoSystemCall {
38 s64 tick{};
39 s32 id{};
40 };
41
42public:
43 KEventInfo() = default;
44 ~KEventInfo() = default;
45
46public:
47 Svc::DebugEvent event{};
48 u32 thread_id{};
49 u32 flags{};
50 bool is_attached{};
51 bool continue_flag{};
52 bool ignore_continue{};
53 bool close_once{};
54 union {
55 InfoCreateThread create_thread;
56 InfoExitProcess exit_process;
57 InfoExitThread exit_thread;
58 InfoException exception;
59 InfoSystemCall system_call;
60 } info{};
61 KThread* debug_thread{};
62};
63
64} // namespace Kernel
diff --git a/src/core/hle/kernel/k_handle_table.cpp b/src/core/hle/kernel/k_handle_table.cpp
index e830ca46e..1c7a766c8 100644
--- a/src/core/hle/kernel/k_handle_table.cpp
+++ b/src/core/hle/kernel/k_handle_table.cpp
@@ -5,14 +5,11 @@
5 5
6namespace Kernel { 6namespace Kernel {
7 7
8KHandleTable::KHandleTable(KernelCore& kernel_) : kernel{kernel_} {}
9KHandleTable::~KHandleTable() = default;
10
11Result KHandleTable::Finalize() { 8Result KHandleTable::Finalize() {
12 // Get the table and clear our record of it. 9 // Get the table and clear our record of it.
13 u16 saved_table_size = 0; 10 u16 saved_table_size = 0;
14 { 11 {
15 KScopedDisableDispatch dd(kernel); 12 KScopedDisableDispatch dd{m_kernel};
16 KScopedSpinLock lk(m_lock); 13 KScopedSpinLock lk(m_lock);
17 14
18 std::swap(m_table_size, saved_table_size); 15 std::swap(m_table_size, saved_table_size);
@@ -25,28 +22,28 @@ Result KHandleTable::Finalize() {
25 } 22 }
26 } 23 }
27 24
28 return ResultSuccess; 25 R_SUCCEED();
29} 26}
30 27
31bool KHandleTable::Remove(Handle handle) { 28bool KHandleTable::Remove(Handle handle) {
32 // Don't allow removal of a pseudo-handle. 29 // Don't allow removal of a pseudo-handle.
33 if (Svc::IsPseudoHandle(handle)) { 30 if (Svc::IsPseudoHandle(handle)) [[unlikely]] {
34 return false; 31 return false;
35 } 32 }
36 33
37 // Handles must not have reserved bits set. 34 // Handles must not have reserved bits set.
38 const auto handle_pack = HandlePack(handle); 35 const auto handle_pack = HandlePack(handle);
39 if (handle_pack.reserved != 0) { 36 if (handle_pack.reserved != 0) [[unlikely]] {
40 return false; 37 return false;
41 } 38 }
42 39
43 // Find the object and free the entry. 40 // Find the object and free the entry.
44 KAutoObject* obj = nullptr; 41 KAutoObject* obj = nullptr;
45 { 42 {
46 KScopedDisableDispatch dd(kernel); 43 KScopedDisableDispatch dd{m_kernel};
47 KScopedSpinLock lk(m_lock); 44 KScopedSpinLock lk(m_lock);
48 45
49 if (this->IsValidHandle(handle)) { 46 if (this->IsValidHandle(handle)) [[likely]] {
50 const auto index = handle_pack.index; 47 const auto index = handle_pack.index;
51 48
52 obj = m_objects[index]; 49 obj = m_objects[index];
@@ -57,13 +54,13 @@ bool KHandleTable::Remove(Handle handle) {
57 } 54 }
58 55
59 // Close the object. 56 // Close the object.
60 kernel.UnregisterInUseObject(obj); 57 m_kernel.UnregisterInUseObject(obj);
61 obj->Close(); 58 obj->Close();
62 return true; 59 return true;
63} 60}
64 61
65Result KHandleTable::Add(Handle* out_handle, KAutoObject* obj) { 62Result KHandleTable::Add(Handle* out_handle, KAutoObject* obj) {
66 KScopedDisableDispatch dd(kernel); 63 KScopedDisableDispatch dd{m_kernel};
67 KScopedSpinLock lk(m_lock); 64 KScopedSpinLock lk(m_lock);
68 65
69 // Never exceed our capacity. 66 // Never exceed our capacity.
@@ -82,22 +79,22 @@ Result KHandleTable::Add(Handle* out_handle, KAutoObject* obj) {
82 *out_handle = EncodeHandle(static_cast<u16>(index), linear_id); 79 *out_handle = EncodeHandle(static_cast<u16>(index), linear_id);
83 } 80 }
84 81
85 return ResultSuccess; 82 R_SUCCEED();
86} 83}
87 84
88Result KHandleTable::Reserve(Handle* out_handle) { 85Result KHandleTable::Reserve(Handle* out_handle) {
89 KScopedDisableDispatch dd(kernel); 86 KScopedDisableDispatch dd{m_kernel};
90 KScopedSpinLock lk(m_lock); 87 KScopedSpinLock lk(m_lock);
91 88
92 // Never exceed our capacity. 89 // Never exceed our capacity.
93 R_UNLESS(m_count < m_table_size, ResultOutOfHandles); 90 R_UNLESS(m_count < m_table_size, ResultOutOfHandles);
94 91
95 *out_handle = EncodeHandle(static_cast<u16>(this->AllocateEntry()), this->AllocateLinearId()); 92 *out_handle = EncodeHandle(static_cast<u16>(this->AllocateEntry()), this->AllocateLinearId());
96 return ResultSuccess; 93 R_SUCCEED();
97} 94}
98 95
99void KHandleTable::Unreserve(Handle handle) { 96void KHandleTable::Unreserve(Handle handle) {
100 KScopedDisableDispatch dd(kernel); 97 KScopedDisableDispatch dd{m_kernel};
101 KScopedSpinLock lk(m_lock); 98 KScopedSpinLock lk(m_lock);
102 99
103 // Unpack the handle. 100 // Unpack the handle.
@@ -108,7 +105,7 @@ void KHandleTable::Unreserve(Handle handle) {
108 ASSERT(reserved == 0); 105 ASSERT(reserved == 0);
109 ASSERT(linear_id != 0); 106 ASSERT(linear_id != 0);
110 107
111 if (index < m_table_size) { 108 if (index < m_table_size) [[likely]] {
112 // NOTE: This code does not check the linear id. 109 // NOTE: This code does not check the linear id.
113 ASSERT(m_objects[index] == nullptr); 110 ASSERT(m_objects[index] == nullptr);
114 this->FreeEntry(index); 111 this->FreeEntry(index);
@@ -116,7 +113,7 @@ void KHandleTable::Unreserve(Handle handle) {
116} 113}
117 114
118void KHandleTable::Register(Handle handle, KAutoObject* obj) { 115void KHandleTable::Register(Handle handle, KAutoObject* obj) {
119 KScopedDisableDispatch dd(kernel); 116 KScopedDisableDispatch dd{m_kernel};
120 KScopedSpinLock lk(m_lock); 117 KScopedSpinLock lk(m_lock);
121 118
122 // Unpack the handle. 119 // Unpack the handle.
@@ -127,7 +124,7 @@ void KHandleTable::Register(Handle handle, KAutoObject* obj) {
127 ASSERT(reserved == 0); 124 ASSERT(reserved == 0);
128 ASSERT(linear_id != 0); 125 ASSERT(linear_id != 0);
129 126
130 if (index < m_table_size) { 127 if (index < m_table_size) [[likely]] {
131 // Set the entry. 128 // Set the entry.
132 ASSERT(m_objects[index] == nullptr); 129 ASSERT(m_objects[index] == nullptr);
133 130
diff --git a/src/core/hle/kernel/k_handle_table.h b/src/core/hle/kernel/k_handle_table.h
index 0864a737c..65cae3b27 100644
--- a/src/core/hle/kernel/k_handle_table.h
+++ b/src/core/hle/kernel/k_handle_table.h
@@ -21,33 +21,38 @@ namespace Kernel {
21class KernelCore; 21class KernelCore;
22 22
23class KHandleTable { 23class KHandleTable {
24public:
25 YUZU_NON_COPYABLE(KHandleTable); 24 YUZU_NON_COPYABLE(KHandleTable);
26 YUZU_NON_MOVEABLE(KHandleTable); 25 YUZU_NON_MOVEABLE(KHandleTable);
27 26
27public:
28 static constexpr size_t MaxTableSize = 1024; 28 static constexpr size_t MaxTableSize = 1024;
29 29
30 explicit KHandleTable(KernelCore& kernel_); 30public:
31 ~KHandleTable(); 31 explicit KHandleTable(KernelCore& kernel) : m_kernel(kernel) {}
32 32
33 Result Initialize(s32 size) { 33 Result Initialize(s32 size) {
34 // Check that the table size is valid.
34 R_UNLESS(size <= static_cast<s32>(MaxTableSize), ResultOutOfMemory); 35 R_UNLESS(size <= static_cast<s32>(MaxTableSize), ResultOutOfMemory);
35 36
37 // Lock.
38 KScopedDisableDispatch dd{m_kernel};
39 KScopedSpinLock lk(m_lock);
40
36 // Initialize all fields. 41 // Initialize all fields.
37 m_max_count = 0; 42 m_max_count = 0;
38 m_table_size = static_cast<u16>((size <= 0) ? MaxTableSize : size); 43 m_table_size = static_cast<s16>((size <= 0) ? MaxTableSize : size);
39 m_next_linear_id = MinLinearId; 44 m_next_linear_id = MinLinearId;
40 m_count = 0; 45 m_count = 0;
41 m_free_head_index = -1; 46 m_free_head_index = -1;
42 47
43 // Free all entries. 48 // Free all entries.
44 for (s16 i = 0; i < static_cast<s16>(m_table_size); ++i) { 49 for (s32 i = 0; i < static_cast<s32>(m_table_size); ++i) {
45 m_objects[i] = nullptr; 50 m_objects[i] = nullptr;
46 m_entry_infos[i].next_free_index = i - 1; 51 m_entry_infos[i].next_free_index = static_cast<s16>(i - 1);
47 m_free_head_index = i; 52 m_free_head_index = i;
48 } 53 }
49 54
50 return ResultSuccess; 55 R_SUCCEED();
51 } 56 }
52 57
53 size_t GetTableSize() const { 58 size_t GetTableSize() const {
@@ -66,13 +71,13 @@ public:
66 template <typename T = KAutoObject> 71 template <typename T = KAutoObject>
67 KScopedAutoObject<T> GetObjectWithoutPseudoHandle(Handle handle) const { 72 KScopedAutoObject<T> GetObjectWithoutPseudoHandle(Handle handle) const {
68 // Lock and look up in table. 73 // Lock and look up in table.
69 KScopedDisableDispatch dd(kernel); 74 KScopedDisableDispatch dd{m_kernel};
70 KScopedSpinLock lk(m_lock); 75 KScopedSpinLock lk(m_lock);
71 76
72 if constexpr (std::is_same_v<T, KAutoObject>) { 77 if constexpr (std::is_same_v<T, KAutoObject>) {
73 return this->GetObjectImpl(handle); 78 return this->GetObjectImpl(handle);
74 } else { 79 } else {
75 if (auto* obj = this->GetObjectImpl(handle); obj != nullptr) { 80 if (auto* obj = this->GetObjectImpl(handle); obj != nullptr) [[likely]] {
76 return obj->DynamicCast<T*>(); 81 return obj->DynamicCast<T*>();
77 } else { 82 } else {
78 return nullptr; 83 return nullptr;
@@ -85,13 +90,13 @@ public:
85 // Handle pseudo-handles. 90 // Handle pseudo-handles.
86 if constexpr (std::derived_from<KProcess, T>) { 91 if constexpr (std::derived_from<KProcess, T>) {
87 if (handle == Svc::PseudoHandle::CurrentProcess) { 92 if (handle == Svc::PseudoHandle::CurrentProcess) {
88 auto* const cur_process = kernel.CurrentProcess(); 93 auto* const cur_process = m_kernel.CurrentProcess();
89 ASSERT(cur_process != nullptr); 94 ASSERT(cur_process != nullptr);
90 return cur_process; 95 return cur_process;
91 } 96 }
92 } else if constexpr (std::derived_from<KThread, T>) { 97 } else if constexpr (std::derived_from<KThread, T>) {
93 if (handle == Svc::PseudoHandle::CurrentThread) { 98 if (handle == Svc::PseudoHandle::CurrentThread) {
94 auto* const cur_thread = GetCurrentThreadPointer(kernel); 99 auto* const cur_thread = GetCurrentThreadPointer(m_kernel);
95 ASSERT(cur_thread != nullptr); 100 ASSERT(cur_thread != nullptr);
96 return cur_thread; 101 return cur_thread;
97 } 102 }
@@ -100,6 +105,37 @@ public:
100 return this->template GetObjectWithoutPseudoHandle<T>(handle); 105 return this->template GetObjectWithoutPseudoHandle<T>(handle);
101 } 106 }
102 107
108 KScopedAutoObject<KAutoObject> GetObjectForIpcWithoutPseudoHandle(Handle handle) const {
109 // Lock and look up in table.
110 KScopedDisableDispatch dd{m_kernel};
111 KScopedSpinLock lk(m_lock);
112
113 return this->GetObjectImpl(handle);
114 }
115
116 KScopedAutoObject<KAutoObject> GetObjectForIpc(Handle handle, KThread* cur_thread) const {
117 // Handle pseudo-handles.
118 ASSERT(cur_thread != nullptr);
119 if (handle == Svc::PseudoHandle::CurrentProcess) {
120 auto* const cur_process =
121 static_cast<KAutoObject*>(static_cast<void*>(cur_thread->GetOwnerProcess()));
122 ASSERT(cur_process != nullptr);
123 return cur_process;
124 }
125 if (handle == Svc::PseudoHandle::CurrentThread) {
126 return static_cast<KAutoObject*>(cur_thread);
127 }
128
129 return GetObjectForIpcWithoutPseudoHandle(handle);
130 }
131
132 KScopedAutoObject<KAutoObject> GetObjectByIndex(Handle* out_handle, size_t index) const {
133 KScopedDisableDispatch dd{m_kernel};
134 KScopedSpinLock lk(m_lock);
135
136 return this->GetObjectByIndexImpl(out_handle, index);
137 }
138
103 Result Reserve(Handle* out_handle); 139 Result Reserve(Handle* out_handle);
104 void Unreserve(Handle handle); 140 void Unreserve(Handle handle);
105 141
@@ -112,7 +148,7 @@ public:
112 size_t num_opened; 148 size_t num_opened;
113 { 149 {
114 // Lock the table. 150 // Lock the table.
115 KScopedDisableDispatch dd(kernel); 151 KScopedDisableDispatch dd{m_kernel};
116 KScopedSpinLock lk(m_lock); 152 KScopedSpinLock lk(m_lock);
117 for (num_opened = 0; num_opened < num_handles; num_opened++) { 153 for (num_opened = 0; num_opened < num_handles; num_opened++) {
118 // Get the current handle. 154 // Get the current handle.
@@ -120,13 +156,13 @@ public:
120 156
121 // Get the object for the current handle. 157 // Get the object for the current handle.
122 KAutoObject* cur_object = this->GetObjectImpl(cur_handle); 158 KAutoObject* cur_object = this->GetObjectImpl(cur_handle);
123 if (cur_object == nullptr) { 159 if (cur_object == nullptr) [[unlikely]] {
124 break; 160 break;
125 } 161 }
126 162
127 // Cast the current object to the desired type. 163 // Cast the current object to the desired type.
128 T* cur_t = cur_object->DynamicCast<T*>(); 164 T* cur_t = cur_object->DynamicCast<T*>();
129 if (cur_t == nullptr) { 165 if (cur_t == nullptr) [[unlikely]] {
130 break; 166 break;
131 } 167 }
132 168
@@ -137,7 +173,7 @@ public:
137 } 173 }
138 174
139 // If we converted every object, succeed. 175 // If we converted every object, succeed.
140 if (num_opened == num_handles) { 176 if (num_opened == num_handles) [[likely]] {
141 return true; 177 return true;
142 } 178 }
143 179
@@ -191,21 +227,21 @@ private:
191 ASSERT(reserved == 0); 227 ASSERT(reserved == 0);
192 228
193 // Validate our indexing information. 229 // Validate our indexing information.
194 if (raw_value == 0) { 230 if (raw_value == 0) [[unlikely]] {
195 return false; 231 return false;
196 } 232 }
197 if (linear_id == 0) { 233 if (linear_id == 0) [[unlikely]] {
198 return false; 234 return false;
199 } 235 }
200 if (index >= m_table_size) { 236 if (index >= m_table_size) [[unlikely]] {
201 return false; 237 return false;
202 } 238 }
203 239
204 // Check that there's an object, and our serial id is correct. 240 // Check that there's an object, and our serial id is correct.
205 if (m_objects[index] == nullptr) { 241 if (m_objects[index] == nullptr) [[unlikely]] {
206 return false; 242 return false;
207 } 243 }
208 if (m_entry_infos[index].GetLinearId() != linear_id) { 244 if (m_entry_infos[index].GetLinearId() != linear_id) [[unlikely]] {
209 return false; 245 return false;
210 } 246 }
211 247
@@ -215,11 +251,11 @@ private:
215 KAutoObject* GetObjectImpl(Handle handle) const { 251 KAutoObject* GetObjectImpl(Handle handle) const {
216 // Handles must not have reserved bits set. 252 // Handles must not have reserved bits set.
217 const auto handle_pack = HandlePack(handle); 253 const auto handle_pack = HandlePack(handle);
218 if (handle_pack.reserved != 0) { 254 if (handle_pack.reserved != 0) [[unlikely]] {
219 return nullptr; 255 return nullptr;
220 } 256 }
221 257
222 if (this->IsValidHandle(handle)) { 258 if (this->IsValidHandle(handle)) [[likely]] {
223 return m_objects[handle_pack.index]; 259 return m_objects[handle_pack.index];
224 } else { 260 } else {
225 return nullptr; 261 return nullptr;
@@ -227,9 +263,8 @@ private:
227 } 263 }
228 264
229 KAutoObject* GetObjectByIndexImpl(Handle* out_handle, size_t index) const { 265 KAutoObject* GetObjectByIndexImpl(Handle* out_handle, size_t index) const {
230
231 // Index must be in bounds. 266 // Index must be in bounds.
232 if (index >= m_table_size) { 267 if (index >= m_table_size) [[unlikely]] {
233 return nullptr; 268 return nullptr;
234 } 269 }
235 270
@@ -244,18 +279,15 @@ private:
244 279
245private: 280private:
246 union HandlePack { 281 union HandlePack {
247 HandlePack() = default; 282 constexpr HandlePack() = default;
248 HandlePack(Handle handle) : raw{static_cast<u32>(handle)} {} 283 constexpr HandlePack(Handle handle) : raw{static_cast<u32>(handle)} {}
249 284
250 u32 raw; 285 u32 raw{};
251 BitField<0, 15, u32> index; 286 BitField<0, 15, u32> index;
252 BitField<15, 15, u32> linear_id; 287 BitField<15, 15, u32> linear_id;
253 BitField<30, 2, u32> reserved; 288 BitField<30, 2, u32> reserved;
254 }; 289 };
255 290
256 static constexpr u16 MinLinearId = 1;
257 static constexpr u16 MaxLinearId = 0x7FFF;
258
259 static constexpr Handle EncodeHandle(u16 index, u16 linear_id) { 291 static constexpr Handle EncodeHandle(u16 index, u16 linear_id) {
260 HandlePack handle{}; 292 HandlePack handle{};
261 handle.index.Assign(index); 293 handle.index.Assign(index);
@@ -264,6 +296,10 @@ private:
264 return handle.raw; 296 return handle.raw;
265 } 297 }
266 298
299private:
300 static constexpr u16 MinLinearId = 1;
301 static constexpr u16 MaxLinearId = 0x7FFF;
302
267 union EntryInfo { 303 union EntryInfo {
268 u16 linear_id; 304 u16 linear_id;
269 s16 next_free_index; 305 s16 next_free_index;
@@ -271,21 +307,21 @@ private:
271 constexpr u16 GetLinearId() const { 307 constexpr u16 GetLinearId() const {
272 return linear_id; 308 return linear_id;
273 } 309 }
274 constexpr s16 GetNextFreeIndex() const { 310 constexpr s32 GetNextFreeIndex() const {
275 return next_free_index; 311 return next_free_index;
276 } 312 }
277 }; 313 };
278 314
279private: 315private:
316 KernelCore& m_kernel;
280 std::array<EntryInfo, MaxTableSize> m_entry_infos{}; 317 std::array<EntryInfo, MaxTableSize> m_entry_infos{};
281 std::array<KAutoObject*, MaxTableSize> m_objects{}; 318 std::array<KAutoObject*, MaxTableSize> m_objects{};
282 s32 m_free_head_index{-1}; 319 mutable KSpinLock m_lock;
320 s32 m_free_head_index{};
283 u16 m_table_size{}; 321 u16 m_table_size{};
284 u16 m_max_count{}; 322 u16 m_max_count{};
285 u16 m_next_linear_id{MinLinearId}; 323 u16 m_next_linear_id{};
286 u16 m_count{}; 324 u16 m_count{};
287 mutable KSpinLock m_lock;
288 KernelCore& kernel;
289}; 325};
290 326
291} // namespace Kernel 327} // namespace Kernel
diff --git a/src/core/hle/kernel/k_memory_block.h b/src/core/hle/kernel/k_memory_block.h
index 9444f6bd2..3b6e7baff 100644
--- a/src/core/hle/kernel/k_memory_block.h
+++ b/src/core/hle/kernel/k_memory_block.h
@@ -35,26 +35,32 @@ enum class KMemoryState : u32 {
35 FlagCanMapProcess = (1 << 23), 35 FlagCanMapProcess = (1 << 23),
36 FlagCanChangeAttribute = (1 << 24), 36 FlagCanChangeAttribute = (1 << 24),
37 FlagCanCodeMemory = (1 << 25), 37 FlagCanCodeMemory = (1 << 25),
38 FlagLinearMapped = (1 << 26),
38 39
39 FlagsData = FlagCanReprotect | FlagCanUseIpc | FlagCanUseNonDeviceIpc | FlagCanUseNonSecureIpc | 40 FlagsData = FlagCanReprotect | FlagCanUseIpc | FlagCanUseNonDeviceIpc | FlagCanUseNonSecureIpc |
40 FlagMapped | FlagCanAlias | FlagCanTransfer | FlagCanQueryPhysical | 41 FlagMapped | FlagCanAlias | FlagCanTransfer | FlagCanQueryPhysical |
41 FlagCanDeviceMap | FlagCanAlignedDeviceMap | FlagCanIpcUserBuffer | 42 FlagCanDeviceMap | FlagCanAlignedDeviceMap | FlagCanIpcUserBuffer |
42 FlagReferenceCounted | FlagCanChangeAttribute, 43 FlagReferenceCounted | FlagCanChangeAttribute | FlagLinearMapped,
43 44
44 FlagsCode = FlagCanDebug | FlagCanUseIpc | FlagCanUseNonDeviceIpc | FlagCanUseNonSecureIpc | 45 FlagsCode = FlagCanDebug | FlagCanUseIpc | FlagCanUseNonDeviceIpc | FlagCanUseNonSecureIpc |
45 FlagMapped | FlagCode | FlagCanQueryPhysical | FlagCanDeviceMap | 46 FlagMapped | FlagCode | FlagCanQueryPhysical | FlagCanDeviceMap |
46 FlagCanAlignedDeviceMap | FlagReferenceCounted, 47 FlagCanAlignedDeviceMap | FlagReferenceCounted | FlagLinearMapped,
47 48
48 FlagsMisc = FlagMapped | FlagReferenceCounted | FlagCanQueryPhysical | FlagCanDeviceMap, 49 FlagsMisc = FlagMapped | FlagReferenceCounted | FlagCanQueryPhysical | FlagCanDeviceMap |
50 FlagLinearMapped,
49 51
50 Free = static_cast<u32>(Svc::MemoryState::Free), 52 Free = static_cast<u32>(Svc::MemoryState::Free),
51 Io = static_cast<u32>(Svc::MemoryState::Io) | FlagMapped, 53 Io = static_cast<u32>(Svc::MemoryState::Io) | FlagMapped | FlagCanDeviceMap |
54 FlagCanAlignedDeviceMap,
52 Static = static_cast<u32>(Svc::MemoryState::Static) | FlagMapped | FlagCanQueryPhysical, 55 Static = static_cast<u32>(Svc::MemoryState::Static) | FlagMapped | FlagCanQueryPhysical,
53 Code = static_cast<u32>(Svc::MemoryState::Code) | FlagsCode | FlagCanMapProcess, 56 Code = static_cast<u32>(Svc::MemoryState::Code) | FlagsCode | FlagCanMapProcess,
54 CodeData = static_cast<u32>(Svc::MemoryState::CodeData) | FlagsData | FlagCanMapProcess | 57 CodeData = static_cast<u32>(Svc::MemoryState::CodeData) | FlagsData | FlagCanMapProcess |
55 FlagCanCodeMemory, 58 FlagCanCodeMemory,
56 Shared = static_cast<u32>(Svc::MemoryState::Shared) | FlagMapped | FlagReferenceCounted,
57 Normal = static_cast<u32>(Svc::MemoryState::Normal) | FlagsData | FlagCanCodeMemory, 59 Normal = static_cast<u32>(Svc::MemoryState::Normal) | FlagsData | FlagCanCodeMemory,
60 Shared = static_cast<u32>(Svc::MemoryState::Shared) | FlagMapped | FlagReferenceCounted |
61 FlagLinearMapped,
62
63 // Alias was removed after 1.0.0.
58 64
59 AliasCode = static_cast<u32>(Svc::MemoryState::AliasCode) | FlagsCode | FlagCanMapProcess | 65 AliasCode = static_cast<u32>(Svc::MemoryState::AliasCode) | FlagsCode | FlagCanMapProcess |
60 FlagCanCodeAlias, 66 FlagCanCodeAlias,
@@ -67,18 +73,18 @@ enum class KMemoryState : u32 {
67 Stack = static_cast<u32>(Svc::MemoryState::Stack) | FlagsMisc | FlagCanAlignedDeviceMap | 73 Stack = static_cast<u32>(Svc::MemoryState::Stack) | FlagsMisc | FlagCanAlignedDeviceMap |
68 FlagCanUseIpc | FlagCanUseNonSecureIpc | FlagCanUseNonDeviceIpc, 74 FlagCanUseIpc | FlagCanUseNonSecureIpc | FlagCanUseNonDeviceIpc,
69 75
70 ThreadLocal = 76 ThreadLocal = static_cast<u32>(Svc::MemoryState::ThreadLocal) | FlagMapped | FlagLinearMapped,
71 static_cast<u32>(Svc::MemoryState::ThreadLocal) | FlagMapped | FlagReferenceCounted,
72 77
73 Transfered = static_cast<u32>(Svc::MemoryState::Transferred) | FlagsMisc | 78 Transfered = static_cast<u32>(Svc::MemoryState::Transfered) | FlagsMisc |
74 FlagCanAlignedDeviceMap | FlagCanChangeAttribute | FlagCanUseIpc | 79 FlagCanAlignedDeviceMap | FlagCanChangeAttribute | FlagCanUseIpc |
75 FlagCanUseNonSecureIpc | FlagCanUseNonDeviceIpc, 80 FlagCanUseNonSecureIpc | FlagCanUseNonDeviceIpc,
76 81
77 SharedTransfered = static_cast<u32>(Svc::MemoryState::SharedTransferred) | FlagsMisc | 82 SharedTransfered = static_cast<u32>(Svc::MemoryState::SharedTransfered) | FlagsMisc |
78 FlagCanAlignedDeviceMap | FlagCanUseNonSecureIpc | FlagCanUseNonDeviceIpc, 83 FlagCanAlignedDeviceMap | FlagCanUseNonSecureIpc | FlagCanUseNonDeviceIpc,
79 84
80 SharedCode = static_cast<u32>(Svc::MemoryState::SharedCode) | FlagMapped | 85 SharedCode = static_cast<u32>(Svc::MemoryState::SharedCode) | FlagMapped |
81 FlagReferenceCounted | FlagCanUseNonSecureIpc | FlagCanUseNonDeviceIpc, 86 FlagReferenceCounted | FlagLinearMapped | FlagCanUseNonSecureIpc |
87 FlagCanUseNonDeviceIpc,
82 88
83 Inaccessible = static_cast<u32>(Svc::MemoryState::Inaccessible), 89 Inaccessible = static_cast<u32>(Svc::MemoryState::Inaccessible),
84 90
@@ -91,69 +97,69 @@ enum class KMemoryState : u32 {
91 Kernel = static_cast<u32>(Svc::MemoryState::Kernel) | FlagMapped, 97 Kernel = static_cast<u32>(Svc::MemoryState::Kernel) | FlagMapped,
92 98
93 GeneratedCode = static_cast<u32>(Svc::MemoryState::GeneratedCode) | FlagMapped | 99 GeneratedCode = static_cast<u32>(Svc::MemoryState::GeneratedCode) | FlagMapped |
94 FlagReferenceCounted | FlagCanDebug, 100 FlagReferenceCounted | FlagCanDebug | FlagLinearMapped,
95 CodeOut = static_cast<u32>(Svc::MemoryState::CodeOut) | FlagMapped | FlagReferenceCounted, 101 CodeOut = static_cast<u32>(Svc::MemoryState::CodeOut) | FlagMapped | FlagReferenceCounted |
102 FlagLinearMapped,
96 103
97 Coverage = static_cast<u32>(Svc::MemoryState::Coverage) | FlagMapped, 104 Coverage = static_cast<u32>(Svc::MemoryState::Coverage) | FlagMapped,
105
106 Insecure = static_cast<u32>(Svc::MemoryState::Insecure) | FlagMapped | FlagReferenceCounted |
107 FlagLinearMapped | FlagCanChangeAttribute | FlagCanDeviceMap |
108 FlagCanAlignedDeviceMap | FlagCanUseNonSecureIpc | FlagCanUseNonDeviceIpc,
98}; 109};
99DECLARE_ENUM_FLAG_OPERATORS(KMemoryState); 110DECLARE_ENUM_FLAG_OPERATORS(KMemoryState);
100 111
101static_assert(static_cast<u32>(KMemoryState::Free) == 0x00000000); 112static_assert(static_cast<u32>(KMemoryState::Free) == 0x00000000);
102static_assert(static_cast<u32>(KMemoryState::Io) == 0x00002001); 113static_assert(static_cast<u32>(KMemoryState::Io) == 0x00182001);
103static_assert(static_cast<u32>(KMemoryState::Static) == 0x00042002); 114static_assert(static_cast<u32>(KMemoryState::Static) == 0x00042002);
104static_assert(static_cast<u32>(KMemoryState::Code) == 0x00DC7E03); 115static_assert(static_cast<u32>(KMemoryState::Code) == 0x04DC7E03);
105static_assert(static_cast<u32>(KMemoryState::CodeData) == 0x03FEBD04); 116static_assert(static_cast<u32>(KMemoryState::CodeData) == 0x07FEBD04);
106static_assert(static_cast<u32>(KMemoryState::Normal) == 0x037EBD05); 117static_assert(static_cast<u32>(KMemoryState::Normal) == 0x077EBD05);
107static_assert(static_cast<u32>(KMemoryState::Shared) == 0x00402006); 118static_assert(static_cast<u32>(KMemoryState::Shared) == 0x04402006);
108static_assert(static_cast<u32>(KMemoryState::AliasCode) == 0x00DD7E08); 119
109static_assert(static_cast<u32>(KMemoryState::AliasCodeData) == 0x03FFBD09); 120static_assert(static_cast<u32>(KMemoryState::AliasCode) == 0x04DD7E08);
110static_assert(static_cast<u32>(KMemoryState::Ipc) == 0x005C3C0A); 121static_assert(static_cast<u32>(KMemoryState::AliasCodeData) == 0x07FFBD09);
111static_assert(static_cast<u32>(KMemoryState::Stack) == 0x005C3C0B); 122static_assert(static_cast<u32>(KMemoryState::Ipc) == 0x045C3C0A);
112static_assert(static_cast<u32>(KMemoryState::ThreadLocal) == 0x0040200C); 123static_assert(static_cast<u32>(KMemoryState::Stack) == 0x045C3C0B);
113static_assert(static_cast<u32>(KMemoryState::Transfered) == 0x015C3C0D); 124static_assert(static_cast<u32>(KMemoryState::ThreadLocal) == 0x0400200C);
114static_assert(static_cast<u32>(KMemoryState::SharedTransfered) == 0x005C380E); 125static_assert(static_cast<u32>(KMemoryState::Transfered) == 0x055C3C0D);
115static_assert(static_cast<u32>(KMemoryState::SharedCode) == 0x0040380F); 126static_assert(static_cast<u32>(KMemoryState::SharedTransfered) == 0x045C380E);
127static_assert(static_cast<u32>(KMemoryState::SharedCode) == 0x0440380F);
116static_assert(static_cast<u32>(KMemoryState::Inaccessible) == 0x00000010); 128static_assert(static_cast<u32>(KMemoryState::Inaccessible) == 0x00000010);
117static_assert(static_cast<u32>(KMemoryState::NonSecureIpc) == 0x005C3811); 129static_assert(static_cast<u32>(KMemoryState::NonSecureIpc) == 0x045C3811);
118static_assert(static_cast<u32>(KMemoryState::NonDeviceIpc) == 0x004C2812); 130static_assert(static_cast<u32>(KMemoryState::NonDeviceIpc) == 0x044C2812);
119static_assert(static_cast<u32>(KMemoryState::Kernel) == 0x00002013); 131static_assert(static_cast<u32>(KMemoryState::Kernel) == 0x00002013);
120static_assert(static_cast<u32>(KMemoryState::GeneratedCode) == 0x00402214); 132static_assert(static_cast<u32>(KMemoryState::GeneratedCode) == 0x04402214);
121static_assert(static_cast<u32>(KMemoryState::CodeOut) == 0x00402015); 133static_assert(static_cast<u32>(KMemoryState::CodeOut) == 0x04402015);
122static_assert(static_cast<u32>(KMemoryState::Coverage) == 0x00002016); 134static_assert(static_cast<u32>(KMemoryState::Coverage) == 0x00002016);
135static_assert(static_cast<u32>(KMemoryState::Insecure) == 0x05583817);
123 136
124enum class KMemoryPermission : u8 { 137enum class KMemoryPermission : u8 {
125 None = 0, 138 None = 0,
126 All = static_cast<u8>(~None), 139 All = static_cast<u8>(~None),
127 140
128 Read = 1 << 0,
129 Write = 1 << 1,
130 Execute = 1 << 2,
131
132 ReadAndWrite = Read | Write,
133 ReadAndExecute = Read | Execute,
134
135 UserMask = static_cast<u8>(Svc::MemoryPermission::Read | Svc::MemoryPermission::Write |
136 Svc::MemoryPermission::Execute),
137
138 KernelShift = 3, 141 KernelShift = 3,
139 142
140 KernelRead = Read << KernelShift, 143 KernelRead = static_cast<u8>(Svc::MemoryPermission::Read) << KernelShift,
141 KernelWrite = Write << KernelShift, 144 KernelWrite = static_cast<u8>(Svc::MemoryPermission::Write) << KernelShift,
142 KernelExecute = Execute << KernelShift, 145 KernelExecute = static_cast<u8>(Svc::MemoryPermission::Execute) << KernelShift,
143 146
144 NotMapped = (1 << (2 * KernelShift)), 147 NotMapped = (1 << (2 * KernelShift)),
145 148
146 KernelReadWrite = KernelRead | KernelWrite, 149 KernelReadWrite = KernelRead | KernelWrite,
147 KernelReadExecute = KernelRead | KernelExecute, 150 KernelReadExecute = KernelRead | KernelExecute,
148 151
149 UserRead = Read | KernelRead, 152 UserRead = static_cast<u8>(Svc::MemoryPermission::Read) | KernelRead,
150 UserWrite = Write | KernelWrite, 153 UserWrite = static_cast<u8>(Svc::MemoryPermission::Write) | KernelWrite,
151 UserExecute = Execute, 154 UserExecute = static_cast<u8>(Svc::MemoryPermission::Execute),
152 155
153 UserReadWrite = UserRead | UserWrite, 156 UserReadWrite = UserRead | UserWrite,
154 UserReadExecute = UserRead | UserExecute, 157 UserReadExecute = UserRead | UserExecute,
155 158
156 IpcLockChangeMask = NotMapped | UserReadWrite 159 UserMask = static_cast<u8>(Svc::MemoryPermission::Read | Svc::MemoryPermission::Write |
160 Svc::MemoryPermission::Execute),
161
162 IpcLockChangeMask = NotMapped | UserReadWrite,
157}; 163};
158DECLARE_ENUM_FLAG_OPERATORS(KMemoryPermission); 164DECLARE_ENUM_FLAG_OPERATORS(KMemoryPermission);
159 165
@@ -210,13 +216,15 @@ struct KMemoryInfo {
210 216
211 constexpr Svc::MemoryInfo GetSvcMemoryInfo() const { 217 constexpr Svc::MemoryInfo GetSvcMemoryInfo() const {
212 return { 218 return {
213 .addr = m_address, 219 .base_address = m_address,
214 .size = m_size, 220 .size = m_size,
215 .state = static_cast<Svc::MemoryState>(m_state & KMemoryState::Mask), 221 .state = static_cast<Svc::MemoryState>(m_state & KMemoryState::Mask),
216 .attr = static_cast<Svc::MemoryAttribute>(m_attribute & KMemoryAttribute::UserMask), 222 .attribute =
217 .perm = static_cast<Svc::MemoryPermission>(m_permission & KMemoryPermission::UserMask), 223 static_cast<Svc::MemoryAttribute>(m_attribute & KMemoryAttribute::UserMask),
218 .ipc_refcount = m_ipc_lock_count, 224 .permission =
219 .device_refcount = m_device_use_count, 225 static_cast<Svc::MemoryPermission>(m_permission & KMemoryPermission::UserMask),
226 .ipc_count = m_ipc_lock_count,
227 .device_count = m_device_use_count,
220 .padding = {}, 228 .padding = {},
221 }; 229 };
222 } 230 }
@@ -468,6 +476,7 @@ public:
468 476
469 constexpr void UpdateDeviceDisableMergeStateForShareLeft( 477 constexpr void UpdateDeviceDisableMergeStateForShareLeft(
470 [[maybe_unused]] KMemoryPermission new_perm, bool left, [[maybe_unused]] bool right) { 478 [[maybe_unused]] KMemoryPermission new_perm, bool left, [[maybe_unused]] bool right) {
479 // New permission/right aren't used.
471 if (left) { 480 if (left) {
472 m_disable_merge_attribute = static_cast<KMemoryBlockDisableMergeAttribute>( 481 m_disable_merge_attribute = static_cast<KMemoryBlockDisableMergeAttribute>(
473 m_disable_merge_attribute | KMemoryBlockDisableMergeAttribute::DeviceLeft); 482 m_disable_merge_attribute | KMemoryBlockDisableMergeAttribute::DeviceLeft);
@@ -478,6 +487,7 @@ public:
478 487
479 constexpr void UpdateDeviceDisableMergeStateForShareRight( 488 constexpr void UpdateDeviceDisableMergeStateForShareRight(
480 [[maybe_unused]] KMemoryPermission new_perm, [[maybe_unused]] bool left, bool right) { 489 [[maybe_unused]] KMemoryPermission new_perm, [[maybe_unused]] bool left, bool right) {
490 // New permission/left aren't used.
481 if (right) { 491 if (right) {
482 m_disable_merge_attribute = static_cast<KMemoryBlockDisableMergeAttribute>( 492 m_disable_merge_attribute = static_cast<KMemoryBlockDisableMergeAttribute>(
483 m_disable_merge_attribute | KMemoryBlockDisableMergeAttribute::DeviceRight); 493 m_disable_merge_attribute | KMemoryBlockDisableMergeAttribute::DeviceRight);
@@ -494,6 +504,8 @@ public:
494 504
495 constexpr void ShareToDevice([[maybe_unused]] KMemoryPermission new_perm, bool left, 505 constexpr void ShareToDevice([[maybe_unused]] KMemoryPermission new_perm, bool left,
496 bool right) { 506 bool right) {
507 // New permission isn't used.
508
497 // We must either be shared or have a zero lock count. 509 // We must either be shared or have a zero lock count.
498 ASSERT((m_attribute & KMemoryAttribute::DeviceShared) == KMemoryAttribute::DeviceShared || 510 ASSERT((m_attribute & KMemoryAttribute::DeviceShared) == KMemoryAttribute::DeviceShared ||
499 m_device_use_count == 0); 511 m_device_use_count == 0);
@@ -509,6 +521,7 @@ public:
509 521
510 constexpr void UpdateDeviceDisableMergeStateForUnshareLeft( 522 constexpr void UpdateDeviceDisableMergeStateForUnshareLeft(
511 [[maybe_unused]] KMemoryPermission new_perm, bool left, [[maybe_unused]] bool right) { 523 [[maybe_unused]] KMemoryPermission new_perm, bool left, [[maybe_unused]] bool right) {
524 // New permission/right aren't used.
512 525
513 if (left) { 526 if (left) {
514 if (!m_device_disable_merge_left_count) { 527 if (!m_device_disable_merge_left_count) {
@@ -528,6 +541,8 @@ public:
528 541
529 constexpr void UpdateDeviceDisableMergeStateForUnshareRight( 542 constexpr void UpdateDeviceDisableMergeStateForUnshareRight(
530 [[maybe_unused]] KMemoryPermission new_perm, [[maybe_unused]] bool left, bool right) { 543 [[maybe_unused]] KMemoryPermission new_perm, [[maybe_unused]] bool left, bool right) {
544 // New permission/left aren't used.
545
531 if (right) { 546 if (right) {
532 const u16 old_device_disable_merge_right_count = m_device_disable_merge_right_count--; 547 const u16 old_device_disable_merge_right_count = m_device_disable_merge_right_count--;
533 ASSERT(old_device_disable_merge_right_count > 0); 548 ASSERT(old_device_disable_merge_right_count > 0);
@@ -546,6 +561,8 @@ public:
546 561
547 constexpr void UnshareToDevice([[maybe_unused]] KMemoryPermission new_perm, bool left, 562 constexpr void UnshareToDevice([[maybe_unused]] KMemoryPermission new_perm, bool left,
548 bool right) { 563 bool right) {
564 // New permission isn't used.
565
549 // We must be shared. 566 // We must be shared.
550 ASSERT((m_attribute & KMemoryAttribute::DeviceShared) == KMemoryAttribute::DeviceShared); 567 ASSERT((m_attribute & KMemoryAttribute::DeviceShared) == KMemoryAttribute::DeviceShared);
551 568
@@ -563,6 +580,7 @@ public:
563 580
564 constexpr void UnshareToDeviceRight([[maybe_unused]] KMemoryPermission new_perm, bool left, 581 constexpr void UnshareToDeviceRight([[maybe_unused]] KMemoryPermission new_perm, bool left,
565 bool right) { 582 bool right) {
583 // New permission isn't used.
566 584
567 // We must be shared. 585 // We must be shared.
568 ASSERT((m_attribute & KMemoryAttribute::DeviceShared) == KMemoryAttribute::DeviceShared); 586 ASSERT((m_attribute & KMemoryAttribute::DeviceShared) == KMemoryAttribute::DeviceShared);
@@ -613,6 +631,8 @@ public:
613 631
614 constexpr void UnlockForIpc([[maybe_unused]] KMemoryPermission new_perm, bool left, 632 constexpr void UnlockForIpc([[maybe_unused]] KMemoryPermission new_perm, bool left,
615 [[maybe_unused]] bool right) { 633 [[maybe_unused]] bool right) {
634 // New permission isn't used.
635
616 // We must be locked. 636 // We must be locked.
617 ASSERT((m_attribute & KMemoryAttribute::IpcLocked) == KMemoryAttribute::IpcLocked); 637 ASSERT((m_attribute & KMemoryAttribute::IpcLocked) == KMemoryAttribute::IpcLocked);
618 638
diff --git a/src/core/hle/kernel/k_memory_layout.cpp b/src/core/hle/kernel/k_memory_layout.cpp
index 55dc296d0..72c3ee4b7 100644
--- a/src/core/hle/kernel/k_memory_layout.cpp
+++ b/src/core/hle/kernel/k_memory_layout.cpp
@@ -153,13 +153,9 @@ void KMemoryLayout::InitializeLinearMemoryRegionTrees(PAddr aligned_linear_phys_
153 } 153 }
154} 154}
155 155
156size_t KMemoryLayout::GetResourceRegionSizeForInit() { 156size_t KMemoryLayout::GetResourceRegionSizeForInit(bool use_extra_resource) {
157 // Calculate resource region size based on whether we allow extra threads. 157 return KernelResourceSize + KSystemControl::SecureAppletMemorySize +
158 const bool use_extra_resources = KSystemControl::Init::ShouldIncreaseThreadResourceLimit(); 158 (use_extra_resource ? KernelSlabHeapAdditionalSize + KernelPageBufferAdditionalSize : 0);
159 size_t resource_region_size =
160 KernelResourceSize + (use_extra_resources ? KernelSlabHeapAdditionalSize : 0);
161
162 return resource_region_size;
163} 159}
164 160
165} // namespace Kernel 161} // namespace Kernel
diff --git a/src/core/hle/kernel/k_memory_layout.h b/src/core/hle/kernel/k_memory_layout.h
index 884fc623a..fd6e1d3e6 100644
--- a/src/core/hle/kernel/k_memory_layout.h
+++ b/src/core/hle/kernel/k_memory_layout.h
@@ -60,10 +60,12 @@ constexpr std::size_t KernelSlabHeapGapsSizeMax = 2_MiB - 64_KiB;
60constexpr std::size_t KernelSlabHeapSize = KernelSlabHeapDataSize + KernelSlabHeapGapsSizeMax; 60constexpr std::size_t KernelSlabHeapSize = KernelSlabHeapDataSize + KernelSlabHeapGapsSizeMax;
61 61
62// NOTE: This is calculated from KThread slab counts, assuming KThread size <= 0x860. 62// NOTE: This is calculated from KThread slab counts, assuming KThread size <= 0x860.
63constexpr std::size_t KernelSlabHeapAdditionalSize = 0x68000; 63constexpr size_t KernelPageBufferHeapSize = 0x3E0000;
64constexpr size_t KernelSlabHeapAdditionalSize = 0x148000;
65constexpr size_t KernelPageBufferAdditionalSize = 0x33C000;
64 66
65constexpr std::size_t KernelResourceSize = 67constexpr std::size_t KernelResourceSize = KernelPageTableHeapSize + KernelInitialPageHeapSize +
66 KernelPageTableHeapSize + KernelInitialPageHeapSize + KernelSlabHeapSize; 68 KernelSlabHeapSize + KernelPageBufferHeapSize;
67 69
68constexpr bool IsKernelAddressKey(VAddr key) { 70constexpr bool IsKernelAddressKey(VAddr key) {
69 return KernelVirtualAddressSpaceBase <= key && key <= KernelVirtualAddressSpaceLast; 71 return KernelVirtualAddressSpaceBase <= key && key <= KernelVirtualAddressSpaceLast;
@@ -168,6 +170,11 @@ public:
168 KMemoryRegionType_VirtualDramKernelTraceBuffer)); 170 KMemoryRegionType_VirtualDramKernelTraceBuffer));
169 } 171 }
170 172
173 const KMemoryRegion& GetSecureAppletMemoryRegion() {
174 return Dereference(GetVirtualMemoryRegionTree().FindByType(
175 KMemoryRegionType_VirtualDramKernelSecureAppletMemory));
176 }
177
171 const KMemoryRegion& GetVirtualLinearRegion(VAddr address) const { 178 const KMemoryRegion& GetVirtualLinearRegion(VAddr address) const {
172 return Dereference(FindVirtualLinear(address)); 179 return Dereference(FindVirtualLinear(address));
173 } 180 }
@@ -229,7 +236,7 @@ public:
229 236
230 void InitializeLinearMemoryRegionTrees(PAddr aligned_linear_phys_start, 237 void InitializeLinearMemoryRegionTrees(PAddr aligned_linear_phys_start,
231 VAddr linear_virtual_start); 238 VAddr linear_virtual_start);
232 static size_t GetResourceRegionSizeForInit(); 239 static size_t GetResourceRegionSizeForInit(bool use_extra_resource);
233 240
234 auto GetKernelRegionExtents() const { 241 auto GetKernelRegionExtents() const {
235 return GetVirtualMemoryRegionTree().GetDerivedRegionExtents(KMemoryRegionType_Kernel); 242 return GetVirtualMemoryRegionTree().GetDerivedRegionExtents(KMemoryRegionType_Kernel);
@@ -279,6 +286,10 @@ public:
279 return GetPhysicalMemoryRegionTree().GetDerivedRegionExtents( 286 return GetPhysicalMemoryRegionTree().GetDerivedRegionExtents(
280 KMemoryRegionType_DramKernelSlab); 287 KMemoryRegionType_DramKernelSlab);
281 } 288 }
289 auto GetKernelSecureAppletMemoryRegionPhysicalExtents() {
290 return GetPhysicalMemoryRegionTree().GetDerivedRegionExtents(
291 KMemoryRegionType_DramKernelSecureAppletMemory);
292 }
282 auto GetKernelPageTableHeapRegionPhysicalExtents() const { 293 auto GetKernelPageTableHeapRegionPhysicalExtents() const {
283 return GetPhysicalMemoryRegionTree().GetDerivedRegionExtents( 294 return GetPhysicalMemoryRegionTree().GetDerivedRegionExtents(
284 KMemoryRegionType_DramKernelPtHeap); 295 KMemoryRegionType_DramKernelPtHeap);
diff --git a/src/core/hle/kernel/k_memory_manager.cpp b/src/core/hle/kernel/k_memory_manager.cpp
index 646711505..c4bf306e8 100644
--- a/src/core/hle/kernel/k_memory_manager.cpp
+++ b/src/core/hle/kernel/k_memory_manager.cpp
@@ -29,43 +29,44 @@ constexpr KMemoryManager::Pool GetPoolFromMemoryRegionType(u32 type) {
29 } else if ((type | KMemoryRegionType_DramSystemNonSecurePool) == type) { 29 } else if ((type | KMemoryRegionType_DramSystemNonSecurePool) == type) {
30 return KMemoryManager::Pool::SystemNonSecure; 30 return KMemoryManager::Pool::SystemNonSecure;
31 } else { 31 } else {
32 ASSERT_MSG(false, "InvalidMemoryRegionType for conversion to Pool"); 32 UNREACHABLE_MSG("InvalidMemoryRegionType for conversion to Pool");
33 return {};
34 } 33 }
35} 34}
36 35
37} // namespace 36} // namespace
38 37
39KMemoryManager::KMemoryManager(Core::System& system_) 38KMemoryManager::KMemoryManager(Core::System& system)
40 : system{system_}, pool_locks{ 39 : m_system{system}, m_memory_layout{system.Kernel().MemoryLayout()},
41 KLightLock{system_.Kernel()}, 40 m_pool_locks{
42 KLightLock{system_.Kernel()}, 41 KLightLock{system.Kernel()},
43 KLightLock{system_.Kernel()}, 42 KLightLock{system.Kernel()},
44 KLightLock{system_.Kernel()}, 43 KLightLock{system.Kernel()},
45 } {} 44 KLightLock{system.Kernel()},
45 } {}
46 46
47void KMemoryManager::Initialize(VAddr management_region, size_t management_region_size) { 47void KMemoryManager::Initialize(VAddr management_region, size_t management_region_size) {
48 48
49 // Clear the management region to zero. 49 // Clear the management region to zero.
50 const VAddr management_region_end = management_region + management_region_size; 50 const VAddr management_region_end = management_region + management_region_size;
51 // std::memset(GetVoidPointer(management_region), 0, management_region_size);
51 52
52 // Reset our manager count. 53 // Reset our manager count.
53 num_managers = 0; 54 m_num_managers = 0;
54 55
55 // Traverse the virtual memory layout tree, initializing each manager as appropriate. 56 // Traverse the virtual memory layout tree, initializing each manager as appropriate.
56 while (num_managers != MaxManagerCount) { 57 while (m_num_managers != MaxManagerCount) {
57 // Locate the region that should initialize the current manager. 58 // Locate the region that should initialize the current manager.
58 PAddr region_address = 0; 59 PAddr region_address = 0;
59 size_t region_size = 0; 60 size_t region_size = 0;
60 Pool region_pool = Pool::Count; 61 Pool region_pool = Pool::Count;
61 for (const auto& it : system.Kernel().MemoryLayout().GetPhysicalMemoryRegionTree()) { 62 for (const auto& it : m_system.Kernel().MemoryLayout().GetPhysicalMemoryRegionTree()) {
62 // We only care about regions that we need to create managers for. 63 // We only care about regions that we need to create managers for.
63 if (!it.IsDerivedFrom(KMemoryRegionType_DramUserPool)) { 64 if (!it.IsDerivedFrom(KMemoryRegionType_DramUserPool)) {
64 continue; 65 continue;
65 } 66 }
66 67
67 // We want to initialize the managers in order. 68 // We want to initialize the managers in order.
68 if (it.GetAttributes() != num_managers) { 69 if (it.GetAttributes() != m_num_managers) {
69 continue; 70 continue;
70 } 71 }
71 72
@@ -97,8 +98,8 @@ void KMemoryManager::Initialize(VAddr management_region, size_t management_regio
97 } 98 }
98 99
99 // Initialize a new manager for the region. 100 // Initialize a new manager for the region.
100 Impl* manager = std::addressof(managers[num_managers++]); 101 Impl* manager = std::addressof(m_managers[m_num_managers++]);
101 ASSERT(num_managers <= managers.size()); 102 ASSERT(m_num_managers <= m_managers.size());
102 103
103 const size_t cur_size = manager->Initialize(region_address, region_size, management_region, 104 const size_t cur_size = manager->Initialize(region_address, region_size, management_region,
104 management_region_end, region_pool); 105 management_region_end, region_pool);
@@ -107,13 +108,13 @@ void KMemoryManager::Initialize(VAddr management_region, size_t management_regio
107 108
108 // Insert the manager into the pool list. 109 // Insert the manager into the pool list.
109 const auto region_pool_index = static_cast<u32>(region_pool); 110 const auto region_pool_index = static_cast<u32>(region_pool);
110 if (pool_managers_tail[region_pool_index] == nullptr) { 111 if (m_pool_managers_tail[region_pool_index] == nullptr) {
111 pool_managers_head[region_pool_index] = manager; 112 m_pool_managers_head[region_pool_index] = manager;
112 } else { 113 } else {
113 pool_managers_tail[region_pool_index]->SetNext(manager); 114 m_pool_managers_tail[region_pool_index]->SetNext(manager);
114 manager->SetPrev(pool_managers_tail[region_pool_index]); 115 manager->SetPrev(m_pool_managers_tail[region_pool_index]);
115 } 116 }
116 pool_managers_tail[region_pool_index] = manager; 117 m_pool_managers_tail[region_pool_index] = manager;
117 } 118 }
118 119
119 // Free each region to its corresponding heap. 120 // Free each region to its corresponding heap.
@@ -121,11 +122,10 @@ void KMemoryManager::Initialize(VAddr management_region, size_t management_regio
121 const PAddr ini_start = GetInitialProcessBinaryPhysicalAddress(); 122 const PAddr ini_start = GetInitialProcessBinaryPhysicalAddress();
122 const PAddr ini_end = ini_start + InitialProcessBinarySizeMax; 123 const PAddr ini_end = ini_start + InitialProcessBinarySizeMax;
123 const PAddr ini_last = ini_end - 1; 124 const PAddr ini_last = ini_end - 1;
124 for (const auto& it : system.Kernel().MemoryLayout().GetPhysicalMemoryRegionTree()) { 125 for (const auto& it : m_system.Kernel().MemoryLayout().GetPhysicalMemoryRegionTree()) {
125 if (it.IsDerivedFrom(KMemoryRegionType_DramUserPool)) { 126 if (it.IsDerivedFrom(KMemoryRegionType_DramUserPool)) {
126 // Get the manager for the region. 127 // Get the manager for the region.
127 auto index = it.GetAttributes(); 128 auto& manager = m_managers[it.GetAttributes()];
128 auto& manager = managers[index];
129 129
130 const PAddr cur_start = it.GetAddress(); 130 const PAddr cur_start = it.GetAddress();
131 const PAddr cur_last = it.GetLastAddress(); 131 const PAddr cur_last = it.GetLastAddress();
@@ -162,11 +162,19 @@ void KMemoryManager::Initialize(VAddr management_region, size_t management_regio
162 } 162 }
163 163
164 // Update the used size for all managers. 164 // Update the used size for all managers.
165 for (size_t i = 0; i < num_managers; ++i) { 165 for (size_t i = 0; i < m_num_managers; ++i) {
166 managers[i].SetInitialUsedHeapSize(reserved_sizes[i]); 166 m_managers[i].SetInitialUsedHeapSize(reserved_sizes[i]);
167 } 167 }
168} 168}
169 169
170Result KMemoryManager::InitializeOptimizedMemory(u64 process_id, Pool pool) {
171 UNREACHABLE();
172}
173
174void KMemoryManager::FinalizeOptimizedMemory(u64 process_id, Pool pool) {
175 UNREACHABLE();
176}
177
170PAddr KMemoryManager::AllocateAndOpenContinuous(size_t num_pages, size_t align_pages, u32 option) { 178PAddr KMemoryManager::AllocateAndOpenContinuous(size_t num_pages, size_t align_pages, u32 option) {
171 // Early return if we're allocating no pages. 179 // Early return if we're allocating no pages.
172 if (num_pages == 0) { 180 if (num_pages == 0) {
@@ -175,7 +183,7 @@ PAddr KMemoryManager::AllocateAndOpenContinuous(size_t num_pages, size_t align_p
175 183
176 // Lock the pool that we're allocating from. 184 // Lock the pool that we're allocating from.
177 const auto [pool, dir] = DecodeOption(option); 185 const auto [pool, dir] = DecodeOption(option);
178 KScopedLightLock lk(pool_locks[static_cast<std::size_t>(pool)]); 186 KScopedLightLock lk(m_pool_locks[static_cast<std::size_t>(pool)]);
179 187
180 // Choose a heap based on our page size request. 188 // Choose a heap based on our page size request.
181 const s32 heap_index = KPageHeap::GetAlignedBlockIndex(num_pages, align_pages); 189 const s32 heap_index = KPageHeap::GetAlignedBlockIndex(num_pages, align_pages);
@@ -185,7 +193,7 @@ PAddr KMemoryManager::AllocateAndOpenContinuous(size_t num_pages, size_t align_p
185 PAddr allocated_block = 0; 193 PAddr allocated_block = 0;
186 for (chosen_manager = this->GetFirstManager(pool, dir); chosen_manager != nullptr; 194 for (chosen_manager = this->GetFirstManager(pool, dir); chosen_manager != nullptr;
187 chosen_manager = this->GetNextManager(chosen_manager, dir)) { 195 chosen_manager = this->GetNextManager(chosen_manager, dir)) {
188 allocated_block = chosen_manager->AllocateBlock(heap_index, true); 196 allocated_block = chosen_manager->AllocateAligned(heap_index, num_pages, align_pages);
189 if (allocated_block != 0) { 197 if (allocated_block != 0) {
190 break; 198 break;
191 } 199 }
@@ -196,10 +204,9 @@ PAddr KMemoryManager::AllocateAndOpenContinuous(size_t num_pages, size_t align_p
196 return 0; 204 return 0;
197 } 205 }
198 206
199 // If we allocated more than we need, free some. 207 // Maintain the optimized memory bitmap, if we should.
200 const size_t allocated_pages = KPageHeap::GetBlockNumPages(heap_index); 208 if (m_has_optimized_process[static_cast<size_t>(pool)]) {
201 if (allocated_pages > num_pages) { 209 UNIMPLEMENTED();
202 chosen_manager->Free(allocated_block + num_pages * PageSize, allocated_pages - num_pages);
203 } 210 }
204 211
205 // Open the first reference to the pages. 212 // Open the first reference to the pages.
@@ -209,20 +216,21 @@ PAddr KMemoryManager::AllocateAndOpenContinuous(size_t num_pages, size_t align_p
209} 216}
210 217
211Result KMemoryManager::AllocatePageGroupImpl(KPageGroup* out, size_t num_pages, Pool pool, 218Result KMemoryManager::AllocatePageGroupImpl(KPageGroup* out, size_t num_pages, Pool pool,
212 Direction dir, bool random) { 219 Direction dir, bool unoptimized, bool random) {
213 // Choose a heap based on our page size request. 220 // Choose a heap based on our page size request.
214 const s32 heap_index = KPageHeap::GetBlockIndex(num_pages); 221 const s32 heap_index = KPageHeap::GetBlockIndex(num_pages);
215 R_UNLESS(0 <= heap_index, ResultOutOfMemory); 222 R_UNLESS(0 <= heap_index, ResultOutOfMemory);
216 223
217 // Ensure that we don't leave anything un-freed. 224 // Ensure that we don't leave anything un-freed.
218 auto group_guard = SCOPE_GUARD({ 225 ON_RESULT_FAILURE {
219 for (const auto& it : out->Nodes()) { 226 for (const auto& it : out->Nodes()) {
220 auto& manager = this->GetManager(system.Kernel().MemoryLayout(), it.GetAddress()); 227 auto& manager = this->GetManager(it.GetAddress());
221 const size_t num_pages_to_free = 228 const size_t node_num_pages =
222 std::min(it.GetNumPages(), (manager.GetEndAddress() - it.GetAddress()) / PageSize); 229 std::min(it.GetNumPages(), (manager.GetEndAddress() - it.GetAddress()) / PageSize);
223 manager.Free(it.GetAddress(), num_pages_to_free); 230 manager.Free(it.GetAddress(), node_num_pages);
224 } 231 }
225 }); 232 out->Finalize();
233 };
226 234
227 // Keep allocating until we've allocated all our pages. 235 // Keep allocating until we've allocated all our pages.
228 for (s32 index = heap_index; index >= 0 && num_pages > 0; index--) { 236 for (s32 index = heap_index; index >= 0 && num_pages > 0; index--) {
@@ -236,12 +244,17 @@ Result KMemoryManager::AllocatePageGroupImpl(KPageGroup* out, size_t num_pages,
236 break; 244 break;
237 } 245 }
238 246
239 // Safely add it to our group. 247 // Ensure we don't leak the block if we fail.
240 { 248 ON_RESULT_FAILURE_2 {
241 auto block_guard = 249 cur_manager->Free(allocated_block, pages_per_alloc);
242 SCOPE_GUARD({ cur_manager->Free(allocated_block, pages_per_alloc); }); 250 };
243 R_TRY(out->AddBlock(allocated_block, pages_per_alloc)); 251
244 block_guard.Cancel(); 252 // Add the block to our group.
253 R_TRY(out->AddBlock(allocated_block, pages_per_alloc));
254
255 // Maintain the optimized memory bitmap, if we should.
256 if (unoptimized) {
257 UNIMPLEMENTED();
245 } 258 }
246 259
247 num_pages -= pages_per_alloc; 260 num_pages -= pages_per_alloc;
@@ -253,8 +266,7 @@ Result KMemoryManager::AllocatePageGroupImpl(KPageGroup* out, size_t num_pages,
253 R_UNLESS(num_pages == 0, ResultOutOfMemory); 266 R_UNLESS(num_pages == 0, ResultOutOfMemory);
254 267
255 // We succeeded! 268 // We succeeded!
256 group_guard.Cancel(); 269 R_SUCCEED();
257 return ResultSuccess;
258} 270}
259 271
260Result KMemoryManager::AllocateAndOpen(KPageGroup* out, size_t num_pages, u32 option) { 272Result KMemoryManager::AllocateAndOpen(KPageGroup* out, size_t num_pages, u32 option) {
@@ -266,10 +278,11 @@ Result KMemoryManager::AllocateAndOpen(KPageGroup* out, size_t num_pages, u32 op
266 278
267 // Lock the pool that we're allocating from. 279 // Lock the pool that we're allocating from.
268 const auto [pool, dir] = DecodeOption(option); 280 const auto [pool, dir] = DecodeOption(option);
269 KScopedLightLock lk(pool_locks[static_cast<size_t>(pool)]); 281 KScopedLightLock lk(m_pool_locks[static_cast<size_t>(pool)]);
270 282
271 // Allocate the page group. 283 // Allocate the page group.
272 R_TRY(this->AllocatePageGroupImpl(out, num_pages, pool, dir, false)); 284 R_TRY(this->AllocatePageGroupImpl(out, num_pages, pool, dir,
285 m_has_optimized_process[static_cast<size_t>(pool)], true));
273 286
274 // Open the first reference to the pages. 287 // Open the first reference to the pages.
275 for (const auto& block : out->Nodes()) { 288 for (const auto& block : out->Nodes()) {
@@ -277,7 +290,7 @@ Result KMemoryManager::AllocateAndOpen(KPageGroup* out, size_t num_pages, u32 op
277 size_t remaining_pages = block.GetNumPages(); 290 size_t remaining_pages = block.GetNumPages();
278 while (remaining_pages > 0) { 291 while (remaining_pages > 0) {
279 // Get the manager for the current address. 292 // Get the manager for the current address.
280 auto& manager = this->GetManager(system.Kernel().MemoryLayout(), cur_address); 293 auto& manager = this->GetManager(cur_address);
281 294
282 // Process part or all of the block. 295 // Process part or all of the block.
283 const size_t cur_pages = 296 const size_t cur_pages =
@@ -290,11 +303,11 @@ Result KMemoryManager::AllocateAndOpen(KPageGroup* out, size_t num_pages, u32 op
290 } 303 }
291 } 304 }
292 305
293 return ResultSuccess; 306 R_SUCCEED();
294} 307}
295 308
296Result KMemoryManager::AllocateAndOpenForProcess(KPageGroup* out, size_t num_pages, u32 option, 309Result KMemoryManager::AllocateForProcess(KPageGroup* out, size_t num_pages, u32 option,
297 u64 process_id, u8 fill_pattern) { 310 u64 process_id, u8 fill_pattern) {
298 ASSERT(out != nullptr); 311 ASSERT(out != nullptr);
299 ASSERT(out->GetNumPages() == 0); 312 ASSERT(out->GetNumPages() == 0);
300 313
@@ -302,83 +315,89 @@ Result KMemoryManager::AllocateAndOpenForProcess(KPageGroup* out, size_t num_pag
302 const auto [pool, dir] = DecodeOption(option); 315 const auto [pool, dir] = DecodeOption(option);
303 316
304 // Allocate the memory. 317 // Allocate the memory.
318 bool optimized;
305 { 319 {
306 // Lock the pool that we're allocating from. 320 // Lock the pool that we're allocating from.
307 KScopedLightLock lk(pool_locks[static_cast<size_t>(pool)]); 321 KScopedLightLock lk(m_pool_locks[static_cast<size_t>(pool)]);
322
323 // Check if we have an optimized process.
324 const bool has_optimized = m_has_optimized_process[static_cast<size_t>(pool)];
325 const bool is_optimized = m_optimized_process_ids[static_cast<size_t>(pool)] == process_id;
308 326
309 // Allocate the page group. 327 // Allocate the page group.
310 R_TRY(this->AllocatePageGroupImpl(out, num_pages, pool, dir, false)); 328 R_TRY(this->AllocatePageGroupImpl(out, num_pages, pool, dir, has_optimized && !is_optimized,
329 false));
311 330
312 // Open the first reference to the pages. 331 // Set whether we should optimize.
313 for (const auto& block : out->Nodes()) { 332 optimized = has_optimized && is_optimized;
314 PAddr cur_address = block.GetAddress();
315 size_t remaining_pages = block.GetNumPages();
316 while (remaining_pages > 0) {
317 // Get the manager for the current address.
318 auto& manager = this->GetManager(system.Kernel().MemoryLayout(), cur_address);
319
320 // Process part or all of the block.
321 const size_t cur_pages =
322 std::min(remaining_pages, manager.GetPageOffsetToEnd(cur_address));
323 manager.OpenFirst(cur_address, cur_pages);
324
325 // Advance.
326 cur_address += cur_pages * PageSize;
327 remaining_pages -= cur_pages;
328 }
329 }
330 } 333 }
331 334
332 // Set all the allocated memory. 335 // Perform optimized memory tracking, if we should.
333 for (const auto& block : out->Nodes()) { 336 if (optimized) {
334 std::memset(system.DeviceMemory().GetPointer<void>(block.GetAddress()), fill_pattern, 337 // Iterate over the allocated blocks.
335 block.GetSize()); 338 for (const auto& block : out->Nodes()) {
336 } 339 // Get the block extents.
340 const PAddr block_address = block.GetAddress();
341 const size_t block_pages = block.GetNumPages();
337 342
338 return ResultSuccess; 343 // If it has no pages, we don't need to do anything.
339} 344 if (block_pages == 0) {
345 continue;
346 }
340 347
341void KMemoryManager::Open(PAddr address, size_t num_pages) { 348 // Fill all the pages that we need to fill.
342 // Repeatedly open references until we've done so for all pages. 349 bool any_new = false;
343 while (num_pages) { 350 {
344 auto& manager = this->GetManager(system.Kernel().MemoryLayout(), address); 351 PAddr cur_address = block_address;
345 const size_t cur_pages = std::min(num_pages, manager.GetPageOffsetToEnd(address)); 352 size_t remaining_pages = block_pages;
353 while (remaining_pages > 0) {
354 // Get the manager for the current address.
355 auto& manager = this->GetManager(cur_address);
356
357 // Process part or all of the block.
358 const size_t cur_pages =
359 std::min(remaining_pages, manager.GetPageOffsetToEnd(cur_address));
360 any_new =
361 manager.ProcessOptimizedAllocation(cur_address, cur_pages, fill_pattern);
362
363 // Advance.
364 cur_address += cur_pages * PageSize;
365 remaining_pages -= cur_pages;
366 }
367 }
346 368
347 { 369 // If there are new pages, update tracking for the allocation.
348 KScopedLightLock lk(pool_locks[static_cast<size_t>(manager.GetPool())]); 370 if (any_new) {
349 manager.Open(address, cur_pages); 371 // Update tracking for the allocation.
372 PAddr cur_address = block_address;
373 size_t remaining_pages = block_pages;
374 while (remaining_pages > 0) {
375 // Get the manager for the current address.
376 auto& manager = this->GetManager(cur_address);
377
378 // Lock the pool for the manager.
379 KScopedLightLock lk(m_pool_locks[static_cast<size_t>(manager.GetPool())]);
380
381 // Track some or all of the current pages.
382 const size_t cur_pages =
383 std::min(remaining_pages, manager.GetPageOffsetToEnd(cur_address));
384 manager.TrackOptimizedAllocation(cur_address, cur_pages);
385
386 // Advance.
387 cur_address += cur_pages * PageSize;
388 remaining_pages -= cur_pages;
389 }
390 }
350 } 391 }
351 392 } else {
352 num_pages -= cur_pages; 393 // Set all the allocated memory.
353 address += cur_pages * PageSize; 394 for (const auto& block : out->Nodes()) {
354 } 395 std::memset(m_system.DeviceMemory().GetPointer<void>(block.GetAddress()), fill_pattern,
355} 396 block.GetSize());
356
357void KMemoryManager::Close(PAddr address, size_t num_pages) {
358 // Repeatedly close references until we've done so for all pages.
359 while (num_pages) {
360 auto& manager = this->GetManager(system.Kernel().MemoryLayout(), address);
361 const size_t cur_pages = std::min(num_pages, manager.GetPageOffsetToEnd(address));
362
363 {
364 KScopedLightLock lk(pool_locks[static_cast<size_t>(manager.GetPool())]);
365 manager.Close(address, cur_pages);
366 } 397 }
367
368 num_pages -= cur_pages;
369 address += cur_pages * PageSize;
370 } 398 }
371}
372 399
373void KMemoryManager::Close(const KPageGroup& pg) { 400 R_SUCCEED();
374 for (const auto& node : pg.Nodes()) {
375 Close(node.GetAddress(), node.GetNumPages());
376 }
377}
378void KMemoryManager::Open(const KPageGroup& pg) {
379 for (const auto& node : pg.Nodes()) {
380 Open(node.GetAddress(), node.GetNumPages());
381 }
382} 401}
383 402
384size_t KMemoryManager::Impl::Initialize(PAddr address, size_t size, VAddr management, 403size_t KMemoryManager::Impl::Initialize(PAddr address, size_t size, VAddr management,
@@ -394,18 +413,31 @@ size_t KMemoryManager::Impl::Initialize(PAddr address, size_t size, VAddr manage
394 ASSERT(Common::IsAligned(total_management_size, PageSize)); 413 ASSERT(Common::IsAligned(total_management_size, PageSize));
395 414
396 // Setup region. 415 // Setup region.
397 pool = p; 416 m_pool = p;
398 management_region = management; 417 m_management_region = management;
399 page_reference_counts.resize( 418 m_page_reference_counts.resize(
400 Kernel::Board::Nintendo::Nx::KSystemControl::Init::GetIntendedMemorySize() / PageSize); 419 Kernel::Board::Nintendo::Nx::KSystemControl::Init::GetIntendedMemorySize() / PageSize);
401 ASSERT(Common::IsAligned(management_region, PageSize)); 420 ASSERT(Common::IsAligned(m_management_region, PageSize));
402 421
403 // Initialize the manager's KPageHeap. 422 // Initialize the manager's KPageHeap.
404 heap.Initialize(address, size, management + manager_size, page_heap_size); 423 m_heap.Initialize(address, size, management + manager_size, page_heap_size);
405 424
406 return total_management_size; 425 return total_management_size;
407} 426}
408 427
428void KMemoryManager::Impl::TrackUnoptimizedAllocation(PAddr block, size_t num_pages) {
429 UNREACHABLE();
430}
431
432void KMemoryManager::Impl::TrackOptimizedAllocation(PAddr block, size_t num_pages) {
433 UNREACHABLE();
434}
435
436bool KMemoryManager::Impl::ProcessOptimizedAllocation(PAddr block, size_t num_pages,
437 u8 fill_pattern) {
438 UNREACHABLE();
439}
440
409size_t KMemoryManager::Impl::CalculateManagementOverheadSize(size_t region_size) { 441size_t KMemoryManager::Impl::CalculateManagementOverheadSize(size_t region_size) {
410 const size_t ref_count_size = (region_size / PageSize) * sizeof(u16); 442 const size_t ref_count_size = (region_size / PageSize) * sizeof(u16);
411 const size_t optimize_map_size = 443 const size_t optimize_map_size =
diff --git a/src/core/hle/kernel/k_memory_manager.h b/src/core/hle/kernel/k_memory_manager.h
index dcb9b6348..401d4e644 100644
--- a/src/core/hle/kernel/k_memory_manager.h
+++ b/src/core/hle/kernel/k_memory_manager.h
@@ -21,11 +21,8 @@ namespace Kernel {
21 21
22class KPageGroup; 22class KPageGroup;
23 23
24class KMemoryManager final { 24class KMemoryManager {
25public: 25public:
26 YUZU_NON_COPYABLE(KMemoryManager);
27 YUZU_NON_MOVEABLE(KMemoryManager);
28
29 enum class Pool : u32 { 26 enum class Pool : u32 {
30 Application = 0, 27 Application = 0,
31 Applet = 1, 28 Applet = 1,
@@ -45,16 +42,85 @@ public:
45 enum class Direction : u32 { 42 enum class Direction : u32 {
46 FromFront = 0, 43 FromFront = 0,
47 FromBack = 1, 44 FromBack = 1,
48
49 Shift = 0, 45 Shift = 0,
50 Mask = (0xF << Shift), 46 Mask = (0xF << Shift),
51 }; 47 };
52 48
53 explicit KMemoryManager(Core::System& system_); 49 static constexpr size_t MaxManagerCount = 10;
50
51 explicit KMemoryManager(Core::System& system);
54 52
55 void Initialize(VAddr management_region, size_t management_region_size); 53 void Initialize(VAddr management_region, size_t management_region_size);
56 54
57 constexpr size_t GetSize(Pool pool) const { 55 Result InitializeOptimizedMemory(u64 process_id, Pool pool);
56 void FinalizeOptimizedMemory(u64 process_id, Pool pool);
57
58 PAddr AllocateAndOpenContinuous(size_t num_pages, size_t align_pages, u32 option);
59 Result AllocateAndOpen(KPageGroup* out, size_t num_pages, u32 option);
60 Result AllocateForProcess(KPageGroup* out, size_t num_pages, u32 option, u64 process_id,
61 u8 fill_pattern);
62
63 Pool GetPool(PAddr address) const {
64 return this->GetManager(address).GetPool();
65 }
66
67 void Open(PAddr address, size_t num_pages) {
68 // Repeatedly open references until we've done so for all pages.
69 while (num_pages) {
70 auto& manager = this->GetManager(address);
71 const size_t cur_pages = std::min(num_pages, manager.GetPageOffsetToEnd(address));
72
73 {
74 KScopedLightLock lk(m_pool_locks[static_cast<size_t>(manager.GetPool())]);
75 manager.Open(address, cur_pages);
76 }
77
78 num_pages -= cur_pages;
79 address += cur_pages * PageSize;
80 }
81 }
82
83 void OpenFirst(PAddr address, size_t num_pages) {
84 // Repeatedly open references until we've done so for all pages.
85 while (num_pages) {
86 auto& manager = this->GetManager(address);
87 const size_t cur_pages = std::min(num_pages, manager.GetPageOffsetToEnd(address));
88
89 {
90 KScopedLightLock lk(m_pool_locks[static_cast<size_t>(manager.GetPool())]);
91 manager.OpenFirst(address, cur_pages);
92 }
93
94 num_pages -= cur_pages;
95 address += cur_pages * PageSize;
96 }
97 }
98
99 void Close(PAddr address, size_t num_pages) {
100 // Repeatedly close references until we've done so for all pages.
101 while (num_pages) {
102 auto& manager = this->GetManager(address);
103 const size_t cur_pages = std::min(num_pages, manager.GetPageOffsetToEnd(address));
104
105 {
106 KScopedLightLock lk(m_pool_locks[static_cast<size_t>(manager.GetPool())]);
107 manager.Close(address, cur_pages);
108 }
109
110 num_pages -= cur_pages;
111 address += cur_pages * PageSize;
112 }
113 }
114
115 size_t GetSize() {
116 size_t total = 0;
117 for (size_t i = 0; i < m_num_managers; i++) {
118 total += m_managers[i].GetSize();
119 }
120 return total;
121 }
122
123 size_t GetSize(Pool pool) {
58 constexpr Direction GetSizeDirection = Direction::FromFront; 124 constexpr Direction GetSizeDirection = Direction::FromFront;
59 size_t total = 0; 125 size_t total = 0;
60 for (auto* manager = this->GetFirstManager(pool, GetSizeDirection); manager != nullptr; 126 for (auto* manager = this->GetFirstManager(pool, GetSizeDirection); manager != nullptr;
@@ -64,18 +130,36 @@ public:
64 return total; 130 return total;
65 } 131 }
66 132
67 PAddr AllocateAndOpenContinuous(size_t num_pages, size_t align_pages, u32 option); 133 size_t GetFreeSize() {
68 Result AllocateAndOpen(KPageGroup* out, size_t num_pages, u32 option); 134 size_t total = 0;
69 Result AllocateAndOpenForProcess(KPageGroup* out, size_t num_pages, u32 option, u64 process_id, 135 for (size_t i = 0; i < m_num_managers; i++) {
70 u8 fill_pattern); 136 KScopedLightLock lk(m_pool_locks[static_cast<size_t>(m_managers[i].GetPool())]);
137 total += m_managers[i].GetFreeSize();
138 }
139 return total;
140 }
71 141
72 static constexpr size_t MaxManagerCount = 10; 142 size_t GetFreeSize(Pool pool) {
143 KScopedLightLock lk(m_pool_locks[static_cast<size_t>(pool)]);
144
145 constexpr Direction GetSizeDirection = Direction::FromFront;
146 size_t total = 0;
147 for (auto* manager = this->GetFirstManager(pool, GetSizeDirection); manager != nullptr;
148 manager = this->GetNextManager(manager, GetSizeDirection)) {
149 total += manager->GetFreeSize();
150 }
151 return total;
152 }
73 153
74 void Close(PAddr address, size_t num_pages); 154 void DumpFreeList(Pool pool) {
75 void Close(const KPageGroup& pg); 155 KScopedLightLock lk(m_pool_locks[static_cast<size_t>(pool)]);
76 156
77 void Open(PAddr address, size_t num_pages); 157 constexpr Direction DumpDirection = Direction::FromFront;
78 void Open(const KPageGroup& pg); 158 for (auto* manager = this->GetFirstManager(pool, DumpDirection); manager != nullptr;
159 manager = this->GetNextManager(manager, DumpDirection)) {
160 manager->DumpFreeList();
161 }
162 }
79 163
80public: 164public:
81 static size_t CalculateManagementOverheadSize(size_t region_size) { 165 static size_t CalculateManagementOverheadSize(size_t region_size) {
@@ -88,14 +172,13 @@ public:
88 } 172 }
89 173
90 static constexpr Pool GetPool(u32 option) { 174 static constexpr Pool GetPool(u32 option) {
91 return static_cast<Pool>((static_cast<u32>(option) & static_cast<u32>(Pool::Mask)) >> 175 return static_cast<Pool>((option & static_cast<u32>(Pool::Mask)) >>
92 static_cast<u32>(Pool::Shift)); 176 static_cast<u32>(Pool::Shift));
93 } 177 }
94 178
95 static constexpr Direction GetDirection(u32 option) { 179 static constexpr Direction GetDirection(u32 option) {
96 return static_cast<Direction>( 180 return static_cast<Direction>((option & static_cast<u32>(Direction::Mask)) >>
97 (static_cast<u32>(option) & static_cast<u32>(Direction::Mask)) >> 181 static_cast<u32>(Direction::Shift));
98 static_cast<u32>(Direction::Shift));
99 } 182 }
100 183
101 static constexpr std::tuple<Pool, Direction> DecodeOption(u32 option) { 184 static constexpr std::tuple<Pool, Direction> DecodeOption(u32 option) {
@@ -103,74 +186,88 @@ public:
103 } 186 }
104 187
105private: 188private:
106 class Impl final { 189 class Impl {
107 public: 190 public:
108 YUZU_NON_COPYABLE(Impl); 191 static size_t CalculateManagementOverheadSize(size_t region_size);
109 YUZU_NON_MOVEABLE(Impl); 192
193 static constexpr size_t CalculateOptimizedProcessOverheadSize(size_t region_size) {
194 return (Common::AlignUp((region_size / PageSize), Common::BitSize<u64>()) /
195 Common::BitSize<u64>()) *
196 sizeof(u64);
197 }
110 198
199 public:
111 Impl() = default; 200 Impl() = default;
112 ~Impl() = default;
113 201
114 size_t Initialize(PAddr address, size_t size, VAddr management, VAddr management_end, 202 size_t Initialize(PAddr address, size_t size, VAddr management, VAddr management_end,
115 Pool p); 203 Pool p);
116 204
117 VAddr AllocateBlock(s32 index, bool random) { 205 PAddr AllocateBlock(s32 index, bool random) {
118 return heap.AllocateBlock(index, random); 206 return m_heap.AllocateBlock(index, random);
119 } 207 }
120 208 PAddr AllocateAligned(s32 index, size_t num_pages, size_t align_pages) {
121 void Free(VAddr addr, size_t num_pages) { 209 return m_heap.AllocateAligned(index, num_pages, align_pages);
122 heap.Free(addr, num_pages); 210 }
211 void Free(PAddr addr, size_t num_pages) {
212 m_heap.Free(addr, num_pages);
123 } 213 }
124 214
125 void SetInitialUsedHeapSize(size_t reserved_size) { 215 void SetInitialUsedHeapSize(size_t reserved_size) {
126 heap.SetInitialUsedSize(reserved_size); 216 m_heap.SetInitialUsedSize(reserved_size);
127 } 217 }
128 218
129 constexpr Pool GetPool() const { 219 void InitializeOptimizedMemory() {
130 return pool; 220 UNIMPLEMENTED();
131 } 221 }
132 222
223 void TrackUnoptimizedAllocation(PAddr block, size_t num_pages);
224 void TrackOptimizedAllocation(PAddr block, size_t num_pages);
225
226 bool ProcessOptimizedAllocation(PAddr block, size_t num_pages, u8 fill_pattern);
227
228 constexpr Pool GetPool() const {
229 return m_pool;
230 }
133 constexpr size_t GetSize() const { 231 constexpr size_t GetSize() const {
134 return heap.GetSize(); 232 return m_heap.GetSize();
233 }
234 constexpr PAddr GetEndAddress() const {
235 return m_heap.GetEndAddress();
135 } 236 }
136 237
137 constexpr VAddr GetAddress() const { 238 size_t GetFreeSize() const {
138 return heap.GetAddress(); 239 return m_heap.GetFreeSize();
139 } 240 }
140 241
141 constexpr VAddr GetEndAddress() const { 242 void DumpFreeList() const {
142 return heap.GetEndAddress(); 243 UNIMPLEMENTED();
143 } 244 }
144 245
145 constexpr size_t GetPageOffset(PAddr address) const { 246 constexpr size_t GetPageOffset(PAddr address) const {
146 return heap.GetPageOffset(address); 247 return m_heap.GetPageOffset(address);
147 } 248 }
148
149 constexpr size_t GetPageOffsetToEnd(PAddr address) const { 249 constexpr size_t GetPageOffsetToEnd(PAddr address) const {
150 return heap.GetPageOffsetToEnd(address); 250 return m_heap.GetPageOffsetToEnd(address);
151 } 251 }
152 252
153 constexpr void SetNext(Impl* n) { 253 constexpr void SetNext(Impl* n) {
154 next = n; 254 m_next = n;
155 } 255 }
156
157 constexpr void SetPrev(Impl* n) { 256 constexpr void SetPrev(Impl* n) {
158 prev = n; 257 m_prev = n;
159 } 258 }
160
161 constexpr Impl* GetNext() const { 259 constexpr Impl* GetNext() const {
162 return next; 260 return m_next;
163 } 261 }
164
165 constexpr Impl* GetPrev() const { 262 constexpr Impl* GetPrev() const {
166 return prev; 263 return m_prev;
167 } 264 }
168 265
169 void OpenFirst(PAddr address, size_t num_pages) { 266 void OpenFirst(PAddr address, size_t num_pages) {
170 size_t index = this->GetPageOffset(address); 267 size_t index = this->GetPageOffset(address);
171 const size_t end = index + num_pages; 268 const size_t end = index + num_pages;
172 while (index < end) { 269 while (index < end) {
173 const RefCount ref_count = (++page_reference_counts[index]); 270 const RefCount ref_count = (++m_page_reference_counts[index]);
174 ASSERT(ref_count == 1); 271 ASSERT(ref_count == 1);
175 272
176 index++; 273 index++;
@@ -181,7 +278,7 @@ private:
181 size_t index = this->GetPageOffset(address); 278 size_t index = this->GetPageOffset(address);
182 const size_t end = index + num_pages; 279 const size_t end = index + num_pages;
183 while (index < end) { 280 while (index < end) {
184 const RefCount ref_count = (++page_reference_counts[index]); 281 const RefCount ref_count = (++m_page_reference_counts[index]);
185 ASSERT(ref_count > 1); 282 ASSERT(ref_count > 1);
186 283
187 index++; 284 index++;
@@ -195,8 +292,8 @@ private:
195 size_t free_start = 0; 292 size_t free_start = 0;
196 size_t free_count = 0; 293 size_t free_count = 0;
197 while (index < end) { 294 while (index < end) {
198 ASSERT(page_reference_counts[index] > 0); 295 ASSERT(m_page_reference_counts[index] > 0);
199 const RefCount ref_count = (--page_reference_counts[index]); 296 const RefCount ref_count = (--m_page_reference_counts[index]);
200 297
201 // Keep track of how many zero refcounts we see in a row, to minimize calls to free. 298 // Keep track of how many zero refcounts we see in a row, to minimize calls to free.
202 if (ref_count == 0) { 299 if (ref_count == 0) {
@@ -208,7 +305,7 @@ private:
208 } 305 }
209 } else { 306 } else {
210 if (free_count > 0) { 307 if (free_count > 0) {
211 this->Free(heap.GetAddress() + free_start * PageSize, free_count); 308 this->Free(m_heap.GetAddress() + free_start * PageSize, free_count);
212 free_count = 0; 309 free_count = 0;
213 } 310 }
214 } 311 }
@@ -217,44 +314,36 @@ private:
217 } 314 }
218 315
219 if (free_count > 0) { 316 if (free_count > 0) {
220 this->Free(heap.GetAddress() + free_start * PageSize, free_count); 317 this->Free(m_heap.GetAddress() + free_start * PageSize, free_count);
221 } 318 }
222 } 319 }
223 320
224 static size_t CalculateManagementOverheadSize(size_t region_size);
225
226 static constexpr size_t CalculateOptimizedProcessOverheadSize(size_t region_size) {
227 return (Common::AlignUp((region_size / PageSize), Common::BitSize<u64>()) /
228 Common::BitSize<u64>()) *
229 sizeof(u64);
230 }
231
232 private: 321 private:
233 using RefCount = u16; 322 using RefCount = u16;
234 323
235 KPageHeap heap; 324 KPageHeap m_heap;
236 std::vector<RefCount> page_reference_counts; 325 std::vector<RefCount> m_page_reference_counts;
237 VAddr management_region{}; 326 VAddr m_management_region{};
238 Pool pool{}; 327 Pool m_pool{};
239 Impl* next{}; 328 Impl* m_next{};
240 Impl* prev{}; 329 Impl* m_prev{};
241 }; 330 };
242 331
243private: 332private:
244 Impl& GetManager(const KMemoryLayout& memory_layout, PAddr address) { 333 Impl& GetManager(PAddr address) {
245 return managers[memory_layout.GetPhysicalLinearRegion(address).GetAttributes()]; 334 return m_managers[m_memory_layout.GetPhysicalLinearRegion(address).GetAttributes()];
246 } 335 }
247 336
248 const Impl& GetManager(const KMemoryLayout& memory_layout, PAddr address) const { 337 const Impl& GetManager(PAddr address) const {
249 return managers[memory_layout.GetPhysicalLinearRegion(address).GetAttributes()]; 338 return m_managers[m_memory_layout.GetPhysicalLinearRegion(address).GetAttributes()];
250 } 339 }
251 340
252 constexpr Impl* GetFirstManager(Pool pool, Direction dir) const { 341 constexpr Impl* GetFirstManager(Pool pool, Direction dir) {
253 return dir == Direction::FromBack ? pool_managers_tail[static_cast<size_t>(pool)] 342 return dir == Direction::FromBack ? m_pool_managers_tail[static_cast<size_t>(pool)]
254 : pool_managers_head[static_cast<size_t>(pool)]; 343 : m_pool_managers_head[static_cast<size_t>(pool)];
255 } 344 }
256 345
257 constexpr Impl* GetNextManager(Impl* cur, Direction dir) const { 346 constexpr Impl* GetNextManager(Impl* cur, Direction dir) {
258 if (dir == Direction::FromBack) { 347 if (dir == Direction::FromBack) {
259 return cur->GetPrev(); 348 return cur->GetPrev();
260 } else { 349 } else {
@@ -263,15 +352,21 @@ private:
263 } 352 }
264 353
265 Result AllocatePageGroupImpl(KPageGroup* out, size_t num_pages, Pool pool, Direction dir, 354 Result AllocatePageGroupImpl(KPageGroup* out, size_t num_pages, Pool pool, Direction dir,
266 bool random); 355 bool unoptimized, bool random);
267 356
268private: 357private:
269 Core::System& system; 358 template <typename T>
270 std::array<KLightLock, static_cast<size_t>(Pool::Count)> pool_locks; 359 using PoolArray = std::array<T, static_cast<size_t>(Pool::Count)>;
271 std::array<Impl*, MaxManagerCount> pool_managers_head{}; 360
272 std::array<Impl*, MaxManagerCount> pool_managers_tail{}; 361 Core::System& m_system;
273 std::array<Impl, MaxManagerCount> managers; 362 const KMemoryLayout& m_memory_layout;
274 size_t num_managers{}; 363 PoolArray<KLightLock> m_pool_locks;
364 std::array<Impl*, MaxManagerCount> m_pool_managers_head{};
365 std::array<Impl*, MaxManagerCount> m_pool_managers_tail{};
366 std::array<Impl, MaxManagerCount> m_managers;
367 size_t m_num_managers{};
368 PoolArray<u64> m_optimized_process_ids{};
369 PoolArray<bool> m_has_optimized_process{};
275}; 370};
276 371
277} // namespace Kernel 372} // namespace Kernel
diff --git a/src/core/hle/kernel/k_memory_region_type.h b/src/core/hle/kernel/k_memory_region_type.h
index 7e2fcccdc..e5630c1ac 100644
--- a/src/core/hle/kernel/k_memory_region_type.h
+++ b/src/core/hle/kernel/k_memory_region_type.h
@@ -142,32 +142,38 @@ private:
142 142
143} // namespace impl 143} // namespace impl
144 144
145constexpr auto KMemoryRegionType_None = impl::KMemoryRegionTypeValue(); 145constexpr inline auto KMemoryRegionType_None = impl::KMemoryRegionTypeValue();
146constexpr auto KMemoryRegionType_Kernel = KMemoryRegionType_None.DeriveInitial(0, 2); 146
147constexpr auto KMemoryRegionType_Dram = KMemoryRegionType_None.DeriveInitial(1, 2); 147constexpr inline auto KMemoryRegionType_Kernel = KMemoryRegionType_None.DeriveInitial(0, 2);
148constexpr inline auto KMemoryRegionType_Dram = KMemoryRegionType_None.DeriveInitial(1, 2);
148static_assert(KMemoryRegionType_Kernel.GetValue() == 0x1); 149static_assert(KMemoryRegionType_Kernel.GetValue() == 0x1);
149static_assert(KMemoryRegionType_Dram.GetValue() == 0x2); 150static_assert(KMemoryRegionType_Dram.GetValue() == 0x2);
150 151
151constexpr auto KMemoryRegionType_DramKernelBase = 152// constexpr inline auto KMemoryRegionType_CoreLocalRegion =
153// KMemoryRegionType_None.DeriveInitial(2).Finalize();
154// static_assert(KMemoryRegionType_CoreLocalRegion.GetValue() == 0x4);
155
156constexpr inline auto KMemoryRegionType_DramKernelBase =
152 KMemoryRegionType_Dram.DeriveSparse(0, 3, 0) 157 KMemoryRegionType_Dram.DeriveSparse(0, 3, 0)
153 .SetAttribute(KMemoryRegionAttr_NoUserMap) 158 .SetAttribute(KMemoryRegionAttr_NoUserMap)
154 .SetAttribute(KMemoryRegionAttr_CarveoutProtected); 159 .SetAttribute(KMemoryRegionAttr_CarveoutProtected);
155constexpr auto KMemoryRegionType_DramReservedBase = KMemoryRegionType_Dram.DeriveSparse(0, 3, 1); 160constexpr inline auto KMemoryRegionType_DramReservedBase =
156constexpr auto KMemoryRegionType_DramHeapBase = 161 KMemoryRegionType_Dram.DeriveSparse(0, 3, 1);
162constexpr inline auto KMemoryRegionType_DramHeapBase =
157 KMemoryRegionType_Dram.DeriveSparse(0, 3, 2).SetAttribute(KMemoryRegionAttr_LinearMapped); 163 KMemoryRegionType_Dram.DeriveSparse(0, 3, 2).SetAttribute(KMemoryRegionAttr_LinearMapped);
158static_assert(KMemoryRegionType_DramKernelBase.GetValue() == 164static_assert(KMemoryRegionType_DramKernelBase.GetValue() ==
159 (0xE | KMemoryRegionAttr_CarveoutProtected | KMemoryRegionAttr_NoUserMap)); 165 (0xE | KMemoryRegionAttr_CarveoutProtected | KMemoryRegionAttr_NoUserMap));
160static_assert(KMemoryRegionType_DramReservedBase.GetValue() == (0x16)); 166static_assert(KMemoryRegionType_DramReservedBase.GetValue() == (0x16));
161static_assert(KMemoryRegionType_DramHeapBase.GetValue() == (0x26 | KMemoryRegionAttr_LinearMapped)); 167static_assert(KMemoryRegionType_DramHeapBase.GetValue() == (0x26 | KMemoryRegionAttr_LinearMapped));
162 168
163constexpr auto KMemoryRegionType_DramKernelCode = 169constexpr inline auto KMemoryRegionType_DramKernelCode =
164 KMemoryRegionType_DramKernelBase.DeriveSparse(0, 4, 0); 170 KMemoryRegionType_DramKernelBase.DeriveSparse(0, 4, 0);
165constexpr auto KMemoryRegionType_DramKernelSlab = 171constexpr inline auto KMemoryRegionType_DramKernelSlab =
166 KMemoryRegionType_DramKernelBase.DeriveSparse(0, 4, 1); 172 KMemoryRegionType_DramKernelBase.DeriveSparse(0, 4, 1);
167constexpr auto KMemoryRegionType_DramKernelPtHeap = 173constexpr inline auto KMemoryRegionType_DramKernelPtHeap =
168 KMemoryRegionType_DramKernelBase.DeriveSparse(0, 4, 2).SetAttribute( 174 KMemoryRegionType_DramKernelBase.DeriveSparse(0, 4, 2).SetAttribute(
169 KMemoryRegionAttr_LinearMapped); 175 KMemoryRegionAttr_LinearMapped);
170constexpr auto KMemoryRegionType_DramKernelInitPt = 176constexpr inline auto KMemoryRegionType_DramKernelInitPt =
171 KMemoryRegionType_DramKernelBase.DeriveSparse(0, 4, 3).SetAttribute( 177 KMemoryRegionType_DramKernelBase.DeriveSparse(0, 4, 3).SetAttribute(
172 KMemoryRegionAttr_LinearMapped); 178 KMemoryRegionAttr_LinearMapped);
173static_assert(KMemoryRegionType_DramKernelCode.GetValue() == 179static_assert(KMemoryRegionType_DramKernelCode.GetValue() ==
@@ -181,32 +187,40 @@ static_assert(KMemoryRegionType_DramKernelInitPt.GetValue() ==
181 (0x44E | KMemoryRegionAttr_CarveoutProtected | KMemoryRegionAttr_NoUserMap | 187 (0x44E | KMemoryRegionAttr_CarveoutProtected | KMemoryRegionAttr_NoUserMap |
182 KMemoryRegionAttr_LinearMapped)); 188 KMemoryRegionAttr_LinearMapped));
183 189
184constexpr auto KMemoryRegionType_DramReservedEarly = 190constexpr inline auto KMemoryRegionType_DramKernelSecureAppletMemory =
191 KMemoryRegionType_DramKernelBase.DeriveSparse(1, 3, 0).SetAttribute(
192 KMemoryRegionAttr_LinearMapped);
193static_assert(KMemoryRegionType_DramKernelSecureAppletMemory.GetValue() ==
194 (0x18E | KMemoryRegionAttr_CarveoutProtected | KMemoryRegionAttr_NoUserMap |
195 KMemoryRegionAttr_LinearMapped));
196
197constexpr inline auto KMemoryRegionType_DramReservedEarly =
185 KMemoryRegionType_DramReservedBase.DeriveAttribute(KMemoryRegionAttr_NoUserMap); 198 KMemoryRegionType_DramReservedBase.DeriveAttribute(KMemoryRegionAttr_NoUserMap);
186static_assert(KMemoryRegionType_DramReservedEarly.GetValue() == 199static_assert(KMemoryRegionType_DramReservedEarly.GetValue() ==
187 (0x16 | KMemoryRegionAttr_NoUserMap)); 200 (0x16 | KMemoryRegionAttr_NoUserMap));
188 201
189constexpr auto KMemoryRegionType_KernelTraceBuffer = 202constexpr inline auto KMemoryRegionType_KernelTraceBuffer =
190 KMemoryRegionType_DramReservedBase.DeriveSparse(0, 3, 0) 203 KMemoryRegionType_DramReservedBase.DeriveSparse(0, 3, 0)
191 .SetAttribute(KMemoryRegionAttr_LinearMapped) 204 .SetAttribute(KMemoryRegionAttr_LinearMapped)
192 .SetAttribute(KMemoryRegionAttr_UserReadOnly); 205 .SetAttribute(KMemoryRegionAttr_UserReadOnly);
193constexpr auto KMemoryRegionType_OnMemoryBootImage = 206constexpr inline auto KMemoryRegionType_OnMemoryBootImage =
194 KMemoryRegionType_DramReservedBase.DeriveSparse(0, 3, 1); 207 KMemoryRegionType_DramReservedBase.DeriveSparse(0, 3, 1);
195constexpr auto KMemoryRegionType_DTB = KMemoryRegionType_DramReservedBase.DeriveSparse(0, 3, 2); 208constexpr inline auto KMemoryRegionType_DTB =
209 KMemoryRegionType_DramReservedBase.DeriveSparse(0, 3, 2);
196static_assert(KMemoryRegionType_KernelTraceBuffer.GetValue() == 210static_assert(KMemoryRegionType_KernelTraceBuffer.GetValue() ==
197 (0xD6 | KMemoryRegionAttr_LinearMapped | KMemoryRegionAttr_UserReadOnly)); 211 (0xD6 | KMemoryRegionAttr_LinearMapped | KMemoryRegionAttr_UserReadOnly));
198static_assert(KMemoryRegionType_OnMemoryBootImage.GetValue() == 0x156); 212static_assert(KMemoryRegionType_OnMemoryBootImage.GetValue() == 0x156);
199static_assert(KMemoryRegionType_DTB.GetValue() == 0x256); 213static_assert(KMemoryRegionType_DTB.GetValue() == 0x256);
200 214
201constexpr auto KMemoryRegionType_DramPoolPartition = 215constexpr inline auto KMemoryRegionType_DramPoolPartition =
202 KMemoryRegionType_DramHeapBase.DeriveAttribute(KMemoryRegionAttr_NoUserMap); 216 KMemoryRegionType_DramHeapBase.DeriveAttribute(KMemoryRegionAttr_NoUserMap);
203static_assert(KMemoryRegionType_DramPoolPartition.GetValue() == 217static_assert(KMemoryRegionType_DramPoolPartition.GetValue() ==
204 (0x26 | KMemoryRegionAttr_LinearMapped | KMemoryRegionAttr_NoUserMap)); 218 (0x26 | KMemoryRegionAttr_LinearMapped | KMemoryRegionAttr_NoUserMap));
205 219
206constexpr auto KMemoryRegionType_DramPoolManagement = 220constexpr inline auto KMemoryRegionType_DramPoolManagement =
207 KMemoryRegionType_DramPoolPartition.DeriveTransition(0, 2).DeriveTransition().SetAttribute( 221 KMemoryRegionType_DramPoolPartition.DeriveTransition(0, 2).DeriveTransition().SetAttribute(
208 KMemoryRegionAttr_CarveoutProtected); 222 KMemoryRegionAttr_CarveoutProtected);
209constexpr auto KMemoryRegionType_DramUserPool = 223constexpr inline auto KMemoryRegionType_DramUserPool =
210 KMemoryRegionType_DramPoolPartition.DeriveTransition(1, 2).DeriveTransition(); 224 KMemoryRegionType_DramPoolPartition.DeriveTransition(1, 2).DeriveTransition();
211static_assert(KMemoryRegionType_DramPoolManagement.GetValue() == 225static_assert(KMemoryRegionType_DramPoolManagement.GetValue() ==
212 (0x166 | KMemoryRegionAttr_LinearMapped | KMemoryRegionAttr_NoUserMap | 226 (0x166 | KMemoryRegionAttr_LinearMapped | KMemoryRegionAttr_NoUserMap |
@@ -214,11 +228,13 @@ static_assert(KMemoryRegionType_DramPoolManagement.GetValue() ==
214static_assert(KMemoryRegionType_DramUserPool.GetValue() == 228static_assert(KMemoryRegionType_DramUserPool.GetValue() ==
215 (0x1A6 | KMemoryRegionAttr_LinearMapped | KMemoryRegionAttr_NoUserMap)); 229 (0x1A6 | KMemoryRegionAttr_LinearMapped | KMemoryRegionAttr_NoUserMap));
216 230
217constexpr auto KMemoryRegionType_DramApplicationPool = KMemoryRegionType_DramUserPool.Derive(4, 0); 231constexpr inline auto KMemoryRegionType_DramApplicationPool =
218constexpr auto KMemoryRegionType_DramAppletPool = KMemoryRegionType_DramUserPool.Derive(4, 1); 232 KMemoryRegionType_DramUserPool.Derive(4, 0);
219constexpr auto KMemoryRegionType_DramSystemNonSecurePool = 233constexpr inline auto KMemoryRegionType_DramAppletPool =
234 KMemoryRegionType_DramUserPool.Derive(4, 1);
235constexpr inline auto KMemoryRegionType_DramSystemNonSecurePool =
220 KMemoryRegionType_DramUserPool.Derive(4, 2); 236 KMemoryRegionType_DramUserPool.Derive(4, 2);
221constexpr auto KMemoryRegionType_DramSystemPool = 237constexpr inline auto KMemoryRegionType_DramSystemPool =
222 KMemoryRegionType_DramUserPool.Derive(4, 3).SetAttribute(KMemoryRegionAttr_CarveoutProtected); 238 KMemoryRegionType_DramUserPool.Derive(4, 3).SetAttribute(KMemoryRegionAttr_CarveoutProtected);
223static_assert(KMemoryRegionType_DramApplicationPool.GetValue() == 239static_assert(KMemoryRegionType_DramApplicationPool.GetValue() ==
224 (0x7A6 | KMemoryRegionAttr_LinearMapped | KMemoryRegionAttr_NoUserMap)); 240 (0x7A6 | KMemoryRegionAttr_LinearMapped | KMemoryRegionAttr_NoUserMap));
@@ -230,50 +246,55 @@ static_assert(KMemoryRegionType_DramSystemPool.GetValue() ==
230 (0x13A6 | KMemoryRegionAttr_LinearMapped | KMemoryRegionAttr_NoUserMap | 246 (0x13A6 | KMemoryRegionAttr_LinearMapped | KMemoryRegionAttr_NoUserMap |
231 KMemoryRegionAttr_CarveoutProtected)); 247 KMemoryRegionAttr_CarveoutProtected));
232 248
233constexpr auto KMemoryRegionType_VirtualDramHeapBase = KMemoryRegionType_Dram.DeriveSparse(1, 3, 0); 249constexpr inline auto KMemoryRegionType_VirtualDramHeapBase =
234constexpr auto KMemoryRegionType_VirtualDramKernelPtHeap = 250 KMemoryRegionType_Dram.DeriveSparse(1, 3, 0);
251constexpr inline auto KMemoryRegionType_VirtualDramKernelPtHeap =
235 KMemoryRegionType_Dram.DeriveSparse(1, 3, 1); 252 KMemoryRegionType_Dram.DeriveSparse(1, 3, 1);
236constexpr auto KMemoryRegionType_VirtualDramKernelTraceBuffer = 253constexpr inline auto KMemoryRegionType_VirtualDramKernelTraceBuffer =
237 KMemoryRegionType_Dram.DeriveSparse(1, 3, 2); 254 KMemoryRegionType_Dram.DeriveSparse(1, 3, 2);
238static_assert(KMemoryRegionType_VirtualDramHeapBase.GetValue() == 0x1A); 255static_assert(KMemoryRegionType_VirtualDramHeapBase.GetValue() == 0x1A);
239static_assert(KMemoryRegionType_VirtualDramKernelPtHeap.GetValue() == 0x2A); 256static_assert(KMemoryRegionType_VirtualDramKernelPtHeap.GetValue() == 0x2A);
240static_assert(KMemoryRegionType_VirtualDramKernelTraceBuffer.GetValue() == 0x4A); 257static_assert(KMemoryRegionType_VirtualDramKernelTraceBuffer.GetValue() == 0x4A);
241 258
242// UNUSED: .DeriveSparse(2, 2, 0); 259// UNUSED: .DeriveSparse(2, 2, 0);
243constexpr auto KMemoryRegionType_VirtualDramUnknownDebug = 260constexpr inline auto KMemoryRegionType_VirtualDramUnknownDebug =
244 KMemoryRegionType_Dram.DeriveSparse(2, 2, 1); 261 KMemoryRegionType_Dram.DeriveSparse(2, 2, 1);
245static_assert(KMemoryRegionType_VirtualDramUnknownDebug.GetValue() == (0x52)); 262static_assert(KMemoryRegionType_VirtualDramUnknownDebug.GetValue() == (0x52));
246 263
247constexpr auto KMemoryRegionType_VirtualDramKernelInitPt = 264constexpr inline auto KMemoryRegionType_VirtualDramKernelSecureAppletMemory =
265 KMemoryRegionType_Dram.DeriveSparse(3, 1, 0);
266static_assert(KMemoryRegionType_VirtualDramKernelSecureAppletMemory.GetValue() == (0x62));
267
268constexpr inline auto KMemoryRegionType_VirtualDramKernelInitPt =
248 KMemoryRegionType_VirtualDramHeapBase.Derive(3, 0); 269 KMemoryRegionType_VirtualDramHeapBase.Derive(3, 0);
249constexpr auto KMemoryRegionType_VirtualDramPoolManagement = 270constexpr inline auto KMemoryRegionType_VirtualDramPoolManagement =
250 KMemoryRegionType_VirtualDramHeapBase.Derive(3, 1); 271 KMemoryRegionType_VirtualDramHeapBase.Derive(3, 1);
251constexpr auto KMemoryRegionType_VirtualDramUserPool = 272constexpr inline auto KMemoryRegionType_VirtualDramUserPool =
252 KMemoryRegionType_VirtualDramHeapBase.Derive(3, 2); 273 KMemoryRegionType_VirtualDramHeapBase.Derive(3, 2);
253static_assert(KMemoryRegionType_VirtualDramKernelInitPt.GetValue() == 0x19A); 274static_assert(KMemoryRegionType_VirtualDramKernelInitPt.GetValue() == 0x19A);
254static_assert(KMemoryRegionType_VirtualDramPoolManagement.GetValue() == 0x29A); 275static_assert(KMemoryRegionType_VirtualDramPoolManagement.GetValue() == 0x29A);
255static_assert(KMemoryRegionType_VirtualDramUserPool.GetValue() == 0x31A); 276static_assert(KMemoryRegionType_VirtualDramUserPool.GetValue() == 0x31A);
256 277
257// NOTE: For unknown reason, the pools are derived out-of-order here. It's worth eventually trying 278// NOTE: For unknown reason, the pools are derived out-of-order here.
258// to understand why Nintendo made this choice. 279// It's worth eventually trying to understand why Nintendo made this choice.
259// UNUSED: .Derive(6, 0); 280// UNUSED: .Derive(6, 0);
260// UNUSED: .Derive(6, 1); 281// UNUSED: .Derive(6, 1);
261constexpr auto KMemoryRegionType_VirtualDramAppletPool = 282constexpr inline auto KMemoryRegionType_VirtualDramAppletPool =
262 KMemoryRegionType_VirtualDramUserPool.Derive(6, 2); 283 KMemoryRegionType_VirtualDramUserPool.Derive(6, 2);
263constexpr auto KMemoryRegionType_VirtualDramApplicationPool = 284constexpr inline auto KMemoryRegionType_VirtualDramApplicationPool =
264 KMemoryRegionType_VirtualDramUserPool.Derive(6, 3); 285 KMemoryRegionType_VirtualDramUserPool.Derive(6, 3);
265constexpr auto KMemoryRegionType_VirtualDramSystemNonSecurePool = 286constexpr inline auto KMemoryRegionType_VirtualDramSystemNonSecurePool =
266 KMemoryRegionType_VirtualDramUserPool.Derive(6, 4); 287 KMemoryRegionType_VirtualDramUserPool.Derive(6, 4);
267constexpr auto KMemoryRegionType_VirtualDramSystemPool = 288constexpr inline auto KMemoryRegionType_VirtualDramSystemPool =
268 KMemoryRegionType_VirtualDramUserPool.Derive(6, 5); 289 KMemoryRegionType_VirtualDramUserPool.Derive(6, 5);
269static_assert(KMemoryRegionType_VirtualDramAppletPool.GetValue() == 0x1B1A); 290static_assert(KMemoryRegionType_VirtualDramAppletPool.GetValue() == 0x1B1A);
270static_assert(KMemoryRegionType_VirtualDramApplicationPool.GetValue() == 0x271A); 291static_assert(KMemoryRegionType_VirtualDramApplicationPool.GetValue() == 0x271A);
271static_assert(KMemoryRegionType_VirtualDramSystemNonSecurePool.GetValue() == 0x2B1A); 292static_assert(KMemoryRegionType_VirtualDramSystemNonSecurePool.GetValue() == 0x2B1A);
272static_assert(KMemoryRegionType_VirtualDramSystemPool.GetValue() == 0x331A); 293static_assert(KMemoryRegionType_VirtualDramSystemPool.GetValue() == 0x331A);
273 294
274constexpr auto KMemoryRegionType_ArchDeviceBase = 295constexpr inline auto KMemoryRegionType_ArchDeviceBase =
275 KMemoryRegionType_Kernel.DeriveTransition(0, 1).SetSparseOnly(); 296 KMemoryRegionType_Kernel.DeriveTransition(0, 1).SetSparseOnly();
276constexpr auto KMemoryRegionType_BoardDeviceBase = 297constexpr inline auto KMemoryRegionType_BoardDeviceBase =
277 KMemoryRegionType_Kernel.DeriveTransition(0, 2).SetDenseOnly(); 298 KMemoryRegionType_Kernel.DeriveTransition(0, 2).SetDenseOnly();
278static_assert(KMemoryRegionType_ArchDeviceBase.GetValue() == 0x5); 299static_assert(KMemoryRegionType_ArchDeviceBase.GetValue() == 0x5);
279static_assert(KMemoryRegionType_BoardDeviceBase.GetValue() == 0x5); 300static_assert(KMemoryRegionType_BoardDeviceBase.GetValue() == 0x5);
@@ -284,7 +305,7 @@ static_assert(KMemoryRegionType_BoardDeviceBase.GetValue() == 0x5);
284#error "Unimplemented" 305#error "Unimplemented"
285#else 306#else
286// Default to no architecture devices. 307// Default to no architecture devices.
287constexpr auto NumArchitectureDeviceRegions = 0; 308constexpr inline auto NumArchitectureDeviceRegions = 0;
288#endif 309#endif
289static_assert(NumArchitectureDeviceRegions >= 0); 310static_assert(NumArchitectureDeviceRegions >= 0);
290 311
@@ -292,34 +313,35 @@ static_assert(NumArchitectureDeviceRegions >= 0);
292#include "core/hle/kernel/board/nintendo/nx/k_memory_region_device_types.inc" 313#include "core/hle/kernel/board/nintendo/nx/k_memory_region_device_types.inc"
293#else 314#else
294// Default to no board devices. 315// Default to no board devices.
295constexpr auto NumBoardDeviceRegions = 0; 316constexpr inline auto NumBoardDeviceRegions = 0;
296#endif 317#endif
297static_assert(NumBoardDeviceRegions >= 0); 318static_assert(NumBoardDeviceRegions >= 0);
298 319
299constexpr auto KMemoryRegionType_KernelCode = KMemoryRegionType_Kernel.DeriveSparse(1, 4, 0); 320constexpr inline auto KMemoryRegionType_KernelCode = KMemoryRegionType_Kernel.DeriveSparse(1, 4, 0);
300constexpr auto KMemoryRegionType_KernelStack = KMemoryRegionType_Kernel.DeriveSparse(1, 4, 1); 321constexpr inline auto KMemoryRegionType_KernelStack =
301constexpr auto KMemoryRegionType_KernelMisc = KMemoryRegionType_Kernel.DeriveSparse(1, 4, 2); 322 KMemoryRegionType_Kernel.DeriveSparse(1, 4, 1);
302constexpr auto KMemoryRegionType_KernelSlab = KMemoryRegionType_Kernel.DeriveSparse(1, 4, 3); 323constexpr inline auto KMemoryRegionType_KernelMisc = KMemoryRegionType_Kernel.DeriveSparse(1, 4, 2);
324constexpr inline auto KMemoryRegionType_KernelSlab = KMemoryRegionType_Kernel.DeriveSparse(1, 4, 3);
303static_assert(KMemoryRegionType_KernelCode.GetValue() == 0x19); 325static_assert(KMemoryRegionType_KernelCode.GetValue() == 0x19);
304static_assert(KMemoryRegionType_KernelStack.GetValue() == 0x29); 326static_assert(KMemoryRegionType_KernelStack.GetValue() == 0x29);
305static_assert(KMemoryRegionType_KernelMisc.GetValue() == 0x49); 327static_assert(KMemoryRegionType_KernelMisc.GetValue() == 0x49);
306static_assert(KMemoryRegionType_KernelSlab.GetValue() == 0x89); 328static_assert(KMemoryRegionType_KernelSlab.GetValue() == 0x89);
307 329
308constexpr auto KMemoryRegionType_KernelMiscDerivedBase = 330constexpr inline auto KMemoryRegionType_KernelMiscDerivedBase =
309 KMemoryRegionType_KernelMisc.DeriveTransition(); 331 KMemoryRegionType_KernelMisc.DeriveTransition();
310static_assert(KMemoryRegionType_KernelMiscDerivedBase.GetValue() == 0x149); 332static_assert(KMemoryRegionType_KernelMiscDerivedBase.GetValue() == 0x149);
311 333
312// UNUSED: .Derive(7, 0); 334// UNUSED: .Derive(7, 0);
313constexpr auto KMemoryRegionType_KernelMiscMainStack = 335constexpr inline auto KMemoryRegionType_KernelMiscMainStack =
314 KMemoryRegionType_KernelMiscDerivedBase.Derive(7, 1); 336 KMemoryRegionType_KernelMiscDerivedBase.Derive(7, 1);
315constexpr auto KMemoryRegionType_KernelMiscMappedDevice = 337constexpr inline auto KMemoryRegionType_KernelMiscMappedDevice =
316 KMemoryRegionType_KernelMiscDerivedBase.Derive(7, 2); 338 KMemoryRegionType_KernelMiscDerivedBase.Derive(7, 2);
317constexpr auto KMemoryRegionType_KernelMiscExceptionStack = 339constexpr inline auto KMemoryRegionType_KernelMiscExceptionStack =
318 KMemoryRegionType_KernelMiscDerivedBase.Derive(7, 3); 340 KMemoryRegionType_KernelMiscDerivedBase.Derive(7, 3);
319constexpr auto KMemoryRegionType_KernelMiscUnknownDebug = 341constexpr inline auto KMemoryRegionType_KernelMiscUnknownDebug =
320 KMemoryRegionType_KernelMiscDerivedBase.Derive(7, 4); 342 KMemoryRegionType_KernelMiscDerivedBase.Derive(7, 4);
321// UNUSED: .Derive(7, 5); 343// UNUSED: .Derive(7, 5);
322constexpr auto KMemoryRegionType_KernelMiscIdleStack = 344constexpr inline auto KMemoryRegionType_KernelMiscIdleStack =
323 KMemoryRegionType_KernelMiscDerivedBase.Derive(7, 6); 345 KMemoryRegionType_KernelMiscDerivedBase.Derive(7, 6);
324static_assert(KMemoryRegionType_KernelMiscMainStack.GetValue() == 0xB49); 346static_assert(KMemoryRegionType_KernelMiscMainStack.GetValue() == 0xB49);
325static_assert(KMemoryRegionType_KernelMiscMappedDevice.GetValue() == 0xD49); 347static_assert(KMemoryRegionType_KernelMiscMappedDevice.GetValue() == 0xD49);
@@ -327,7 +349,8 @@ static_assert(KMemoryRegionType_KernelMiscExceptionStack.GetValue() == 0x1349);
327static_assert(KMemoryRegionType_KernelMiscUnknownDebug.GetValue() == 0x1549); 349static_assert(KMemoryRegionType_KernelMiscUnknownDebug.GetValue() == 0x1549);
328static_assert(KMemoryRegionType_KernelMiscIdleStack.GetValue() == 0x2349); 350static_assert(KMemoryRegionType_KernelMiscIdleStack.GetValue() == 0x2349);
329 351
330constexpr auto KMemoryRegionType_KernelTemp = KMemoryRegionType_Kernel.Advance(2).Derive(2, 0); 352constexpr inline auto KMemoryRegionType_KernelTemp =
353 KMemoryRegionType_Kernel.Advance(2).Derive(2, 0);
331static_assert(KMemoryRegionType_KernelTemp.GetValue() == 0x31); 354static_assert(KMemoryRegionType_KernelTemp.GetValue() == 0x31);
332 355
333constexpr KMemoryRegionType GetTypeForVirtualLinearMapping(u32 type_id) { 356constexpr KMemoryRegionType GetTypeForVirtualLinearMapping(u32 type_id) {
@@ -335,6 +358,8 @@ constexpr KMemoryRegionType GetTypeForVirtualLinearMapping(u32 type_id) {
335 return KMemoryRegionType_VirtualDramKernelTraceBuffer; 358 return KMemoryRegionType_VirtualDramKernelTraceBuffer;
336 } else if (KMemoryRegionType_DramKernelPtHeap.IsAncestorOf(type_id)) { 359 } else if (KMemoryRegionType_DramKernelPtHeap.IsAncestorOf(type_id)) {
337 return KMemoryRegionType_VirtualDramKernelPtHeap; 360 return KMemoryRegionType_VirtualDramKernelPtHeap;
361 } else if (KMemoryRegionType_DramKernelSecureAppletMemory.IsAncestorOf(type_id)) {
362 return KMemoryRegionType_VirtualDramKernelSecureAppletMemory;
338 } else if ((type_id | KMemoryRegionAttr_ShouldKernelMap) == type_id) { 363 } else if ((type_id | KMemoryRegionAttr_ShouldKernelMap) == type_id) {
339 return KMemoryRegionType_VirtualDramUnknownDebug; 364 return KMemoryRegionType_VirtualDramUnknownDebug;
340 } else { 365 } else {
diff --git a/src/core/hle/kernel/k_page_bitmap.h b/src/core/hle/kernel/k_page_bitmap.h
index c97b3dc0b..0ff987732 100644
--- a/src/core/hle/kernel/k_page_bitmap.h
+++ b/src/core/hle/kernel/k_page_bitmap.h
@@ -16,107 +16,126 @@
16namespace Kernel { 16namespace Kernel {
17 17
18class KPageBitmap { 18class KPageBitmap {
19private: 19public:
20 class RandomBitGenerator { 20 class RandomBitGenerator {
21 private: 21 public:
22 Common::TinyMT rng{}; 22 RandomBitGenerator() {
23 u32 entropy{}; 23 m_rng.Initialize(static_cast<u32>(KSystemControl::GenerateRandomU64()));
24 u32 bits_available{}; 24 }
25
26 u64 SelectRandomBit(u64 bitmap) {
27 u64 selected = 0;
28
29 for (size_t cur_num_bits = Common::BitSize<decltype(bitmap)>() / 2; cur_num_bits != 0;
30 cur_num_bits /= 2) {
31 const u64 high = (bitmap >> cur_num_bits);
32 const u64 low = (bitmap & (~(UINT64_C(0xFFFFFFFFFFFFFFFF) << cur_num_bits)));
33
34 // Choose high if we have high and (don't have low or select high randomly).
35 if (high && (low == 0 || this->GenerateRandomBit())) {
36 bitmap = high;
37 selected += cur_num_bits;
38 } else {
39 bitmap = low;
40 selected += 0;
41 }
42 }
43
44 return selected;
45 }
46
47 u64 GenerateRandom(u64 max) {
48 // Determine the number of bits we need.
49 const u64 bits_needed = 1 + (Common::BitSize<decltype(max)>() - std::countl_zero(max));
50
51 // Generate a random value of the desired bitwidth.
52 const u64 rnd = this->GenerateRandomBits(static_cast<u32>(bits_needed));
53
54 // Adjust the value to be in range.
55 return rnd - ((rnd / max) * max);
56 }
25 57
26 private: 58 private:
27 void RefreshEntropy() { 59 void RefreshEntropy() {
28 entropy = rng.GenerateRandomU32(); 60 m_entropy = m_rng.GenerateRandomU32();
29 bits_available = static_cast<u32>(Common::BitSize<decltype(entropy)>()); 61 m_bits_available = static_cast<u32>(Common::BitSize<decltype(m_entropy)>());
30 } 62 }
31 63
32 bool GenerateRandomBit() { 64 bool GenerateRandomBit() {
33 if (bits_available == 0) { 65 if (m_bits_available == 0) {
34 this->RefreshEntropy(); 66 this->RefreshEntropy();
35 } 67 }
36 68
37 const bool rnd_bit = (entropy & 1) != 0; 69 const bool rnd_bit = (m_entropy & 1) != 0;
38 entropy >>= 1; 70 m_entropy >>= 1;
39 --bits_available; 71 --m_bits_available;
40 return rnd_bit; 72 return rnd_bit;
41 } 73 }
42 74
43 public: 75 u64 GenerateRandomBits(u32 num_bits) {
44 RandomBitGenerator() { 76 u64 result = 0;
45 rng.Initialize(static_cast<u32>(KSystemControl::GenerateRandomU64()));
46 }
47 77
48 std::size_t SelectRandomBit(u64 bitmap) { 78 // Iteratively add random bits to our result.
49 u64 selected = 0; 79 while (num_bits > 0) {
80 // Ensure we have random bits to take from.
81 if (m_bits_available == 0) {
82 this->RefreshEntropy();
83 }
50 84
51 u64 cur_num_bits = Common::BitSize<decltype(bitmap)>() / 2; 85 // Determine how many bits to take this round.
52 u64 cur_mask = (1ULL << cur_num_bits) - 1; 86 const auto cur_bits = std::min(num_bits, m_bits_available);
53 87
54 while (cur_num_bits) { 88 // Generate mask for our current bits.
55 const u64 low = (bitmap >> 0) & cur_mask; 89 const u64 mask = (static_cast<u64>(1) << cur_bits) - 1;
56 const u64 high = (bitmap >> cur_num_bits) & cur_mask;
57 90
58 bool choose_low; 91 // Add bits to output from our entropy.
59 if (high == 0) { 92 result <<= cur_bits;
60 // If only low val is set, choose low. 93 result |= (m_entropy & mask);
61 choose_low = true;
62 } else if (low == 0) {
63 // If only high val is set, choose high.
64 choose_low = false;
65 } else {
66 // If both are set, choose random.
67 choose_low = this->GenerateRandomBit();
68 }
69 94
70 // If we chose low, proceed with low. 95 // Remove bits from our entropy.
71 if (choose_low) { 96 m_entropy >>= cur_bits;
72 bitmap = low; 97 m_bits_available -= cur_bits;
73 selected += 0;
74 } else {
75 bitmap = high;
76 selected += cur_num_bits;
77 }
78 98
79 // Proceed. 99 // Advance.
80 cur_num_bits /= 2; 100 num_bits -= cur_bits;
81 cur_mask >>= cur_num_bits;
82 } 101 }
83 102
84 return selected; 103 return result;
85 } 104 }
105
106 private:
107 Common::TinyMT m_rng;
108 u32 m_entropy{};
109 u32 m_bits_available{};
86 }; 110 };
87 111
88public: 112public:
89 static constexpr std::size_t MaxDepth = 4; 113 static constexpr size_t MaxDepth = 4;
90
91private:
92 std::array<u64*, MaxDepth> bit_storages{};
93 RandomBitGenerator rng{};
94 std::size_t num_bits{};
95 std::size_t used_depths{};
96 114
97public: 115public:
98 KPageBitmap() = default; 116 KPageBitmap() = default;
99 117
100 constexpr std::size_t GetNumBits() const { 118 constexpr size_t GetNumBits() const {
101 return num_bits; 119 return m_num_bits;
102 } 120 }
103 constexpr s32 GetHighestDepthIndex() const { 121 constexpr s32 GetHighestDepthIndex() const {
104 return static_cast<s32>(used_depths) - 1; 122 return static_cast<s32>(m_used_depths) - 1;
105 } 123 }
106 124
107 u64* Initialize(u64* storage, std::size_t size) { 125 u64* Initialize(u64* storage, size_t size) {
108 // Initially, everything is un-set. 126 // Initially, everything is un-set.
109 num_bits = 0; 127 m_num_bits = 0;
110 128
111 // Calculate the needed bitmap depth. 129 // Calculate the needed bitmap depth.
112 used_depths = static_cast<std::size_t>(GetRequiredDepth(size)); 130 m_used_depths = static_cast<size_t>(GetRequiredDepth(size));
113 ASSERT(used_depths <= MaxDepth); 131 ASSERT(m_used_depths <= MaxDepth);
114 132
115 // Set the bitmap pointers. 133 // Set the bitmap pointers.
116 for (s32 depth = this->GetHighestDepthIndex(); depth >= 0; depth--) { 134 for (s32 depth = this->GetHighestDepthIndex(); depth >= 0; depth--) {
117 bit_storages[depth] = storage; 135 m_bit_storages[depth] = storage;
118 size = Common::AlignUp(size, Common::BitSize<u64>()) / Common::BitSize<u64>(); 136 size = Common::AlignUp(size, Common::BitSize<u64>()) / Common::BitSize<u64>();
119 storage += size; 137 storage += size;
138 m_end_storages[depth] = storage;
120 } 139 }
121 140
122 return storage; 141 return storage;
@@ -128,19 +147,19 @@ public:
128 147
129 if (random) { 148 if (random) {
130 do { 149 do {
131 const u64 v = bit_storages[depth][offset]; 150 const u64 v = m_bit_storages[depth][offset];
132 if (v == 0) { 151 if (v == 0) {
133 // If depth is bigger than zero, then a previous level indicated a block was 152 // If depth is bigger than zero, then a previous level indicated a block was
134 // free. 153 // free.
135 ASSERT(depth == 0); 154 ASSERT(depth == 0);
136 return -1; 155 return -1;
137 } 156 }
138 offset = offset * Common::BitSize<u64>() + rng.SelectRandomBit(v); 157 offset = offset * Common::BitSize<u64>() + m_rng.SelectRandomBit(v);
139 ++depth; 158 ++depth;
140 } while (depth < static_cast<s32>(used_depths)); 159 } while (depth < static_cast<s32>(m_used_depths));
141 } else { 160 } else {
142 do { 161 do {
143 const u64 v = bit_storages[depth][offset]; 162 const u64 v = m_bit_storages[depth][offset];
144 if (v == 0) { 163 if (v == 0) {
145 // If depth is bigger than zero, then a previous level indicated a block was 164 // If depth is bigger than zero, then a previous level indicated a block was
146 // free. 165 // free.
@@ -149,28 +168,69 @@ public:
149 } 168 }
150 offset = offset * Common::BitSize<u64>() + std::countr_zero(v); 169 offset = offset * Common::BitSize<u64>() + std::countr_zero(v);
151 ++depth; 170 ++depth;
152 } while (depth < static_cast<s32>(used_depths)); 171 } while (depth < static_cast<s32>(m_used_depths));
153 } 172 }
154 173
155 return static_cast<s64>(offset); 174 return static_cast<s64>(offset);
156 } 175 }
157 176
158 void SetBit(std::size_t offset) { 177 s64 FindFreeRange(size_t count) {
178 // Check that it is possible to find a range.
179 const u64* const storage_start = m_bit_storages[m_used_depths - 1];
180 const u64* const storage_end = m_end_storages[m_used_depths - 1];
181
182 // If we don't have a storage to iterate (or want more blocks than fit in a single storage),
183 // we can't find a free range.
184 if (!(storage_start < storage_end && count <= Common::BitSize<u64>())) {
185 return -1;
186 }
187
188 // Walk the storages to select a random free range.
189 const size_t options_per_storage = std::max<size_t>(Common::BitSize<u64>() / count, 1);
190 const size_t num_entries = std::max<size_t>(storage_end - storage_start, 1);
191
192 const u64 free_mask = (static_cast<u64>(1) << count) - 1;
193
194 size_t num_valid_options = 0;
195 s64 chosen_offset = -1;
196 for (size_t storage_index = 0; storage_index < num_entries; ++storage_index) {
197 u64 storage = storage_start[storage_index];
198 for (size_t option = 0; option < options_per_storage; ++option) {
199 if ((storage & free_mask) == free_mask) {
200 // We've found a new valid option.
201 ++num_valid_options;
202
203 // Select the Kth valid option with probability 1/K. This leads to an overall
204 // uniform distribution.
205 if (num_valid_options == 1 || m_rng.GenerateRandom(num_valid_options) == 0) {
206 // This is our first option, so select it.
207 chosen_offset = storage_index * Common::BitSize<u64>() + option * count;
208 }
209 }
210 storage >>= count;
211 }
212 }
213
214 // Return the random offset we chose.*/
215 return chosen_offset;
216 }
217
218 void SetBit(size_t offset) {
159 this->SetBit(this->GetHighestDepthIndex(), offset); 219 this->SetBit(this->GetHighestDepthIndex(), offset);
160 num_bits++; 220 m_num_bits++;
161 } 221 }
162 222
163 void ClearBit(std::size_t offset) { 223 void ClearBit(size_t offset) {
164 this->ClearBit(this->GetHighestDepthIndex(), offset); 224 this->ClearBit(this->GetHighestDepthIndex(), offset);
165 num_bits--; 225 m_num_bits--;
166 } 226 }
167 227
168 bool ClearRange(std::size_t offset, std::size_t count) { 228 bool ClearRange(size_t offset, size_t count) {
169 s32 depth = this->GetHighestDepthIndex(); 229 s32 depth = this->GetHighestDepthIndex();
170 u64* bits = bit_storages[depth]; 230 u64* bits = m_bit_storages[depth];
171 std::size_t bit_ind = offset / Common::BitSize<u64>(); 231 size_t bit_ind = offset / Common::BitSize<u64>();
172 if (count < Common::BitSize<u64>()) { 232 if (count < Common::BitSize<u64>()) [[likely]] {
173 const std::size_t shift = offset % Common::BitSize<u64>(); 233 const size_t shift = offset % Common::BitSize<u64>();
174 ASSERT(shift + count <= Common::BitSize<u64>()); 234 ASSERT(shift + count <= Common::BitSize<u64>());
175 // Check that all the bits are set. 235 // Check that all the bits are set.
176 const u64 mask = ((u64(1) << count) - 1) << shift; 236 const u64 mask = ((u64(1) << count) - 1) << shift;
@@ -189,8 +249,8 @@ public:
189 ASSERT(offset % Common::BitSize<u64>() == 0); 249 ASSERT(offset % Common::BitSize<u64>() == 0);
190 ASSERT(count % Common::BitSize<u64>() == 0); 250 ASSERT(count % Common::BitSize<u64>() == 0);
191 // Check that all the bits are set. 251 // Check that all the bits are set.
192 std::size_t remaining = count; 252 size_t remaining = count;
193 std::size_t i = 0; 253 size_t i = 0;
194 do { 254 do {
195 if (bits[bit_ind + i++] != ~u64(0)) { 255 if (bits[bit_ind + i++] != ~u64(0)) {
196 return false; 256 return false;
@@ -209,18 +269,18 @@ public:
209 } while (remaining > 0); 269 } while (remaining > 0);
210 } 270 }
211 271
212 num_bits -= count; 272 m_num_bits -= count;
213 return true; 273 return true;
214 } 274 }
215 275
216private: 276private:
217 void SetBit(s32 depth, std::size_t offset) { 277 void SetBit(s32 depth, size_t offset) {
218 while (depth >= 0) { 278 while (depth >= 0) {
219 std::size_t ind = offset / Common::BitSize<u64>(); 279 size_t ind = offset / Common::BitSize<u64>();
220 std::size_t which = offset % Common::BitSize<u64>(); 280 size_t which = offset % Common::BitSize<u64>();
221 const u64 mask = u64(1) << which; 281 const u64 mask = u64(1) << which;
222 282
223 u64* bit = std::addressof(bit_storages[depth][ind]); 283 u64* bit = std::addressof(m_bit_storages[depth][ind]);
224 u64 v = *bit; 284 u64 v = *bit;
225 ASSERT((v & mask) == 0); 285 ASSERT((v & mask) == 0);
226 *bit = v | mask; 286 *bit = v | mask;
@@ -232,13 +292,13 @@ private:
232 } 292 }
233 } 293 }
234 294
235 void ClearBit(s32 depth, std::size_t offset) { 295 void ClearBit(s32 depth, size_t offset) {
236 while (depth >= 0) { 296 while (depth >= 0) {
237 std::size_t ind = offset / Common::BitSize<u64>(); 297 size_t ind = offset / Common::BitSize<u64>();
238 std::size_t which = offset % Common::BitSize<u64>(); 298 size_t which = offset % Common::BitSize<u64>();
239 const u64 mask = u64(1) << which; 299 const u64 mask = u64(1) << which;
240 300
241 u64* bit = std::addressof(bit_storages[depth][ind]); 301 u64* bit = std::addressof(m_bit_storages[depth][ind]);
242 u64 v = *bit; 302 u64 v = *bit;
243 ASSERT((v & mask) != 0); 303 ASSERT((v & mask) != 0);
244 v &= ~mask; 304 v &= ~mask;
@@ -252,7 +312,7 @@ private:
252 } 312 }
253 313
254private: 314private:
255 static constexpr s32 GetRequiredDepth(std::size_t region_size) { 315 static constexpr s32 GetRequiredDepth(size_t region_size) {
256 s32 depth = 0; 316 s32 depth = 0;
257 while (true) { 317 while (true) {
258 region_size /= Common::BitSize<u64>(); 318 region_size /= Common::BitSize<u64>();
@@ -264,8 +324,8 @@ private:
264 } 324 }
265 325
266public: 326public:
267 static constexpr std::size_t CalculateManagementOverheadSize(std::size_t region_size) { 327 static constexpr size_t CalculateManagementOverheadSize(size_t region_size) {
268 std::size_t overhead_bits = 0; 328 size_t overhead_bits = 0;
269 for (s32 depth = GetRequiredDepth(region_size) - 1; depth >= 0; depth--) { 329 for (s32 depth = GetRequiredDepth(region_size) - 1; depth >= 0; depth--) {
270 region_size = 330 region_size =
271 Common::AlignUp(region_size, Common::BitSize<u64>()) / Common::BitSize<u64>(); 331 Common::AlignUp(region_size, Common::BitSize<u64>()) / Common::BitSize<u64>();
@@ -273,6 +333,13 @@ public:
273 } 333 }
274 return overhead_bits * sizeof(u64); 334 return overhead_bits * sizeof(u64);
275 } 335 }
336
337private:
338 std::array<u64*, MaxDepth> m_bit_storages{};
339 std::array<u64*, MaxDepth> m_end_storages{};
340 RandomBitGenerator m_rng;
341 size_t m_num_bits{};
342 size_t m_used_depths{};
276}; 343};
277 344
278} // namespace Kernel 345} // namespace Kernel
diff --git a/src/core/hle/kernel/k_page_buffer.h b/src/core/hle/kernel/k_page_buffer.h
index aef06e213..cfedaae61 100644
--- a/src/core/hle/kernel/k_page_buffer.h
+++ b/src/core/hle/kernel/k_page_buffer.h
@@ -11,6 +11,16 @@
11 11
12namespace Kernel { 12namespace Kernel {
13 13
14class KernelCore;
15
16class KPageBufferSlabHeap : protected impl::KSlabHeapImpl {
17public:
18 static constexpr size_t BufferSize = PageSize;
19
20public:
21 void Initialize(Core::System& system);
22};
23
14class KPageBuffer final : public KSlabAllocated<KPageBuffer> { 24class KPageBuffer final : public KSlabAllocated<KPageBuffer> {
15public: 25public:
16 explicit KPageBuffer(KernelCore&) {} 26 explicit KPageBuffer(KernelCore&) {}
@@ -21,8 +31,6 @@ public:
21private: 31private:
22 [[maybe_unused]] alignas(PageSize) std::array<u8, PageSize> m_buffer{}; 32 [[maybe_unused]] alignas(PageSize) std::array<u8, PageSize> m_buffer{};
23}; 33};
24 34static_assert(sizeof(KPageBuffer) == KPageBufferSlabHeap::BufferSize);
25static_assert(sizeof(KPageBuffer) == PageSize);
26static_assert(alignof(KPageBuffer) == PageSize);
27 35
28} // namespace Kernel 36} // namespace Kernel
diff --git a/src/core/hle/kernel/k_page_group.h b/src/core/hle/kernel/k_page_group.h
index 968753992..316f172f2 100644
--- a/src/core/hle/kernel/k_page_group.h
+++ b/src/core/hle/kernel/k_page_group.h
@@ -5,6 +5,7 @@
5 5
6#include <list> 6#include <list>
7 7
8#include "common/alignment.h"
8#include "common/assert.h" 9#include "common/assert.h"
9#include "common/common_types.h" 10#include "common/common_types.h"
10#include "core/hle/kernel/memory_types.h" 11#include "core/hle/kernel/memory_types.h"
@@ -12,6 +13,89 @@
12 13
13namespace Kernel { 14namespace Kernel {
14 15
16class KPageGroup;
17
18class KBlockInfo {
19private:
20 friend class KPageGroup;
21
22public:
23 constexpr KBlockInfo() = default;
24
25 constexpr void Initialize(PAddr addr, size_t np) {
26 ASSERT(Common::IsAligned(addr, PageSize));
27 ASSERT(static_cast<u32>(np) == np);
28
29 m_page_index = static_cast<u32>(addr) / PageSize;
30 m_num_pages = static_cast<u32>(np);
31 }
32
33 constexpr PAddr GetAddress() const {
34 return m_page_index * PageSize;
35 }
36 constexpr size_t GetNumPages() const {
37 return m_num_pages;
38 }
39 constexpr size_t GetSize() const {
40 return this->GetNumPages() * PageSize;
41 }
42 constexpr PAddr GetEndAddress() const {
43 return (m_page_index + m_num_pages) * PageSize;
44 }
45 constexpr PAddr GetLastAddress() const {
46 return this->GetEndAddress() - 1;
47 }
48
49 constexpr KBlockInfo* GetNext() const {
50 return m_next;
51 }
52
53 constexpr bool IsEquivalentTo(const KBlockInfo& rhs) const {
54 return m_page_index == rhs.m_page_index && m_num_pages == rhs.m_num_pages;
55 }
56
57 constexpr bool operator==(const KBlockInfo& rhs) const {
58 return this->IsEquivalentTo(rhs);
59 }
60
61 constexpr bool operator!=(const KBlockInfo& rhs) const {
62 return !(*this == rhs);
63 }
64
65 constexpr bool IsStrictlyBefore(PAddr addr) const {
66 const PAddr end = this->GetEndAddress();
67
68 if (m_page_index != 0 && end == 0) {
69 return false;
70 }
71
72 return end < addr;
73 }
74
75 constexpr bool operator<(PAddr addr) const {
76 return this->IsStrictlyBefore(addr);
77 }
78
79 constexpr bool TryConcatenate(PAddr addr, size_t np) {
80 if (addr != 0 && addr == this->GetEndAddress()) {
81 m_num_pages += static_cast<u32>(np);
82 return true;
83 }
84 return false;
85 }
86
87private:
88 constexpr void SetNext(KBlockInfo* next) {
89 m_next = next;
90 }
91
92private:
93 KBlockInfo* m_next{};
94 u32 m_page_index{};
95 u32 m_num_pages{};
96};
97static_assert(sizeof(KBlockInfo) <= 0x10);
98
15class KPageGroup final { 99class KPageGroup final {
16public: 100public:
17 class Node final { 101 class Node final {
@@ -92,6 +176,8 @@ public:
92 return nodes.empty(); 176 return nodes.empty();
93 } 177 }
94 178
179 void Finalize() {}
180
95private: 181private:
96 std::list<Node> nodes; 182 std::list<Node> nodes;
97}; 183};
diff --git a/src/core/hle/kernel/k_page_heap.cpp b/src/core/hle/kernel/k_page_heap.cpp
index 5ede60168..7b02c7d8b 100644
--- a/src/core/hle/kernel/k_page_heap.cpp
+++ b/src/core/hle/kernel/k_page_heap.cpp
@@ -44,11 +44,11 @@ size_t KPageHeap::GetNumFreePages() const {
44 return num_free; 44 return num_free;
45} 45}
46 46
47PAddr KPageHeap::AllocateBlock(s32 index, bool random) { 47PAddr KPageHeap::AllocateByLinearSearch(s32 index) {
48 const size_t needed_size = m_blocks[index].GetSize(); 48 const size_t needed_size = m_blocks[index].GetSize();
49 49
50 for (s32 i = index; i < static_cast<s32>(m_num_blocks); i++) { 50 for (s32 i = index; i < static_cast<s32>(m_num_blocks); i++) {
51 if (const PAddr addr = m_blocks[i].PopBlock(random); addr != 0) { 51 if (const PAddr addr = m_blocks[i].PopBlock(false); addr != 0) {
52 if (const size_t allocated_size = m_blocks[i].GetSize(); allocated_size > needed_size) { 52 if (const size_t allocated_size = m_blocks[i].GetSize(); allocated_size > needed_size) {
53 this->Free(addr + needed_size, (allocated_size - needed_size) / PageSize); 53 this->Free(addr + needed_size, (allocated_size - needed_size) / PageSize);
54 } 54 }
@@ -59,6 +59,88 @@ PAddr KPageHeap::AllocateBlock(s32 index, bool random) {
59 return 0; 59 return 0;
60} 60}
61 61
62PAddr KPageHeap::AllocateByRandom(s32 index, size_t num_pages, size_t align_pages) {
63 // Get the size and required alignment.
64 const size_t needed_size = num_pages * PageSize;
65 const size_t align_size = align_pages * PageSize;
66
67 // Determine meta-alignment of our desired alignment size.
68 const size_t align_shift = std::countr_zero(align_size);
69
70 // Decide on a block to allocate from.
71 constexpr size_t MinimumPossibleAlignmentsForRandomAllocation = 4;
72 {
73 // By default, we'll want to look at all blocks larger than our current one.
74 s32 max_blocks = static_cast<s32>(m_num_blocks);
75
76 // Determine the maximum block we should try to allocate from.
77 size_t possible_alignments = 0;
78 for (s32 i = index; i < max_blocks; ++i) {
79 // Add the possible alignments from blocks at the current size.
80 possible_alignments += (1 + ((m_blocks[i].GetSize() - needed_size) >> align_shift)) *
81 m_blocks[i].GetNumFreeBlocks();
82
83 // If there are enough possible alignments, we don't need to look at larger blocks.
84 if (possible_alignments >= MinimumPossibleAlignmentsForRandomAllocation) {
85 max_blocks = i + 1;
86 break;
87 }
88 }
89
90 // If we have any possible alignments which require a larger block, we need to pick one.
91 if (possible_alignments > 0 && index + 1 < max_blocks) {
92 // Select a random alignment from the possibilities.
93 const size_t rnd = m_rng.GenerateRandom(possible_alignments);
94
95 // Determine which block corresponds to the random alignment we chose.
96 possible_alignments = 0;
97 for (s32 i = index; i < max_blocks; ++i) {
98 // Add the possible alignments from blocks at the current size.
99 possible_alignments +=
100 (1 + ((m_blocks[i].GetSize() - needed_size) >> align_shift)) *
101 m_blocks[i].GetNumFreeBlocks();
102
103 // If the current block gets us to our random choice, use the current block.
104 if (rnd < possible_alignments) {
105 index = i;
106 break;
107 }
108 }
109 }
110 }
111
112 // Pop a block from the index we selected.
113 if (PAddr addr = m_blocks[index].PopBlock(true); addr != 0) {
114 // Determine how much size we have left over.
115 if (const size_t leftover_size = m_blocks[index].GetSize() - needed_size;
116 leftover_size > 0) {
117 // Determine how many valid alignments we can have.
118 const size_t possible_alignments = 1 + (leftover_size >> align_shift);
119
120 // Select a random valid alignment.
121 const size_t random_offset = m_rng.GenerateRandom(possible_alignments) << align_shift;
122
123 // Free memory before the random offset.
124 if (random_offset != 0) {
125 this->Free(addr, random_offset / PageSize);
126 }
127
128 // Advance our block by the random offset.
129 addr += random_offset;
130
131 // Free memory after our allocated block.
132 if (random_offset != leftover_size) {
133 this->Free(addr + needed_size, (leftover_size - random_offset) / PageSize);
134 }
135 }
136
137 // Return the block we allocated.
138 return addr;
139 }
140
141 return 0;
142}
143
62void KPageHeap::FreeBlock(PAddr block, s32 index) { 144void KPageHeap::FreeBlock(PAddr block, s32 index) {
63 do { 145 do {
64 block = m_blocks[index++].PushBlock(block); 146 block = m_blocks[index++].PushBlock(block);
diff --git a/src/core/hle/kernel/k_page_heap.h b/src/core/hle/kernel/k_page_heap.h
index 0917a8bed..9021edcf7 100644
--- a/src/core/hle/kernel/k_page_heap.h
+++ b/src/core/hle/kernel/k_page_heap.h
@@ -14,13 +14,9 @@
14 14
15namespace Kernel { 15namespace Kernel {
16 16
17class KPageHeap final { 17class KPageHeap {
18public: 18public:
19 YUZU_NON_COPYABLE(KPageHeap);
20 YUZU_NON_MOVEABLE(KPageHeap);
21
22 KPageHeap() = default; 19 KPageHeap() = default;
23 ~KPageHeap() = default;
24 20
25 constexpr PAddr GetAddress() const { 21 constexpr PAddr GetAddress() const {
26 return m_heap_address; 22 return m_heap_address;
@@ -57,7 +53,20 @@ public:
57 m_initial_used_size = m_heap_size - free_size - reserved_size; 53 m_initial_used_size = m_heap_size - free_size - reserved_size;
58 } 54 }
59 55
60 PAddr AllocateBlock(s32 index, bool random); 56 PAddr AllocateBlock(s32 index, bool random) {
57 if (random) {
58 const size_t block_pages = m_blocks[index].GetNumPages();
59 return this->AllocateByRandom(index, block_pages, block_pages);
60 } else {
61 return this->AllocateByLinearSearch(index);
62 }
63 }
64
65 PAddr AllocateAligned(s32 index, size_t num_pages, size_t align_pages) {
66 // TODO: linear search support?
67 return this->AllocateByRandom(index, num_pages, align_pages);
68 }
69
61 void Free(PAddr addr, size_t num_pages); 70 void Free(PAddr addr, size_t num_pages);
62 71
63 static size_t CalculateManagementOverheadSize(size_t region_size) { 72 static size_t CalculateManagementOverheadSize(size_t region_size) {
@@ -68,7 +77,7 @@ public:
68 static constexpr s32 GetAlignedBlockIndex(size_t num_pages, size_t align_pages) { 77 static constexpr s32 GetAlignedBlockIndex(size_t num_pages, size_t align_pages) {
69 const size_t target_pages = std::max(num_pages, align_pages); 78 const size_t target_pages = std::max(num_pages, align_pages);
70 for (size_t i = 0; i < NumMemoryBlockPageShifts; i++) { 79 for (size_t i = 0; i < NumMemoryBlockPageShifts; i++) {
71 if (target_pages <= (size_t(1) << MemoryBlockPageShifts[i]) / PageSize) { 80 if (target_pages <= (static_cast<size_t>(1) << MemoryBlockPageShifts[i]) / PageSize) {
72 return static_cast<s32>(i); 81 return static_cast<s32>(i);
73 } 82 }
74 } 83 }
@@ -77,7 +86,7 @@ public:
77 86
78 static constexpr s32 GetBlockIndex(size_t num_pages) { 87 static constexpr s32 GetBlockIndex(size_t num_pages) {
79 for (s32 i = static_cast<s32>(NumMemoryBlockPageShifts) - 1; i >= 0; i--) { 88 for (s32 i = static_cast<s32>(NumMemoryBlockPageShifts) - 1; i >= 0; i--) {
80 if (num_pages >= (size_t(1) << MemoryBlockPageShifts[i]) / PageSize) { 89 if (num_pages >= (static_cast<size_t>(1) << MemoryBlockPageShifts[i]) / PageSize) {
81 return i; 90 return i;
82 } 91 }
83 } 92 }
@@ -85,7 +94,7 @@ public:
85 } 94 }
86 95
87 static constexpr size_t GetBlockSize(size_t index) { 96 static constexpr size_t GetBlockSize(size_t index) {
88 return size_t(1) << MemoryBlockPageShifts[index]; 97 return static_cast<size_t>(1) << MemoryBlockPageShifts[index];
89 } 98 }
90 99
91 static constexpr size_t GetBlockNumPages(size_t index) { 100 static constexpr size_t GetBlockNumPages(size_t index) {
@@ -93,13 +102,9 @@ public:
93 } 102 }
94 103
95private: 104private:
96 class Block final { 105 class Block {
97 public: 106 public:
98 YUZU_NON_COPYABLE(Block);
99 YUZU_NON_MOVEABLE(Block);
100
101 Block() = default; 107 Block() = default;
102 ~Block() = default;
103 108
104 constexpr size_t GetShift() const { 109 constexpr size_t GetShift() const {
105 return m_block_shift; 110 return m_block_shift;
@@ -201,6 +206,9 @@ private:
201 }; 206 };
202 207
203private: 208private:
209 PAddr AllocateByLinearSearch(s32 index);
210 PAddr AllocateByRandom(s32 index, size_t num_pages, size_t align_pages);
211
204 static size_t CalculateManagementOverheadSize(size_t region_size, const size_t* block_shifts, 212 static size_t CalculateManagementOverheadSize(size_t region_size, const size_t* block_shifts,
205 size_t num_block_shifts); 213 size_t num_block_shifts);
206 214
@@ -209,7 +217,8 @@ private:
209 size_t m_heap_size{}; 217 size_t m_heap_size{};
210 size_t m_initial_used_size{}; 218 size_t m_initial_used_size{};
211 size_t m_num_blocks{}; 219 size_t m_num_blocks{};
212 std::array<Block, NumMemoryBlockPageShifts> m_blocks{}; 220 std::array<Block, NumMemoryBlockPageShifts> m_blocks;
221 KPageBitmap::RandomBitGenerator m_rng;
213 std::vector<u64> m_management_data; 222 std::vector<u64> m_management_data;
214}; 223};
215 224
diff --git a/src/core/hle/kernel/k_page_table.cpp b/src/core/hle/kernel/k_page_table.cpp
index 307e491cb..5387bf5fe 100644
--- a/src/core/hle/kernel/k_page_table.cpp
+++ b/src/core/hle/kernel/k_page_table.cpp
@@ -15,6 +15,7 @@
15#include "core/hle/kernel/k_resource_limit.h" 15#include "core/hle/kernel/k_resource_limit.h"
16#include "core/hle/kernel/k_scoped_resource_reservation.h" 16#include "core/hle/kernel/k_scoped_resource_reservation.h"
17#include "core/hle/kernel/k_system_control.h" 17#include "core/hle/kernel/k_system_control.h"
18#include "core/hle/kernel/k_system_resource.h"
18#include "core/hle/kernel/kernel.h" 19#include "core/hle/kernel/kernel.h"
19#include "core/hle/kernel/svc_results.h" 20#include "core/hle/kernel/svc_results.h"
20#include "core/memory.h" 21#include "core/memory.h"
@@ -23,6 +24,61 @@ namespace Kernel {
23 24
24namespace { 25namespace {
25 26
27class KScopedLightLockPair {
28 YUZU_NON_COPYABLE(KScopedLightLockPair);
29 YUZU_NON_MOVEABLE(KScopedLightLockPair);
30
31private:
32 KLightLock* m_lower;
33 KLightLock* m_upper;
34
35public:
36 KScopedLightLockPair(KLightLock& lhs, KLightLock& rhs) {
37 // Ensure our locks are in a consistent order.
38 if (std::addressof(lhs) <= std::addressof(rhs)) {
39 m_lower = std::addressof(lhs);
40 m_upper = std::addressof(rhs);
41 } else {
42 m_lower = std::addressof(rhs);
43 m_upper = std::addressof(lhs);
44 }
45
46 // Acquire both locks.
47 m_lower->Lock();
48 if (m_lower != m_upper) {
49 m_upper->Lock();
50 }
51 }
52
53 ~KScopedLightLockPair() {
54 // Unlock the upper lock.
55 if (m_upper != nullptr && m_upper != m_lower) {
56 m_upper->Unlock();
57 }
58
59 // Unlock the lower lock.
60 if (m_lower != nullptr) {
61 m_lower->Unlock();
62 }
63 }
64
65public:
66 // Utility.
67 void TryUnlockHalf(KLightLock& lock) {
68 // Only allow unlocking if the lock is half the pair.
69 if (m_lower != m_upper) {
70 // We want to be sure the lock is one we own.
71 if (m_lower == std::addressof(lock)) {
72 lock.Unlock();
73 m_lower = nullptr;
74 } else if (m_upper == std::addressof(lock)) {
75 lock.Unlock();
76 m_upper = nullptr;
77 }
78 }
79 }
80};
81
26using namespace Common::Literals; 82using namespace Common::Literals;
27 83
28constexpr size_t GetAddressSpaceWidthFromType(FileSys::ProgramAddressSpaceType as_type) { 84constexpr size_t GetAddressSpaceWidthFromType(FileSys::ProgramAddressSpaceType as_type) {
@@ -49,9 +105,10 @@ KPageTable::KPageTable(Core::System& system_)
49KPageTable::~KPageTable() = default; 105KPageTable::~KPageTable() = default;
50 106
51Result KPageTable::InitializeForProcess(FileSys::ProgramAddressSpaceType as_type, bool enable_aslr, 107Result KPageTable::InitializeForProcess(FileSys::ProgramAddressSpaceType as_type, bool enable_aslr,
52 VAddr code_addr, size_t code_size, 108 bool enable_das_merge, bool from_back,
53 KMemoryBlockSlabManager* mem_block_slab_manager, 109 KMemoryManager::Pool pool, VAddr code_addr,
54 KMemoryManager::Pool pool) { 110 size_t code_size, KSystemResource* system_resource,
111 KResourceLimit* resource_limit) {
55 112
56 const auto GetSpaceStart = [this](KAddressSpaceInfo::Type type) { 113 const auto GetSpaceStart = [this](KAddressSpaceInfo::Type type) {
57 return KAddressSpaceInfo::GetAddressSpaceStart(m_address_space_width, type); 114 return KAddressSpaceInfo::GetAddressSpaceStart(m_address_space_width, type);
@@ -112,11 +169,13 @@ Result KPageTable::InitializeForProcess(FileSys::ProgramAddressSpaceType as_type
112 169
113 // Set other basic fields 170 // Set other basic fields
114 m_enable_aslr = enable_aslr; 171 m_enable_aslr = enable_aslr;
115 m_enable_device_address_space_merge = false; 172 m_enable_device_address_space_merge = enable_das_merge;
116 m_address_space_start = start; 173 m_address_space_start = start;
117 m_address_space_end = end; 174 m_address_space_end = end;
118 m_is_kernel = false; 175 m_is_kernel = false;
119 m_memory_block_slab_manager = mem_block_slab_manager; 176 m_memory_block_slab_manager = system_resource->GetMemoryBlockSlabManagerPointer();
177 m_block_info_manager = system_resource->GetBlockInfoManagerPointer();
178 m_resource_limit = resource_limit;
120 179
121 // Determine the region we can place our undetermineds in 180 // Determine the region we can place our undetermineds in
122 VAddr alloc_start{}; 181 VAddr alloc_start{};
@@ -215,10 +274,22 @@ Result KPageTable::InitializeForProcess(FileSys::ProgramAddressSpaceType as_type
215 } 274 }
216 } 275 }
217 276
218 // Set heap members 277 // Set heap and fill members.
219 m_current_heap_end = m_heap_region_start; 278 m_current_heap_end = m_heap_region_start;
220 m_max_heap_size = 0; 279 m_max_heap_size = 0;
221 m_max_physical_memory_size = 0; 280 m_mapped_physical_memory_size = 0;
281 m_mapped_unsafe_physical_memory = 0;
282 m_mapped_insecure_memory = 0;
283 m_mapped_ipc_server_memory = 0;
284
285 m_heap_fill_value = 0;
286 m_ipc_fill_value = 0;
287 m_stack_fill_value = 0;
288
289 // Set allocation option.
290 m_allocate_option =
291 KMemoryManager::EncodeOption(pool, from_back ? KMemoryManager::Direction::FromBack
292 : KMemoryManager::Direction::FromFront);
222 293
223 // Ensure that we regions inside our address space 294 // Ensure that we regions inside our address space
224 auto IsInAddressSpace = [&](VAddr addr) { 295 auto IsInAddressSpace = [&](VAddr addr) {
@@ -267,6 +338,16 @@ void KPageTable::Finalize() {
267 m_system.Memory().UnmapRegion(*m_page_table_impl, addr, size); 338 m_system.Memory().UnmapRegion(*m_page_table_impl, addr, size);
268 }); 339 });
269 340
341 // Release any insecure mapped memory.
342 if (m_mapped_insecure_memory) {
343 UNIMPLEMENTED();
344 }
345
346 // Release any ipc server memory.
347 if (m_mapped_ipc_server_memory) {
348 UNIMPLEMENTED();
349 }
350
270 // Close the backing page table, as the destructor is not called for guest objects. 351 // Close the backing page table, as the destructor is not called for guest objects.
271 m_page_table_impl.reset(); 352 m_page_table_impl.reset();
272} 353}
@@ -650,7 +731,8 @@ bool KPageTable::IsValidPageGroup(const KPageGroup& pg_ll, VAddr addr, size_t nu
650 731
651Result KPageTable::UnmapProcessMemory(VAddr dst_addr, size_t size, KPageTable& src_page_table, 732Result KPageTable::UnmapProcessMemory(VAddr dst_addr, size_t size, KPageTable& src_page_table,
652 VAddr src_addr) { 733 VAddr src_addr) {
653 KScopedLightLock lk(m_general_lock); 734 // Acquire the table locks.
735 KScopedLightLockPair lk(src_page_table.m_general_lock, m_general_lock);
654 736
655 const size_t num_pages{size / PageSize}; 737 const size_t num_pages{size / PageSize};
656 738
@@ -686,9 +768,753 @@ Result KPageTable::UnmapProcessMemory(VAddr dst_addr, size_t size, KPageTable& s
686 R_SUCCEED(); 768 R_SUCCEED();
687} 769}
688 770
771Result KPageTable::SetupForIpcClient(PageLinkedList* page_list, size_t* out_blocks_needed,
772 VAddr address, size_t size, KMemoryPermission test_perm,
773 KMemoryState dst_state) {
774 // Validate pre-conditions.
775 ASSERT(this->IsLockedByCurrentThread());
776 ASSERT(test_perm == KMemoryPermission::UserReadWrite ||
777 test_perm == KMemoryPermission::UserRead);
778
779 // Check that the address is in range.
780 R_UNLESS(this->Contains(address, size), ResultInvalidCurrentMemory);
781
782 // Get the source permission.
783 const auto src_perm = (test_perm == KMemoryPermission::UserReadWrite)
784 ? KMemoryPermission::KernelReadWrite | KMemoryPermission::NotMapped
785 : KMemoryPermission::UserRead;
786
787 // Get aligned extents.
788 const VAddr aligned_src_start = Common::AlignDown((address), PageSize);
789 const VAddr aligned_src_end = Common::AlignUp((address) + size, PageSize);
790 const VAddr mapping_src_start = Common::AlignUp((address), PageSize);
791 const VAddr mapping_src_end = Common::AlignDown((address) + size, PageSize);
792
793 const auto aligned_src_last = (aligned_src_end)-1;
794 const auto mapping_src_last = (mapping_src_end)-1;
795
796 // Get the test state and attribute mask.
797 KMemoryState test_state;
798 KMemoryAttribute test_attr_mask;
799 switch (dst_state) {
800 case KMemoryState::Ipc:
801 test_state = KMemoryState::FlagCanUseIpc;
802 test_attr_mask =
803 KMemoryAttribute::Uncached | KMemoryAttribute::DeviceShared | KMemoryAttribute::Locked;
804 break;
805 case KMemoryState::NonSecureIpc:
806 test_state = KMemoryState::FlagCanUseNonSecureIpc;
807 test_attr_mask = KMemoryAttribute::Uncached | KMemoryAttribute::Locked;
808 break;
809 case KMemoryState::NonDeviceIpc:
810 test_state = KMemoryState::FlagCanUseNonDeviceIpc;
811 test_attr_mask = KMemoryAttribute::Uncached | KMemoryAttribute::Locked;
812 break;
813 default:
814 R_THROW(ResultInvalidCombination);
815 }
816
817 // Ensure that on failure, we roll back appropriately.
818 size_t mapped_size = 0;
819 ON_RESULT_FAILURE {
820 if (mapped_size > 0) {
821 this->CleanupForIpcClientOnServerSetupFailure(page_list, mapping_src_start, mapped_size,
822 src_perm);
823 }
824 };
825
826 size_t blocks_needed = 0;
827
828 // Iterate, mapping as needed.
829 KMemoryBlockManager::const_iterator it = m_memory_block_manager.FindIterator(aligned_src_start);
830 while (true) {
831 const KMemoryInfo info = it->GetMemoryInfo();
832
833 // Validate the current block.
834 R_TRY(this->CheckMemoryState(info, test_state, test_state, test_perm, test_perm,
835 test_attr_mask, KMemoryAttribute::None));
836
837 if (mapping_src_start < mapping_src_end && (mapping_src_start) < info.GetEndAddress() &&
838 info.GetAddress() < (mapping_src_end)) {
839 const auto cur_start =
840 info.GetAddress() >= (mapping_src_start) ? info.GetAddress() : (mapping_src_start);
841 const auto cur_end = mapping_src_last >= info.GetLastAddress() ? info.GetEndAddress()
842 : (mapping_src_end);
843 const size_t cur_size = cur_end - cur_start;
844
845 if (info.GetAddress() < (mapping_src_start)) {
846 ++blocks_needed;
847 }
848 if (mapping_src_last < info.GetLastAddress()) {
849 ++blocks_needed;
850 }
851
852 // Set the permissions on the block, if we need to.
853 if ((info.GetPermission() & KMemoryPermission::IpcLockChangeMask) != src_perm) {
854 R_TRY(Operate(cur_start, cur_size / PageSize, src_perm,
855 OperationType::ChangePermissions));
856 }
857
858 // Note that we mapped this part.
859 mapped_size += cur_size;
860 }
861
862 // If the block is at the end, we're done.
863 if (aligned_src_last <= info.GetLastAddress()) {
864 break;
865 }
866
867 // Advance.
868 ++it;
869 ASSERT(it != m_memory_block_manager.end());
870 }
871
872 if (out_blocks_needed != nullptr) {
873 ASSERT(blocks_needed <= KMemoryBlockManagerUpdateAllocator::MaxBlocks);
874 *out_blocks_needed = blocks_needed;
875 }
876
877 R_SUCCEED();
878}
879
880Result KPageTable::SetupForIpcServer(VAddr* out_addr, size_t size, VAddr src_addr,
881 KMemoryPermission test_perm, KMemoryState dst_state,
882 KPageTable& src_page_table, bool send) {
883 ASSERT(this->IsLockedByCurrentThread());
884 ASSERT(src_page_table.IsLockedByCurrentThread());
885
886 // Check that we can theoretically map.
887 const VAddr region_start = m_alias_region_start;
888 const size_t region_size = m_alias_region_end - m_alias_region_start;
889 R_UNLESS(size < region_size, ResultOutOfAddressSpace);
890
891 // Get aligned source extents.
892 const VAddr src_start = src_addr;
893 const VAddr src_end = src_addr + size;
894 const VAddr aligned_src_start = Common::AlignDown((src_start), PageSize);
895 const VAddr aligned_src_end = Common::AlignUp((src_start) + size, PageSize);
896 const VAddr mapping_src_start = Common::AlignUp((src_start), PageSize);
897 const VAddr mapping_src_end = Common::AlignDown((src_start) + size, PageSize);
898 const size_t aligned_src_size = aligned_src_end - aligned_src_start;
899 const size_t mapping_src_size =
900 (mapping_src_start < mapping_src_end) ? (mapping_src_end - mapping_src_start) : 0;
901
902 // Select a random address to map at.
903 VAddr dst_addr =
904 this->FindFreeArea(region_start, region_size / PageSize, aligned_src_size / PageSize,
905 PageSize, 0, this->GetNumGuardPages());
906
907 R_UNLESS(dst_addr != 0, ResultOutOfAddressSpace);
908
909 // Check that we can perform the operation we're about to perform.
910 ASSERT(this->CanContain(dst_addr, aligned_src_size, dst_state));
911
912 // Create an update allocator.
913 Result allocator_result;
914 KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result),
915 m_memory_block_slab_manager);
916 R_TRY(allocator_result);
917
918 // We're going to perform an update, so create a helper.
919 KScopedPageTableUpdater updater(this);
920
921 // Reserve space for any partial pages we allocate.
922 const size_t unmapped_size = aligned_src_size - mapping_src_size;
923 KScopedResourceReservation memory_reservation(
924 m_resource_limit, LimitableResource::PhysicalMemoryMax, unmapped_size);
925 R_UNLESS(memory_reservation.Succeeded(), ResultLimitReached);
926
927 // Ensure that we manage page references correctly.
928 PAddr start_partial_page = 0;
929 PAddr end_partial_page = 0;
930 VAddr cur_mapped_addr = dst_addr;
931
932 // If the partial pages are mapped, an extra reference will have been opened. Otherwise, they'll
933 // free on scope exit.
934 SCOPE_EXIT({
935 if (start_partial_page != 0) {
936 m_system.Kernel().MemoryManager().Close(start_partial_page, 1);
937 }
938 if (end_partial_page != 0) {
939 m_system.Kernel().MemoryManager().Close(end_partial_page, 1);
940 }
941 });
942
943 ON_RESULT_FAILURE {
944 if (cur_mapped_addr != dst_addr) {
945 // HACK: Manually close the pages.
946 HACK_ClosePages(dst_addr, (cur_mapped_addr - dst_addr) / PageSize);
947
948 ASSERT(Operate(dst_addr, (cur_mapped_addr - dst_addr) / PageSize,
949 KMemoryPermission::None, OperationType::Unmap)
950 .IsSuccess());
951 }
952 };
953
954 // Allocate the start page as needed.
955 if (aligned_src_start < mapping_src_start) {
956 start_partial_page =
957 m_system.Kernel().MemoryManager().AllocateAndOpenContinuous(1, 1, m_allocate_option);
958 R_UNLESS(start_partial_page != 0, ResultOutOfMemory);
959 }
960
961 // Allocate the end page as needed.
962 if (mapping_src_end < aligned_src_end &&
963 (aligned_src_start < mapping_src_end || aligned_src_start == mapping_src_start)) {
964 end_partial_page =
965 m_system.Kernel().MemoryManager().AllocateAndOpenContinuous(1, 1, m_allocate_option);
966 R_UNLESS(end_partial_page != 0, ResultOutOfMemory);
967 }
968
969 // Get the implementation.
970 auto& src_impl = src_page_table.PageTableImpl();
971
972 // Get the fill value for partial pages.
973 const auto fill_val = m_ipc_fill_value;
974
975 // Begin traversal.
976 Common::PageTable::TraversalContext context;
977 Common::PageTable::TraversalEntry next_entry;
978 bool traverse_valid = src_impl.BeginTraversal(next_entry, context, aligned_src_start);
979 ASSERT(traverse_valid);
980
981 // Prepare tracking variables.
982 PAddr cur_block_addr = next_entry.phys_addr;
983 size_t cur_block_size =
984 next_entry.block_size - ((cur_block_addr) & (next_entry.block_size - 1));
985 size_t tot_block_size = cur_block_size;
986
987 // Map the start page, if we have one.
988 if (start_partial_page != 0) {
989 // Ensure the page holds correct data.
990 const VAddr start_partial_virt =
991 GetHeapVirtualAddress(m_system.Kernel().MemoryLayout(), start_partial_page);
992 if (send) {
993 const size_t partial_offset = src_start - aligned_src_start;
994 size_t copy_size, clear_size;
995 if (src_end < mapping_src_start) {
996 copy_size = size;
997 clear_size = mapping_src_start - src_end;
998 } else {
999 copy_size = mapping_src_start - src_start;
1000 clear_size = 0;
1001 }
1002
1003 std::memset(m_system.Memory().GetPointer<void>(start_partial_virt), fill_val,
1004 partial_offset);
1005 std::memcpy(
1006 m_system.Memory().GetPointer<void>(start_partial_virt + partial_offset),
1007 m_system.Memory().GetPointer<void>(
1008 GetHeapVirtualAddress(m_system.Kernel().MemoryLayout(), cur_block_addr) +
1009 partial_offset),
1010 copy_size);
1011 if (clear_size > 0) {
1012 std::memset(m_system.Memory().GetPointer<void>(start_partial_virt + partial_offset +
1013 copy_size),
1014 fill_val, clear_size);
1015 }
1016 } else {
1017 std::memset(m_system.Memory().GetPointer<void>(start_partial_virt), fill_val, PageSize);
1018 }
1019
1020 // Map the page.
1021 R_TRY(Operate(cur_mapped_addr, 1, test_perm, OperationType::Map, start_partial_page));
1022
1023 // HACK: Manually open the pages.
1024 HACK_OpenPages(start_partial_page, 1);
1025
1026 // Update tracking extents.
1027 cur_mapped_addr += PageSize;
1028 cur_block_addr += PageSize;
1029 cur_block_size -= PageSize;
1030
1031 // If the block's size was one page, we may need to continue traversal.
1032 if (cur_block_size == 0 && aligned_src_size > PageSize) {
1033 traverse_valid = src_impl.ContinueTraversal(next_entry, context);
1034 ASSERT(traverse_valid);
1035
1036 cur_block_addr = next_entry.phys_addr;
1037 cur_block_size = next_entry.block_size;
1038 tot_block_size += next_entry.block_size;
1039 }
1040 }
1041
1042 // Map the remaining pages.
1043 while (aligned_src_start + tot_block_size < mapping_src_end) {
1044 // Continue the traversal.
1045 traverse_valid = src_impl.ContinueTraversal(next_entry, context);
1046 ASSERT(traverse_valid);
1047
1048 // Process the block.
1049 if (next_entry.phys_addr != cur_block_addr + cur_block_size) {
1050 // Map the block we've been processing so far.
1051 R_TRY(Operate(cur_mapped_addr, cur_block_size / PageSize, test_perm, OperationType::Map,
1052 cur_block_addr));
1053
1054 // HACK: Manually open the pages.
1055 HACK_OpenPages(cur_block_addr, cur_block_size / PageSize);
1056
1057 // Update tracking extents.
1058 cur_mapped_addr += cur_block_size;
1059 cur_block_addr = next_entry.phys_addr;
1060 cur_block_size = next_entry.block_size;
1061 } else {
1062 cur_block_size += next_entry.block_size;
1063 }
1064 tot_block_size += next_entry.block_size;
1065 }
1066
1067 // Handle the last direct-mapped page.
1068 if (const VAddr mapped_block_end = aligned_src_start + tot_block_size - cur_block_size;
1069 mapped_block_end < mapping_src_end) {
1070 const size_t last_block_size = mapping_src_end - mapped_block_end;
1071
1072 // Map the last block.
1073 R_TRY(Operate(cur_mapped_addr, last_block_size / PageSize, test_perm, OperationType::Map,
1074 cur_block_addr));
1075
1076 // HACK: Manually open the pages.
1077 HACK_OpenPages(cur_block_addr, last_block_size / PageSize);
1078
1079 // Update tracking extents.
1080 cur_mapped_addr += last_block_size;
1081 cur_block_addr += last_block_size;
1082 if (mapped_block_end + cur_block_size < aligned_src_end &&
1083 cur_block_size == last_block_size) {
1084 traverse_valid = src_impl.ContinueTraversal(next_entry, context);
1085 ASSERT(traverse_valid);
1086
1087 cur_block_addr = next_entry.phys_addr;
1088 }
1089 }
1090
1091 // Map the end page, if we have one.
1092 if (end_partial_page != 0) {
1093 // Ensure the page holds correct data.
1094 const VAddr end_partial_virt =
1095 GetHeapVirtualAddress(m_system.Kernel().MemoryLayout(), end_partial_page);
1096 if (send) {
1097 const size_t copy_size = src_end - mapping_src_end;
1098 std::memcpy(m_system.Memory().GetPointer<void>(end_partial_virt),
1099 m_system.Memory().GetPointer<void>(GetHeapVirtualAddress(
1100 m_system.Kernel().MemoryLayout(), cur_block_addr)),
1101 copy_size);
1102 std::memset(m_system.Memory().GetPointer<void>(end_partial_virt + copy_size), fill_val,
1103 PageSize - copy_size);
1104 } else {
1105 std::memset(m_system.Memory().GetPointer<void>(end_partial_virt), fill_val, PageSize);
1106 }
1107
1108 // Map the page.
1109 R_TRY(Operate(cur_mapped_addr, 1, test_perm, OperationType::Map, end_partial_page));
1110
1111 // HACK: Manually open the pages.
1112 HACK_OpenPages(end_partial_page, 1);
1113 }
1114
1115 // Update memory blocks to reflect our changes
1116 m_memory_block_manager.Update(std::addressof(allocator), dst_addr, aligned_src_size / PageSize,
1117 dst_state, test_perm, KMemoryAttribute::None,
1118 KMemoryBlockDisableMergeAttribute::Normal,
1119 KMemoryBlockDisableMergeAttribute::None);
1120
1121 // Set the output address.
1122 *out_addr = dst_addr + (src_start - aligned_src_start);
1123
1124 // We succeeded.
1125 memory_reservation.Commit();
1126 R_SUCCEED();
1127}
1128
1129Result KPageTable::SetupForIpc(VAddr* out_dst_addr, size_t size, VAddr src_addr,
1130 KPageTable& src_page_table, KMemoryPermission test_perm,
1131 KMemoryState dst_state, bool send) {
1132 // For convenience, alias this.
1133 KPageTable& dst_page_table = *this;
1134
1135 // Acquire the table locks.
1136 KScopedLightLockPair lk(src_page_table.m_general_lock, dst_page_table.m_general_lock);
1137
1138 // We're going to perform an update, so create a helper.
1139 KScopedPageTableUpdater updater(std::addressof(src_page_table));
1140
1141 // Perform client setup.
1142 size_t num_allocator_blocks;
1143 R_TRY(src_page_table.SetupForIpcClient(updater.GetPageList(),
1144 std::addressof(num_allocator_blocks), src_addr, size,
1145 test_perm, dst_state));
1146
1147 // Create an update allocator.
1148 Result allocator_result;
1149 KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result),
1150 src_page_table.m_memory_block_slab_manager,
1151 num_allocator_blocks);
1152 R_TRY(allocator_result);
1153
1154 // Get the mapped extents.
1155 const VAddr src_map_start = Common::AlignUp((src_addr), PageSize);
1156 const VAddr src_map_end = Common::AlignDown((src_addr) + size, PageSize);
1157 const size_t src_map_size = src_map_end - src_map_start;
1158
1159 // Ensure that we clean up appropriately if we fail after this.
1160 const auto src_perm = (test_perm == KMemoryPermission::UserReadWrite)
1161 ? KMemoryPermission::KernelReadWrite | KMemoryPermission::NotMapped
1162 : KMemoryPermission::UserRead;
1163 ON_RESULT_FAILURE {
1164 if (src_map_end > src_map_start) {
1165 src_page_table.CleanupForIpcClientOnServerSetupFailure(
1166 updater.GetPageList(), src_map_start, src_map_size, src_perm);
1167 }
1168 };
1169
1170 // Perform server setup.
1171 R_TRY(dst_page_table.SetupForIpcServer(out_dst_addr, size, src_addr, test_perm, dst_state,
1172 src_page_table, send));
1173
1174 // If anything was mapped, ipc-lock the pages.
1175 if (src_map_start < src_map_end) {
1176 // Get the source permission.
1177 src_page_table.m_memory_block_manager.UpdateLock(std::addressof(allocator), src_map_start,
1178 (src_map_end - src_map_start) / PageSize,
1179 &KMemoryBlock::LockForIpc, src_perm);
1180 }
1181
1182 R_SUCCEED();
1183}
1184
1185Result KPageTable::CleanupForIpcServer(VAddr address, size_t size, KMemoryState dst_state) {
1186 // Validate the address.
1187 R_UNLESS(this->Contains(address, size), ResultInvalidCurrentMemory);
1188
1189 // Lock the table.
1190 KScopedLightLock lk(m_general_lock);
1191
1192 // Validate the memory state.
1193 size_t num_allocator_blocks;
1194 R_TRY(this->CheckMemoryState(std::addressof(num_allocator_blocks), address, size,
1195 KMemoryState::All, dst_state, KMemoryPermission::UserRead,
1196 KMemoryPermission::UserRead, KMemoryAttribute::All,
1197 KMemoryAttribute::None));
1198
1199 // Create an update allocator.
1200 Result allocator_result;
1201 KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result),
1202 m_memory_block_slab_manager, num_allocator_blocks);
1203 R_TRY(allocator_result);
1204
1205 // We're going to perform an update, so create a helper.
1206 KScopedPageTableUpdater updater(this);
1207
1208 // Get aligned extents.
1209 const VAddr aligned_start = Common::AlignDown((address), PageSize);
1210 const VAddr aligned_end = Common::AlignUp((address) + size, PageSize);
1211 const size_t aligned_size = aligned_end - aligned_start;
1212 const size_t aligned_num_pages = aligned_size / PageSize;
1213
1214 // HACK: Manually close the pages.
1215 HACK_ClosePages(aligned_start, aligned_num_pages);
1216
1217 // Unmap the pages.
1218 R_TRY(Operate(aligned_start, aligned_num_pages, KMemoryPermission::None, OperationType::Unmap));
1219
1220 // Update memory blocks.
1221 m_memory_block_manager.Update(std::addressof(allocator), aligned_start, aligned_num_pages,
1222 KMemoryState::None, KMemoryPermission::None,
1223 KMemoryAttribute::None, KMemoryBlockDisableMergeAttribute::None,
1224 KMemoryBlockDisableMergeAttribute::Normal);
1225
1226 // Release from the resource limit as relevant.
1227 const VAddr mapping_start = Common::AlignUp((address), PageSize);
1228 const VAddr mapping_end = Common::AlignDown((address) + size, PageSize);
1229 const size_t mapping_size = (mapping_start < mapping_end) ? mapping_end - mapping_start : 0;
1230 m_resource_limit->Release(LimitableResource::PhysicalMemoryMax, aligned_size - mapping_size);
1231
1232 R_SUCCEED();
1233}
1234
1235Result KPageTable::CleanupForIpcClient(VAddr address, size_t size, KMemoryState dst_state) {
1236 // Validate the address.
1237 R_UNLESS(this->Contains(address, size), ResultInvalidCurrentMemory);
1238
1239 // Get aligned source extents.
1240 const VAddr mapping_start = Common::AlignUp((address), PageSize);
1241 const VAddr mapping_end = Common::AlignDown((address) + size, PageSize);
1242 const VAddr mapping_last = mapping_end - 1;
1243 const size_t mapping_size = (mapping_start < mapping_end) ? (mapping_end - mapping_start) : 0;
1244
1245 // If nothing was mapped, we're actually done immediately.
1246 R_SUCCEED_IF(mapping_size == 0);
1247
1248 // Get the test state and attribute mask.
1249 KMemoryState test_state;
1250 KMemoryAttribute test_attr_mask;
1251 switch (dst_state) {
1252 case KMemoryState::Ipc:
1253 test_state = KMemoryState::FlagCanUseIpc;
1254 test_attr_mask =
1255 KMemoryAttribute::Uncached | KMemoryAttribute::DeviceShared | KMemoryAttribute::Locked;
1256 break;
1257 case KMemoryState::NonSecureIpc:
1258 test_state = KMemoryState::FlagCanUseNonSecureIpc;
1259 test_attr_mask = KMemoryAttribute::Uncached | KMemoryAttribute::Locked;
1260 break;
1261 case KMemoryState::NonDeviceIpc:
1262 test_state = KMemoryState::FlagCanUseNonDeviceIpc;
1263 test_attr_mask = KMemoryAttribute::Uncached | KMemoryAttribute::Locked;
1264 break;
1265 default:
1266 R_THROW(ResultInvalidCombination);
1267 }
1268
1269 // Lock the table.
1270 // NOTE: Nintendo does this *after* creating the updater below, but this does not follow
1271 // convention elsewhere in KPageTable.
1272 KScopedLightLock lk(m_general_lock);
1273
1274 // We're going to perform an update, so create a helper.
1275 KScopedPageTableUpdater updater(this);
1276
1277 // Ensure that on failure, we roll back appropriately.
1278 size_t mapped_size = 0;
1279 ON_RESULT_FAILURE {
1280 if (mapped_size > 0) {
1281 // Determine where the mapping ends.
1282 const auto mapped_end = (mapping_start) + mapped_size;
1283 const auto mapped_last = mapped_end - 1;
1284
1285 // Get current and next iterators.
1286 KMemoryBlockManager::const_iterator start_it =
1287 m_memory_block_manager.FindIterator(mapping_start);
1288 KMemoryBlockManager::const_iterator next_it = start_it;
1289 ++next_it;
1290
1291 // Get the current block info.
1292 KMemoryInfo cur_info = start_it->GetMemoryInfo();
1293
1294 // Create tracking variables.
1295 VAddr cur_address = cur_info.GetAddress();
1296 size_t cur_size = cur_info.GetSize();
1297 bool cur_perm_eq = cur_info.GetPermission() == cur_info.GetOriginalPermission();
1298 bool cur_needs_set_perm = !cur_perm_eq && cur_info.GetIpcLockCount() == 1;
1299 bool first =
1300 cur_info.GetIpcDisableMergeCount() == 1 &&
1301 (cur_info.GetDisableMergeAttribute() & KMemoryBlockDisableMergeAttribute::Locked) ==
1302 KMemoryBlockDisableMergeAttribute::None;
1303
1304 while (((cur_address) + cur_size - 1) < mapped_last) {
1305 // Check that we have a next block.
1306 ASSERT(next_it != m_memory_block_manager.end());
1307
1308 // Get the next info.
1309 const KMemoryInfo next_info = next_it->GetMemoryInfo();
1310
1311 // Check if we can consolidate the next block's permission set with the current one.
1312
1313 const bool next_perm_eq =
1314 next_info.GetPermission() == next_info.GetOriginalPermission();
1315 const bool next_needs_set_perm = !next_perm_eq && next_info.GetIpcLockCount() == 1;
1316 if (cur_perm_eq == next_perm_eq && cur_needs_set_perm == next_needs_set_perm &&
1317 cur_info.GetOriginalPermission() == next_info.GetOriginalPermission()) {
1318 // We can consolidate the reprotection for the current and next block into a
1319 // single call.
1320 cur_size += next_info.GetSize();
1321 } else {
1322 // We have to operate on the current block.
1323 if ((cur_needs_set_perm || first) && !cur_perm_eq) {
1324 ASSERT(Operate(cur_address, cur_size / PageSize, cur_info.GetPermission(),
1325 OperationType::ChangePermissions)
1326 .IsSuccess());
1327 }
1328
1329 // Advance.
1330 cur_address = next_info.GetAddress();
1331 cur_size = next_info.GetSize();
1332 first = false;
1333 }
1334
1335 // Advance.
1336 cur_info = next_info;
1337 cur_perm_eq = next_perm_eq;
1338 cur_needs_set_perm = next_needs_set_perm;
1339 ++next_it;
1340 }
1341
1342 // Process the last block.
1343 if ((first || cur_needs_set_perm) && !cur_perm_eq) {
1344 ASSERT(Operate(cur_address, cur_size / PageSize, cur_info.GetPermission(),
1345 OperationType::ChangePermissions)
1346 .IsSuccess());
1347 }
1348 }
1349 };
1350
1351 // Iterate, reprotecting as needed.
1352 {
1353 // Get current and next iterators.
1354 KMemoryBlockManager::const_iterator start_it =
1355 m_memory_block_manager.FindIterator(mapping_start);
1356 KMemoryBlockManager::const_iterator next_it = start_it;
1357 ++next_it;
1358
1359 // Validate the current block.
1360 KMemoryInfo cur_info = start_it->GetMemoryInfo();
1361 ASSERT(this->CheckMemoryState(cur_info, test_state, test_state, KMemoryPermission::None,
1362 KMemoryPermission::None,
1363 test_attr_mask | KMemoryAttribute::IpcLocked,
1364 KMemoryAttribute::IpcLocked)
1365 .IsSuccess());
1366
1367 // Create tracking variables.
1368 VAddr cur_address = cur_info.GetAddress();
1369 size_t cur_size = cur_info.GetSize();
1370 bool cur_perm_eq = cur_info.GetPermission() == cur_info.GetOriginalPermission();
1371 bool cur_needs_set_perm = !cur_perm_eq && cur_info.GetIpcLockCount() == 1;
1372 bool first =
1373 cur_info.GetIpcDisableMergeCount() == 1 &&
1374 (cur_info.GetDisableMergeAttribute() & KMemoryBlockDisableMergeAttribute::Locked) ==
1375 KMemoryBlockDisableMergeAttribute::None;
1376
1377 while ((cur_address + cur_size - 1) < mapping_last) {
1378 // Check that we have a next block.
1379 ASSERT(next_it != m_memory_block_manager.end());
1380
1381 // Get the next info.
1382 const KMemoryInfo next_info = next_it->GetMemoryInfo();
1383
1384 // Validate the next block.
1385 ASSERT(this->CheckMemoryState(next_info, test_state, test_state,
1386 KMemoryPermission::None, KMemoryPermission::None,
1387 test_attr_mask | KMemoryAttribute::IpcLocked,
1388 KMemoryAttribute::IpcLocked)
1389 .IsSuccess());
1390
1391 // Check if we can consolidate the next block's permission set with the current one.
1392 const bool next_perm_eq =
1393 next_info.GetPermission() == next_info.GetOriginalPermission();
1394 const bool next_needs_set_perm = !next_perm_eq && next_info.GetIpcLockCount() == 1;
1395 if (cur_perm_eq == next_perm_eq && cur_needs_set_perm == next_needs_set_perm &&
1396 cur_info.GetOriginalPermission() == next_info.GetOriginalPermission()) {
1397 // We can consolidate the reprotection for the current and next block into a single
1398 // call.
1399 cur_size += next_info.GetSize();
1400 } else {
1401 // We have to operate on the current block.
1402 if ((cur_needs_set_perm || first) && !cur_perm_eq) {
1403 R_TRY(Operate(cur_address, cur_size / PageSize,
1404 cur_needs_set_perm ? cur_info.GetOriginalPermission()
1405 : cur_info.GetPermission(),
1406 OperationType::ChangePermissions));
1407 }
1408
1409 // Mark that we mapped the block.
1410 mapped_size += cur_size;
1411
1412 // Advance.
1413 cur_address = next_info.GetAddress();
1414 cur_size = next_info.GetSize();
1415 first = false;
1416 }
1417
1418 // Advance.
1419 cur_info = next_info;
1420 cur_perm_eq = next_perm_eq;
1421 cur_needs_set_perm = next_needs_set_perm;
1422 ++next_it;
1423 }
1424
1425 // Process the last block.
1426 const auto lock_count =
1427 cur_info.GetIpcLockCount() +
1428 (next_it != m_memory_block_manager.end()
1429 ? (next_it->GetIpcDisableMergeCount() - next_it->GetIpcLockCount())
1430 : 0);
1431 if ((first || cur_needs_set_perm || (lock_count == 1)) && !cur_perm_eq) {
1432 R_TRY(Operate(cur_address, cur_size / PageSize,
1433 cur_needs_set_perm ? cur_info.GetOriginalPermission()
1434 : cur_info.GetPermission(),
1435 OperationType::ChangePermissions));
1436 }
1437 }
1438
1439 // Create an update allocator.
1440 // NOTE: Guaranteed zero blocks needed here.
1441 Result allocator_result;
1442 KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result),
1443 m_memory_block_slab_manager, 0);
1444 R_TRY(allocator_result);
1445
1446 // Unlock the pages.
1447 m_memory_block_manager.UpdateLock(std::addressof(allocator), mapping_start,
1448 mapping_size / PageSize, &KMemoryBlock::UnlockForIpc,
1449 KMemoryPermission::None);
1450
1451 R_SUCCEED();
1452}
1453
1454void KPageTable::CleanupForIpcClientOnServerSetupFailure([[maybe_unused]] PageLinkedList* page_list,
1455 VAddr address, size_t size,
1456 KMemoryPermission prot_perm) {
1457 ASSERT(this->IsLockedByCurrentThread());
1458 ASSERT(Common::IsAligned(address, PageSize));
1459 ASSERT(Common::IsAligned(size, PageSize));
1460
1461 // Get the mapped extents.
1462 const VAddr src_map_start = address;
1463 const VAddr src_map_end = address + size;
1464 const VAddr src_map_last = src_map_end - 1;
1465
1466 // This function is only invoked when there's something to do.
1467 ASSERT(src_map_end > src_map_start);
1468
1469 // Iterate over blocks, fixing permissions.
1470 KMemoryBlockManager::const_iterator it = m_memory_block_manager.FindIterator(address);
1471 while (true) {
1472 const KMemoryInfo info = it->GetMemoryInfo();
1473
1474 const auto cur_start =
1475 info.GetAddress() >= src_map_start ? info.GetAddress() : src_map_start;
1476 const auto cur_end =
1477 src_map_last <= info.GetLastAddress() ? src_map_end : info.GetEndAddress();
1478
1479 // If we can, fix the protections on the block.
1480 if ((info.GetIpcLockCount() == 0 &&
1481 (info.GetPermission() & KMemoryPermission::IpcLockChangeMask) != prot_perm) ||
1482 (info.GetIpcLockCount() != 0 &&
1483 (info.GetOriginalPermission() & KMemoryPermission::IpcLockChangeMask) != prot_perm)) {
1484 // Check if we actually need to fix the protections on the block.
1485 if (cur_end == src_map_end || info.GetAddress() <= src_map_start ||
1486 (info.GetPermission() & KMemoryPermission::IpcLockChangeMask) != prot_perm) {
1487 ASSERT(Operate(cur_start, (cur_end - cur_start) / PageSize, info.GetPermission(),
1488 OperationType::ChangePermissions)
1489 .IsSuccess());
1490 }
1491 }
1492
1493 // If we're past the end of the region, we're done.
1494 if (src_map_last <= info.GetLastAddress()) {
1495 break;
1496 }
1497
1498 // Advance.
1499 ++it;
1500 ASSERT(it != m_memory_block_manager.end());
1501 }
1502}
1503
1504void KPageTable::HACK_OpenPages(PAddr phys_addr, size_t num_pages) {
1505 m_system.Kernel().MemoryManager().OpenFirst(phys_addr, num_pages);
1506}
1507
1508void KPageTable::HACK_ClosePages(VAddr virt_addr, size_t num_pages) {
1509 for (size_t index = 0; index < num_pages; ++index) {
1510 const auto paddr = GetPhysicalAddr(virt_addr + (index * PageSize));
1511 m_system.Kernel().MemoryManager().Close(paddr, 1);
1512 }
1513}
1514
689Result KPageTable::MapPhysicalMemory(VAddr address, size_t size) { 1515Result KPageTable::MapPhysicalMemory(VAddr address, size_t size) {
690 // Lock the physical memory lock. 1516 // Lock the physical memory lock.
691 KScopedLightLock map_phys_mem_lk(m_map_physical_memory_lock); 1517 KScopedLightLock phys_lk(m_map_physical_memory_lock);
692 1518
693 // Calculate the last address for convenience. 1519 // Calculate the last address for convenience.
694 const VAddr last_address = address + size - 1; 1520 const VAddr last_address = address + size - 1;
@@ -742,15 +1568,19 @@ Result KPageTable::MapPhysicalMemory(VAddr address, size_t size) {
742 { 1568 {
743 // Reserve the memory from the process resource limit. 1569 // Reserve the memory from the process resource limit.
744 KScopedResourceReservation memory_reservation( 1570 KScopedResourceReservation memory_reservation(
745 m_system.Kernel().CurrentProcess()->GetResourceLimit(), 1571 m_resource_limit, LimitableResource::PhysicalMemoryMax, size - mapped_size);
746 LimitableResource::PhysicalMemory, size - mapped_size);
747 R_UNLESS(memory_reservation.Succeeded(), ResultLimitReached); 1572 R_UNLESS(memory_reservation.Succeeded(), ResultLimitReached);
748 1573
749 // Allocate pages for the new memory. 1574 // Allocate pages for the new memory.
750 KPageGroup pg; 1575 KPageGroup pg;
751 R_TRY(m_system.Kernel().MemoryManager().AllocateAndOpenForProcess( 1576 R_TRY(m_system.Kernel().MemoryManager().AllocateForProcess(
752 &pg, (size - mapped_size) / PageSize, 1577 &pg, (size - mapped_size) / PageSize, m_allocate_option, 0, 0));
753 KMemoryManager::EncodeOption(m_memory_pool, m_allocation_option), 0, 0)); 1578
1579 // If we fail in the next bit (or retry), we need to cleanup the pages.
1580 // auto pg_guard = SCOPE_GUARD {
1581 // pg.OpenFirst();
1582 // pg.Close();
1583 //};
754 1584
755 // Map the memory. 1585 // Map the memory.
756 { 1586 {
@@ -810,15 +1640,24 @@ Result KPageTable::MapPhysicalMemory(VAddr address, size_t size) {
810 1640
811 // Create an update allocator. 1641 // Create an update allocator.
812 ASSERT(num_allocator_blocks <= KMemoryBlockManagerUpdateAllocator::MaxBlocks); 1642 ASSERT(num_allocator_blocks <= KMemoryBlockManagerUpdateAllocator::MaxBlocks);
813 Result allocator_result{ResultSuccess}; 1643 Result allocator_result;
814 KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result), 1644 KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result),
815 m_memory_block_slab_manager, 1645 m_memory_block_slab_manager,
816 num_allocator_blocks); 1646 num_allocator_blocks);
817 R_TRY(allocator_result); 1647 R_TRY(allocator_result);
818 1648
1649 // We're going to perform an update, so create a helper.
1650 KScopedPageTableUpdater updater(this);
1651
1652 // Prepare to iterate over the memory.
1653 auto pg_it = pg.Nodes().begin();
1654 PAddr pg_phys_addr = pg_it->GetAddress();
1655 size_t pg_pages = pg_it->GetNumPages();
1656
819 // Reset the current tracking address, and make sure we clean up on failure. 1657 // Reset the current tracking address, and make sure we clean up on failure.
1658 // pg_guard.Cancel();
820 cur_address = address; 1659 cur_address = address;
821 auto unmap_guard = detail::ScopeExit([&] { 1660 ON_RESULT_FAILURE {
822 if (cur_address > address) { 1661 if (cur_address > address) {
823 const VAddr last_unmap_address = cur_address - 1; 1662 const VAddr last_unmap_address = cur_address - 1;
824 1663
@@ -841,6 +1680,9 @@ Result KPageTable::MapPhysicalMemory(VAddr address, size_t size) {
841 last_unmap_address + 1 - cur_address) / 1680 last_unmap_address + 1 - cur_address) /
842 PageSize; 1681 PageSize;
843 1682
1683 // HACK: Manually close the pages.
1684 HACK_ClosePages(cur_address, cur_pages);
1685
844 // Unmap. 1686 // Unmap.
845 ASSERT(Operate(cur_address, cur_pages, KMemoryPermission::None, 1687 ASSERT(Operate(cur_address, cur_pages, KMemoryPermission::None,
846 OperationType::Unmap) 1688 OperationType::Unmap)
@@ -857,12 +1699,17 @@ Result KPageTable::MapPhysicalMemory(VAddr address, size_t size) {
857 ++it; 1699 ++it;
858 } 1700 }
859 } 1701 }
860 });
861 1702
862 // Iterate over the memory. 1703 // Release any remaining unmapped memory.
863 auto pg_it = pg.Nodes().begin(); 1704 m_system.Kernel().MemoryManager().OpenFirst(pg_phys_addr, pg_pages);
864 PAddr pg_phys_addr = pg_it->GetAddress(); 1705 m_system.Kernel().MemoryManager().Close(pg_phys_addr, pg_pages);
865 size_t pg_pages = pg_it->GetNumPages(); 1706 for (++pg_it; pg_it != pg.Nodes().end(); ++pg_it) {
1707 m_system.Kernel().MemoryManager().OpenFirst(pg_it->GetAddress(),
1708 pg_it->GetNumPages());
1709 m_system.Kernel().MemoryManager().Close(pg_it->GetAddress(),
1710 pg_it->GetNumPages());
1711 }
1712 };
866 1713
867 auto it = m_memory_block_manager.FindIterator(cur_address); 1714 auto it = m_memory_block_manager.FindIterator(cur_address);
868 while (true) { 1715 while (true) {
@@ -897,6 +1744,9 @@ Result KPageTable::MapPhysicalMemory(VAddr address, size_t size) {
897 R_TRY(Operate(cur_address, cur_pages, KMemoryPermission::UserReadWrite, 1744 R_TRY(Operate(cur_address, cur_pages, KMemoryPermission::UserReadWrite,
898 OperationType::Map, pg_phys_addr)); 1745 OperationType::Map, pg_phys_addr));
899 1746
1747 // HACK: Manually open the pages.
1748 HACK_OpenPages(pg_phys_addr, cur_pages);
1749
900 // Advance. 1750 // Advance.
901 cur_address += cur_pages * PageSize; 1751 cur_address += cur_pages * PageSize;
902 map_pages -= cur_pages; 1752 map_pages -= cur_pages;
@@ -928,9 +1778,6 @@ Result KPageTable::MapPhysicalMemory(VAddr address, size_t size) {
928 KMemoryPermission::None, KMemoryAttribute::None, KMemoryState::Normal, 1778 KMemoryPermission::None, KMemoryAttribute::None, KMemoryState::Normal,
929 KMemoryPermission::UserReadWrite, KMemoryAttribute::None); 1779 KMemoryPermission::UserReadWrite, KMemoryAttribute::None);
930 1780
931 // Cancel our guard.
932 unmap_guard.Cancel();
933
934 R_SUCCEED(); 1781 R_SUCCEED();
935 } 1782 }
936 } 1783 }
@@ -939,7 +1786,7 @@ Result KPageTable::MapPhysicalMemory(VAddr address, size_t size) {
939 1786
940Result KPageTable::UnmapPhysicalMemory(VAddr address, size_t size) { 1787Result KPageTable::UnmapPhysicalMemory(VAddr address, size_t size) {
941 // Lock the physical memory lock. 1788 // Lock the physical memory lock.
942 KScopedLightLock map_phys_mem_lk(m_map_physical_memory_lock); 1789 KScopedLightLock phys_lk(m_map_physical_memory_lock);
943 1790
944 // Lock the table. 1791 // Lock the table.
945 KScopedLightLock lk(m_general_lock); 1792 KScopedLightLock lk(m_general_lock);
@@ -948,8 +1795,11 @@ Result KPageTable::UnmapPhysicalMemory(VAddr address, size_t size) {
948 const VAddr last_address = address + size - 1; 1795 const VAddr last_address = address + size - 1;
949 1796
950 // Define iteration variables. 1797 // Define iteration variables.
951 VAddr cur_address = 0; 1798 VAddr map_start_address = 0;
952 size_t mapped_size = 0; 1799 VAddr map_last_address = 0;
1800
1801 VAddr cur_address;
1802 size_t mapped_size;
953 size_t num_allocator_blocks = 0; 1803 size_t num_allocator_blocks = 0;
954 1804
955 // Check if the memory is mapped. 1805 // Check if the memory is mapped.
@@ -975,27 +1825,27 @@ Result KPageTable::UnmapPhysicalMemory(VAddr address, size_t size) {
975 if (is_normal) { 1825 if (is_normal) {
976 R_UNLESS(info.GetAttribute() == KMemoryAttribute::None, ResultInvalidCurrentMemory); 1826 R_UNLESS(info.GetAttribute() == KMemoryAttribute::None, ResultInvalidCurrentMemory);
977 1827
1828 if (map_start_address == 0) {
1829 map_start_address = cur_address;
1830 }
1831 map_last_address =
1832 (last_address >= info.GetLastAddress()) ? info.GetLastAddress() : last_address;
1833
978 if (info.GetAddress() < address) { 1834 if (info.GetAddress() < address) {
979 ++num_allocator_blocks; 1835 ++num_allocator_blocks;
980 } 1836 }
981 if (last_address < info.GetLastAddress()) { 1837 if (last_address < info.GetLastAddress()) {
982 ++num_allocator_blocks; 1838 ++num_allocator_blocks;
983 } 1839 }
1840
1841 mapped_size += (map_last_address + 1 - cur_address);
984 } 1842 }
985 1843
986 // Check if we're done. 1844 // Check if we're done.
987 if (last_address <= info.GetLastAddress()) { 1845 if (last_address <= info.GetLastAddress()) {
988 if (is_normal) {
989 mapped_size += (last_address + 1 - cur_address);
990 }
991 break; 1846 break;
992 } 1847 }
993 1848
994 // Track the memory if it's mapped.
995 if (is_normal) {
996 mapped_size += VAddr(info.GetEndAddress()) - cur_address;
997 }
998
999 // Advance. 1849 // Advance.
1000 cur_address = info.GetEndAddress(); 1850 cur_address = info.GetEndAddress();
1001 ++it; 1851 ++it;
@@ -1005,125 +1855,22 @@ Result KPageTable::UnmapPhysicalMemory(VAddr address, size_t size) {
1005 R_SUCCEED_IF(mapped_size == 0); 1855 R_SUCCEED_IF(mapped_size == 0);
1006 } 1856 }
1007 1857
1008 // Make a page group for the unmap region.
1009 KPageGroup pg;
1010 {
1011 auto& impl = this->PageTableImpl();
1012
1013 // Begin traversal.
1014 Common::PageTable::TraversalContext context;
1015 Common::PageTable::TraversalEntry cur_entry = {.phys_addr = 0, .block_size = 0};
1016 bool cur_valid = false;
1017 Common::PageTable::TraversalEntry next_entry;
1018 bool next_valid = false;
1019 size_t tot_size = 0;
1020
1021 cur_address = address;
1022 next_valid = impl.BeginTraversal(next_entry, context, cur_address);
1023 next_entry.block_size =
1024 (next_entry.block_size - (next_entry.phys_addr & (next_entry.block_size - 1)));
1025
1026 // Iterate, building the group.
1027 while (true) {
1028 if ((!next_valid && !cur_valid) ||
1029 (next_valid && cur_valid &&
1030 next_entry.phys_addr == cur_entry.phys_addr + cur_entry.block_size)) {
1031 cur_entry.block_size += next_entry.block_size;
1032 } else {
1033 if (cur_valid) {
1034 // ASSERT(IsHeapPhysicalAddress(cur_entry.phys_addr));
1035 R_TRY(pg.AddBlock(cur_entry.phys_addr, cur_entry.block_size / PageSize));
1036 }
1037
1038 // Update tracking variables.
1039 tot_size += cur_entry.block_size;
1040 cur_entry = next_entry;
1041 cur_valid = next_valid;
1042 }
1043
1044 if (cur_entry.block_size + tot_size >= size) {
1045 break;
1046 }
1047
1048 next_valid = impl.ContinueTraversal(next_entry, context);
1049 }
1050
1051 // Add the last block.
1052 if (cur_valid) {
1053 // ASSERT(IsHeapPhysicalAddress(cur_entry.phys_addr));
1054 R_TRY(pg.AddBlock(cur_entry.phys_addr, (size - tot_size) / PageSize));
1055 }
1056 }
1057 ASSERT(pg.GetNumPages() == mapped_size / PageSize);
1058
1059 // Create an update allocator. 1858 // Create an update allocator.
1060 ASSERT(num_allocator_blocks <= KMemoryBlockManagerUpdateAllocator::MaxBlocks); 1859 ASSERT(num_allocator_blocks <= KMemoryBlockManagerUpdateAllocator::MaxBlocks);
1061 Result allocator_result{ResultSuccess}; 1860 Result allocator_result;
1062 KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result), 1861 KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result),
1063 m_memory_block_slab_manager, num_allocator_blocks); 1862 m_memory_block_slab_manager, num_allocator_blocks);
1064 R_TRY(allocator_result); 1863 R_TRY(allocator_result);
1065 1864
1066 // Reset the current tracking address, and make sure we clean up on failure. 1865 // We're going to perform an update, so create a helper.
1067 cur_address = address; 1866 KScopedPageTableUpdater updater(this);
1068 auto remap_guard = detail::ScopeExit([&] {
1069 if (cur_address > address) {
1070 const VAddr last_map_address = cur_address - 1;
1071 cur_address = address;
1072
1073 // Iterate over the memory we unmapped.
1074 auto it = m_memory_block_manager.FindIterator(cur_address);
1075 auto pg_it = pg.Nodes().begin();
1076 PAddr pg_phys_addr = pg_it->GetAddress();
1077 size_t pg_pages = pg_it->GetNumPages();
1078
1079 while (true) {
1080 // Get the memory info for the pages we unmapped, convert to property.
1081 const KMemoryInfo info = it->GetMemoryInfo();
1082
1083 // If the memory is normal, we unmapped it and need to re-map it.
1084 if (info.GetState() == KMemoryState::Normal) {
1085 // Determine the range to map.
1086 size_t map_pages = std::min(VAddr(info.GetEndAddress()) - cur_address,
1087 last_map_address + 1 - cur_address) /
1088 PageSize;
1089
1090 // While we have pages to map, map them.
1091 while (map_pages > 0) {
1092 // Check if we're at the end of the physical block.
1093 if (pg_pages == 0) {
1094 // Ensure there are more pages to map.
1095 ASSERT(pg_it != pg.Nodes().end());
1096
1097 // Advance our physical block.
1098 ++pg_it;
1099 pg_phys_addr = pg_it->GetAddress();
1100 pg_pages = pg_it->GetNumPages();
1101 }
1102
1103 // Map whatever we can.
1104 const size_t cur_pages = std::min(pg_pages, map_pages);
1105 ASSERT(this->Operate(cur_address, cur_pages, info.GetPermission(),
1106 OperationType::Map, pg_phys_addr) == ResultSuccess);
1107 1867
1108 // Advance. 1868 // Separate the mapping.
1109 cur_address += cur_pages * PageSize; 1869 R_TRY(Operate(map_start_address, (map_last_address + 1 - map_start_address) / PageSize,
1110 map_pages -= cur_pages; 1870 KMemoryPermission::None, OperationType::Separate));
1111 1871
1112 pg_phys_addr += cur_pages * PageSize; 1872 // Reset the current tracking address, and make sure we clean up on failure.
1113 pg_pages -= cur_pages; 1873 cur_address = address;
1114 }
1115 }
1116
1117 // Check if we're done.
1118 if (last_map_address <= info.GetLastAddress()) {
1119 break;
1120 }
1121
1122 // Advance.
1123 ++it;
1124 }
1125 }
1126 });
1127 1874
1128 // Iterate over the memory, unmapping as we go. 1875 // Iterate over the memory, unmapping as we go.
1129 auto it = m_memory_block_manager.FindIterator(cur_address); 1876 auto it = m_memory_block_manager.FindIterator(cur_address);
@@ -1141,8 +1888,12 @@ Result KPageTable::UnmapPhysicalMemory(VAddr address, size_t size) {
1141 last_address + 1 - cur_address) / 1888 last_address + 1 - cur_address) /
1142 PageSize; 1889 PageSize;
1143 1890
1891 // HACK: Manually close the pages.
1892 HACK_ClosePages(cur_address, cur_pages);
1893
1144 // Unmap. 1894 // Unmap.
1145 R_TRY(Operate(cur_address, cur_pages, KMemoryPermission::None, OperationType::Unmap)); 1895 ASSERT(Operate(cur_address, cur_pages, KMemoryPermission::None, OperationType::Unmap)
1896 .IsSuccess());
1146 } 1897 }
1147 1898
1148 // Check if we're done. 1899 // Check if we're done.
@@ -1157,8 +1908,7 @@ Result KPageTable::UnmapPhysicalMemory(VAddr address, size_t size) {
1157 1908
1158 // Release the memory resource. 1909 // Release the memory resource.
1159 m_mapped_physical_memory_size -= mapped_size; 1910 m_mapped_physical_memory_size -= mapped_size;
1160 auto process{m_system.Kernel().CurrentProcess()}; 1911 m_resource_limit->Release(LimitableResource::PhysicalMemoryMax, mapped_size);
1161 process->GetResourceLimit()->Release(LimitableResource::PhysicalMemory, mapped_size);
1162 1912
1163 // Update memory blocks. 1913 // Update memory blocks.
1164 m_memory_block_manager.Update(std::addressof(allocator), address, size / PageSize, 1914 m_memory_block_manager.Update(std::addressof(allocator), address, size / PageSize,
@@ -1166,14 +1916,7 @@ Result KPageTable::UnmapPhysicalMemory(VAddr address, size_t size) {
1166 KMemoryAttribute::None, KMemoryBlockDisableMergeAttribute::None, 1916 KMemoryAttribute::None, KMemoryBlockDisableMergeAttribute::None,
1167 KMemoryBlockDisableMergeAttribute::None); 1917 KMemoryBlockDisableMergeAttribute::None);
1168 1918
1169 // TODO(bunnei): This is a workaround until the next set of changes, where we add reference
1170 // counting for mapped pages. Until then, we must manually close the reference to the page
1171 // group.
1172 m_system.Kernel().MemoryManager().Close(pg);
1173
1174 // We succeeded. 1919 // We succeeded.
1175 remap_guard.Cancel();
1176
1177 R_SUCCEED(); 1920 R_SUCCEED();
1178} 1921}
1179 1922
@@ -1749,8 +2492,7 @@ Result KPageTable::SetHeapSize(VAddr* out, size_t size) {
1749 OperationType::Unmap)); 2492 OperationType::Unmap));
1750 2493
1751 // Release the memory from the resource limit. 2494 // Release the memory from the resource limit.
1752 m_system.Kernel().CurrentProcess()->GetResourceLimit()->Release( 2495 m_resource_limit->Release(LimitableResource::PhysicalMemoryMax, num_pages * PageSize);
1753 LimitableResource::PhysicalMemory, num_pages * PageSize);
1754 2496
1755 // Apply the memory block update. 2497 // Apply the memory block update.
1756 m_memory_block_manager.Update(std::addressof(allocator), m_heap_region_start + size, 2498 m_memory_block_manager.Update(std::addressof(allocator), m_heap_region_start + size,
@@ -1780,8 +2522,7 @@ Result KPageTable::SetHeapSize(VAddr* out, size_t size) {
1780 2522
1781 // Reserve memory for the heap extension. 2523 // Reserve memory for the heap extension.
1782 KScopedResourceReservation memory_reservation( 2524 KScopedResourceReservation memory_reservation(
1783 m_system.Kernel().CurrentProcess()->GetResourceLimit(), LimitableResource::PhysicalMemory, 2525 m_resource_limit, LimitableResource::PhysicalMemoryMax, allocation_size);
1784 allocation_size);
1785 R_UNLESS(memory_reservation.Succeeded(), ResultLimitReached); 2526 R_UNLESS(memory_reservation.Succeeded(), ResultLimitReached);
1786 2527
1787 // Allocate pages for the heap extension. 2528 // Allocate pages for the heap extension.
@@ -1869,7 +2610,7 @@ ResultVal<VAddr> KPageTable::AllocateAndMapMemory(size_t needed_num_pages, size_
1869 R_TRY(Operate(addr, needed_num_pages, perm, OperationType::Map, map_addr)); 2610 R_TRY(Operate(addr, needed_num_pages, perm, OperationType::Map, map_addr));
1870 } else { 2611 } else {
1871 KPageGroup page_group; 2612 KPageGroup page_group;
1872 R_TRY(m_system.Kernel().MemoryManager().AllocateAndOpenForProcess( 2613 R_TRY(m_system.Kernel().MemoryManager().AllocateForProcess(
1873 &page_group, needed_num_pages, 2614 &page_group, needed_num_pages,
1874 KMemoryManager::EncodeOption(m_memory_pool, m_allocation_option), 0, 0)); 2615 KMemoryManager::EncodeOption(m_memory_pool, m_allocation_option), 0, 0));
1875 R_TRY(Operate(addr, needed_num_pages, page_group, OperationType::MapGroup)); 2616 R_TRY(Operate(addr, needed_num_pages, page_group, OperationType::MapGroup));
@@ -1883,8 +2624,9 @@ ResultVal<VAddr> KPageTable::AllocateAndMapMemory(size_t needed_num_pages, size_
1883 return addr; 2624 return addr;
1884} 2625}
1885 2626
1886Result KPageTable::LockForMapDeviceAddressSpace(VAddr address, size_t size, KMemoryPermission perm, 2627Result KPageTable::LockForMapDeviceAddressSpace(bool* out_is_io, VAddr address, size_t size,
1887 bool is_aligned) { 2628 KMemoryPermission perm, bool is_aligned,
2629 bool check_heap) {
1888 // Lightly validate the range before doing anything else. 2630 // Lightly validate the range before doing anything else.
1889 const size_t num_pages = size / PageSize; 2631 const size_t num_pages = size / PageSize;
1890 R_UNLESS(this->Contains(address, size), ResultInvalidCurrentMemory); 2632 R_UNLESS(this->Contains(address, size), ResultInvalidCurrentMemory);
@@ -1894,15 +2636,18 @@ Result KPageTable::LockForMapDeviceAddressSpace(VAddr address, size_t size, KMem
1894 2636
1895 // Check the memory state. 2637 // Check the memory state.
1896 const auto test_state = 2638 const auto test_state =
1897 (is_aligned ? KMemoryState::FlagCanAlignedDeviceMap : KMemoryState::FlagCanDeviceMap); 2639 (is_aligned ? KMemoryState::FlagCanAlignedDeviceMap : KMemoryState::FlagCanDeviceMap) |
2640 (check_heap ? KMemoryState::FlagReferenceCounted : KMemoryState::None);
1898 size_t num_allocator_blocks; 2641 size_t num_allocator_blocks;
1899 R_TRY(this->CheckMemoryState(std::addressof(num_allocator_blocks), address, size, test_state, 2642 KMemoryState old_state;
2643 R_TRY(this->CheckMemoryState(std::addressof(old_state), nullptr, nullptr,
2644 std::addressof(num_allocator_blocks), address, size, test_state,
1900 test_state, perm, perm, 2645 test_state, perm, perm,
1901 KMemoryAttribute::IpcLocked | KMemoryAttribute::Locked, 2646 KMemoryAttribute::IpcLocked | KMemoryAttribute::Locked,
1902 KMemoryAttribute::None, KMemoryAttribute::DeviceShared)); 2647 KMemoryAttribute::None, KMemoryAttribute::DeviceShared));
1903 2648
1904 // Create an update allocator. 2649 // Create an update allocator.
1905 Result allocator_result{ResultSuccess}; 2650 Result allocator_result;
1906 KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result), 2651 KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result),
1907 m_memory_block_slab_manager, num_allocator_blocks); 2652 m_memory_block_slab_manager, num_allocator_blocks);
1908 R_TRY(allocator_result); 2653 R_TRY(allocator_result);
@@ -1911,10 +2656,13 @@ Result KPageTable::LockForMapDeviceAddressSpace(VAddr address, size_t size, KMem
1911 m_memory_block_manager.UpdateLock(std::addressof(allocator), address, num_pages, 2656 m_memory_block_manager.UpdateLock(std::addressof(allocator), address, num_pages,
1912 &KMemoryBlock::ShareToDevice, KMemoryPermission::None); 2657 &KMemoryBlock::ShareToDevice, KMemoryPermission::None);
1913 2658
2659 // Set whether the locked memory was io.
2660 *out_is_io = old_state == KMemoryState::Io;
2661
1914 R_SUCCEED(); 2662 R_SUCCEED();
1915} 2663}
1916 2664
1917Result KPageTable::LockForUnmapDeviceAddressSpace(VAddr address, size_t size) { 2665Result KPageTable::LockForUnmapDeviceAddressSpace(VAddr address, size_t size, bool check_heap) {
1918 // Lightly validate the range before doing anything else. 2666 // Lightly validate the range before doing anything else.
1919 const size_t num_pages = size / PageSize; 2667 const size_t num_pages = size / PageSize;
1920 R_UNLESS(this->Contains(address, size), ResultInvalidCurrentMemory); 2668 R_UNLESS(this->Contains(address, size), ResultInvalidCurrentMemory);
@@ -1923,16 +2671,16 @@ Result KPageTable::LockForUnmapDeviceAddressSpace(VAddr address, size_t size) {
1923 KScopedLightLock lk(m_general_lock); 2671 KScopedLightLock lk(m_general_lock);
1924 2672
1925 // Check the memory state. 2673 // Check the memory state.
2674 const auto test_state = KMemoryState::FlagCanDeviceMap |
2675 (check_heap ? KMemoryState::FlagReferenceCounted : KMemoryState::None);
1926 size_t num_allocator_blocks; 2676 size_t num_allocator_blocks;
1927 R_TRY(this->CheckMemoryStateContiguous( 2677 R_TRY(this->CheckMemoryStateContiguous(
1928 std::addressof(num_allocator_blocks), address, size, 2678 std::addressof(num_allocator_blocks), address, size, test_state, test_state,
1929 KMemoryState::FlagReferenceCounted | KMemoryState::FlagCanDeviceMap,
1930 KMemoryState::FlagReferenceCounted | KMemoryState::FlagCanDeviceMap,
1931 KMemoryPermission::None, KMemoryPermission::None, 2679 KMemoryPermission::None, KMemoryPermission::None,
1932 KMemoryAttribute::DeviceShared | KMemoryAttribute::Locked, KMemoryAttribute::DeviceShared)); 2680 KMemoryAttribute::DeviceShared | KMemoryAttribute::Locked, KMemoryAttribute::DeviceShared));
1933 2681
1934 // Create an update allocator. 2682 // Create an update allocator.
1935 Result allocator_result{ResultSuccess}; 2683 Result allocator_result;
1936 KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result), 2684 KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result),
1937 m_memory_block_slab_manager, num_allocator_blocks); 2685 m_memory_block_slab_manager, num_allocator_blocks);
1938 R_TRY(allocator_result); 2686 R_TRY(allocator_result);
@@ -1976,13 +2724,28 @@ Result KPageTable::UnlockForDeviceAddressSpace(VAddr address, size_t size) {
1976 R_SUCCEED(); 2724 R_SUCCEED();
1977} 2725}
1978 2726
2727Result KPageTable::LockForIpcUserBuffer(PAddr* out, VAddr address, size_t size) {
2728 R_RETURN(this->LockMemoryAndOpen(
2729 nullptr, out, address, size, KMemoryState::FlagCanIpcUserBuffer,
2730 KMemoryState::FlagCanIpcUserBuffer, KMemoryPermission::All,
2731 KMemoryPermission::UserReadWrite, KMemoryAttribute::All, KMemoryAttribute::None,
2732 KMemoryPermission::NotMapped | KMemoryPermission::KernelReadWrite,
2733 KMemoryAttribute::Locked));
2734}
2735
2736Result KPageTable::UnlockForIpcUserBuffer(VAddr address, size_t size) {
2737 R_RETURN(this->UnlockMemory(address, size, KMemoryState::FlagCanIpcUserBuffer,
2738 KMemoryState::FlagCanIpcUserBuffer, KMemoryPermission::None,
2739 KMemoryPermission::None, KMemoryAttribute::All,
2740 KMemoryAttribute::Locked, KMemoryPermission::UserReadWrite,
2741 KMemoryAttribute::Locked, nullptr));
2742}
2743
1979Result KPageTable::LockForCodeMemory(KPageGroup* out, VAddr addr, size_t size) { 2744Result KPageTable::LockForCodeMemory(KPageGroup* out, VAddr addr, size_t size) {
1980 R_RETURN(this->LockMemoryAndOpen( 2745 R_RETURN(this->LockMemoryAndOpen(
1981 out, nullptr, addr, size, KMemoryState::FlagCanCodeMemory, KMemoryState::FlagCanCodeMemory, 2746 out, nullptr, addr, size, KMemoryState::FlagCanCodeMemory, KMemoryState::FlagCanCodeMemory,
1982 KMemoryPermission::All, KMemoryPermission::UserReadWrite, KMemoryAttribute::All, 2747 KMemoryPermission::All, KMemoryPermission::UserReadWrite, KMemoryAttribute::All,
1983 KMemoryAttribute::None, 2748 KMemoryAttribute::None, KMemoryPermission::NotMapped | KMemoryPermission::KernelReadWrite,
1984 static_cast<KMemoryPermission>(KMemoryPermission::NotMapped |
1985 KMemoryPermission::KernelReadWrite),
1986 KMemoryAttribute::Locked)); 2749 KMemoryAttribute::Locked));
1987} 2750}
1988 2751
@@ -2066,6 +2829,10 @@ Result KPageTable::Operate(VAddr addr, size_t num_pages, KMemoryPermission perm,
2066 m_system.Memory().MapMemoryRegion(*m_page_table_impl, addr, num_pages * PageSize, map_addr); 2829 m_system.Memory().MapMemoryRegion(*m_page_table_impl, addr, num_pages * PageSize, map_addr);
2067 break; 2830 break;
2068 } 2831 }
2832 case OperationType::Separate: {
2833 // HACK: Unimplemented.
2834 break;
2835 }
2069 case OperationType::ChangePermissions: 2836 case OperationType::ChangePermissions:
2070 case OperationType::ChangePermissionsAndRefresh: 2837 case OperationType::ChangePermissionsAndRefresh:
2071 break; 2838 break;
@@ -2075,6 +2842,17 @@ Result KPageTable::Operate(VAddr addr, size_t num_pages, KMemoryPermission perm,
2075 R_SUCCEED(); 2842 R_SUCCEED();
2076} 2843}
2077 2844
2845void KPageTable::FinalizeUpdate(PageLinkedList* page_list) {
2846 while (page_list->Peek()) {
2847 [[maybe_unused]] auto page = page_list->Pop();
2848
2849 // TODO(bunnei): Free pages once they are allocated in guest memory
2850 // ASSERT(this->GetPageTableManager().IsInPageTableHeap(page));
2851 // ASSERT(this->GetPageTableManager().GetRefCount(page) == 0);
2852 // this->GetPageTableManager().Free(page);
2853 }
2854}
2855
2078VAddr KPageTable::GetRegionAddress(KMemoryState state) const { 2856VAddr KPageTable::GetRegionAddress(KMemoryState state) const {
2079 switch (state) { 2857 switch (state) {
2080 case KMemoryState::Free: 2858 case KMemoryState::Free:
@@ -2101,6 +2879,7 @@ VAddr KPageTable::GetRegionAddress(KMemoryState state) const {
2101 case KMemoryState::GeneratedCode: 2879 case KMemoryState::GeneratedCode:
2102 case KMemoryState::CodeOut: 2880 case KMemoryState::CodeOut:
2103 case KMemoryState::Coverage: 2881 case KMemoryState::Coverage:
2882 case KMemoryState::Insecure:
2104 return m_alias_code_region_start; 2883 return m_alias_code_region_start;
2105 case KMemoryState::Code: 2884 case KMemoryState::Code:
2106 case KMemoryState::CodeData: 2885 case KMemoryState::CodeData:
@@ -2136,6 +2915,7 @@ size_t KPageTable::GetRegionSize(KMemoryState state) const {
2136 case KMemoryState::GeneratedCode: 2915 case KMemoryState::GeneratedCode:
2137 case KMemoryState::CodeOut: 2916 case KMemoryState::CodeOut:
2138 case KMemoryState::Coverage: 2917 case KMemoryState::Coverage:
2918 case KMemoryState::Insecure:
2139 return m_alias_code_region_end - m_alias_code_region_start; 2919 return m_alias_code_region_end - m_alias_code_region_start;
2140 case KMemoryState::Code: 2920 case KMemoryState::Code:
2141 case KMemoryState::CodeData: 2921 case KMemoryState::CodeData:
@@ -2177,6 +2957,7 @@ bool KPageTable::CanContain(VAddr addr, size_t size, KMemoryState state) const {
2177 case KMemoryState::GeneratedCode: 2957 case KMemoryState::GeneratedCode:
2178 case KMemoryState::CodeOut: 2958 case KMemoryState::CodeOut:
2179 case KMemoryState::Coverage: 2959 case KMemoryState::Coverage:
2960 case KMemoryState::Insecure:
2180 return is_in_region && !is_in_heap && !is_in_alias; 2961 return is_in_region && !is_in_heap && !is_in_alias;
2181 case KMemoryState::Normal: 2962 case KMemoryState::Normal:
2182 ASSERT(is_in_heap); 2963 ASSERT(is_in_heap);
diff --git a/src/core/hle/kernel/k_page_table.h b/src/core/hle/kernel/k_page_table.h
index c6aeacd96..950850291 100644
--- a/src/core/hle/kernel/k_page_table.h
+++ b/src/core/hle/kernel/k_page_table.h
@@ -16,6 +16,7 @@
16#include "core/hle/kernel/k_memory_layout.h" 16#include "core/hle/kernel/k_memory_layout.h"
17#include "core/hle/kernel/k_memory_manager.h" 17#include "core/hle/kernel/k_memory_manager.h"
18#include "core/hle/result.h" 18#include "core/hle/result.h"
19#include "core/memory.h"
19 20
20namespace Core { 21namespace Core {
21class System; 22class System;
@@ -23,7 +24,10 @@ class System;
23 24
24namespace Kernel { 25namespace Kernel {
25 26
27class KBlockInfoManager;
26class KMemoryBlockManager; 28class KMemoryBlockManager;
29class KResourceLimit;
30class KSystemResource;
27 31
28class KPageTable final { 32class KPageTable final {
29public: 33public:
@@ -36,9 +40,9 @@ public:
36 ~KPageTable(); 40 ~KPageTable();
37 41
38 Result InitializeForProcess(FileSys::ProgramAddressSpaceType as_type, bool enable_aslr, 42 Result InitializeForProcess(FileSys::ProgramAddressSpaceType as_type, bool enable_aslr,
39 VAddr code_addr, size_t code_size, 43 bool enable_das_merge, bool from_back, KMemoryManager::Pool pool,
40 KMemoryBlockSlabManager* mem_block_slab_manager, 44 VAddr code_addr, size_t code_size, KSystemResource* system_resource,
41 KMemoryManager::Pool pool); 45 KResourceLimit* resource_limit);
42 46
43 void Finalize(); 47 void Finalize();
44 48
@@ -74,12 +78,20 @@ public:
74 KMemoryState state, KMemoryPermission perm, 78 KMemoryState state, KMemoryPermission perm,
75 PAddr map_addr = 0); 79 PAddr map_addr = 0);
76 80
77 Result LockForMapDeviceAddressSpace(VAddr address, size_t size, KMemoryPermission perm, 81 Result LockForMapDeviceAddressSpace(bool* out_is_io, VAddr address, size_t size,
78 bool is_aligned); 82 KMemoryPermission perm, bool is_aligned, bool check_heap);
79 Result LockForUnmapDeviceAddressSpace(VAddr address, size_t size); 83 Result LockForUnmapDeviceAddressSpace(VAddr address, size_t size, bool check_heap);
80 84
81 Result UnlockForDeviceAddressSpace(VAddr addr, size_t size); 85 Result UnlockForDeviceAddressSpace(VAddr addr, size_t size);
82 86
87 Result LockForIpcUserBuffer(PAddr* out, VAddr address, size_t size);
88 Result UnlockForIpcUserBuffer(VAddr address, size_t size);
89
90 Result SetupForIpc(VAddr* out_dst_addr, size_t size, VAddr src_addr, KPageTable& src_page_table,
91 KMemoryPermission test_perm, KMemoryState dst_state, bool send);
92 Result CleanupForIpcServer(VAddr address, size_t size, KMemoryState dst_state);
93 Result CleanupForIpcClient(VAddr address, size_t size, KMemoryState dst_state);
94
83 Result LockForCodeMemory(KPageGroup* out, VAddr addr, size_t size); 95 Result LockForCodeMemory(KPageGroup* out, VAddr addr, size_t size);
84 Result UnlockForCodeMemory(VAddr addr, size_t size, const KPageGroup& pg); 96 Result UnlockForCodeMemory(VAddr addr, size_t size, const KPageGroup& pg);
85 Result MakeAndOpenPageGroup(KPageGroup* out, VAddr address, size_t num_pages, 97 Result MakeAndOpenPageGroup(KPageGroup* out, VAddr address, size_t num_pages,
@@ -97,13 +109,54 @@ public:
97 109
98 bool CanContain(VAddr addr, size_t size, KMemoryState state) const; 110 bool CanContain(VAddr addr, size_t size, KMemoryState state) const;
99 111
112protected:
113 struct PageLinkedList {
114 private:
115 struct Node {
116 Node* m_next;
117 std::array<u8, PageSize - sizeof(Node*)> m_buffer;
118 };
119
120 public:
121 constexpr PageLinkedList() = default;
122
123 void Push(Node* n) {
124 ASSERT(Common::IsAligned(reinterpret_cast<uintptr_t>(n), PageSize));
125 n->m_next = m_root;
126 m_root = n;
127 }
128
129 void Push(Core::Memory::Memory& memory, VAddr addr) {
130 this->Push(memory.GetPointer<Node>(addr));
131 }
132
133 Node* Peek() const {
134 return m_root;
135 }
136
137 Node* Pop() {
138 Node* const r = m_root;
139
140 m_root = r->m_next;
141 r->m_next = nullptr;
142
143 return r;
144 }
145
146 private:
147 Node* m_root{};
148 };
149 static_assert(std::is_trivially_destructible<PageLinkedList>::value);
150
100private: 151private:
101 enum class OperationType : u32 { 152 enum class OperationType : u32 {
102 Map, 153 Map = 0,
103 MapGroup, 154 MapFirst = 1,
104 Unmap, 155 MapGroup = 2,
105 ChangePermissions, 156 Unmap = 3,
106 ChangePermissionsAndRefresh, 157 ChangePermissions = 4,
158 ChangePermissionsAndRefresh = 5,
159 Separate = 6,
107 }; 160 };
108 161
109 static constexpr KMemoryAttribute DefaultMemoryIgnoreAttr = 162 static constexpr KMemoryAttribute DefaultMemoryIgnoreAttr =
@@ -123,6 +176,7 @@ private:
123 OperationType operation); 176 OperationType operation);
124 Result Operate(VAddr addr, size_t num_pages, KMemoryPermission perm, OperationType operation, 177 Result Operate(VAddr addr, size_t num_pages, KMemoryPermission perm, OperationType operation,
125 PAddr map_addr = 0); 178 PAddr map_addr = 0);
179 void FinalizeUpdate(PageLinkedList* page_list);
126 VAddr GetRegionAddress(KMemoryState state) const; 180 VAddr GetRegionAddress(KMemoryState state) const;
127 size_t GetRegionSize(KMemoryState state) const; 181 size_t GetRegionSize(KMemoryState state) const;
128 182
@@ -199,6 +253,18 @@ private:
199 return *out != 0; 253 return *out != 0;
200 } 254 }
201 255
256 Result SetupForIpcClient(PageLinkedList* page_list, size_t* out_blocks_needed, VAddr address,
257 size_t size, KMemoryPermission test_perm, KMemoryState dst_state);
258 Result SetupForIpcServer(VAddr* out_addr, size_t size, VAddr src_addr,
259 KMemoryPermission test_perm, KMemoryState dst_state,
260 KPageTable& src_page_table, bool send);
261 void CleanupForIpcClientOnServerSetupFailure(PageLinkedList* page_list, VAddr address,
262 size_t size, KMemoryPermission prot_perm);
263
264 // HACK: These will be removed once we automatically manage page reference counts.
265 void HACK_OpenPages(PAddr phys_addr, size_t num_pages);
266 void HACK_ClosePages(VAddr virt_addr, size_t num_pages);
267
202 mutable KLightLock m_general_lock; 268 mutable KLightLock m_general_lock;
203 mutable KLightLock m_map_physical_memory_lock; 269 mutable KLightLock m_map_physical_memory_lock;
204 270
@@ -316,6 +382,31 @@ public:
316 addr + size - 1 <= m_address_space_end - 1; 382 addr + size - 1 <= m_address_space_end - 1;
317 } 383 }
318 384
385public:
386 static VAddr GetLinearMappedVirtualAddress(const KMemoryLayout& layout, PAddr addr) {
387 return layout.GetLinearVirtualAddress(addr);
388 }
389
390 static PAddr GetLinearMappedPhysicalAddress(const KMemoryLayout& layout, VAddr addr) {
391 return layout.GetLinearPhysicalAddress(addr);
392 }
393
394 static VAddr GetHeapVirtualAddress(const KMemoryLayout& layout, PAddr addr) {
395 return GetLinearMappedVirtualAddress(layout, addr);
396 }
397
398 static PAddr GetHeapPhysicalAddress(const KMemoryLayout& layout, VAddr addr) {
399 return GetLinearMappedPhysicalAddress(layout, addr);
400 }
401
402 static VAddr GetPageTableVirtualAddress(const KMemoryLayout& layout, PAddr addr) {
403 return GetLinearMappedVirtualAddress(layout, addr);
404 }
405
406 static PAddr GetPageTablePhysicalAddress(const KMemoryLayout& layout, VAddr addr) {
407 return GetLinearMappedPhysicalAddress(layout, addr);
408 }
409
319private: 410private:
320 constexpr bool IsKernel() const { 411 constexpr bool IsKernel() const {
321 return m_is_kernel; 412 return m_is_kernel;
@@ -331,6 +422,24 @@ private:
331 } 422 }
332 423
333private: 424private:
425 class KScopedPageTableUpdater {
426 private:
427 KPageTable* m_pt{};
428 PageLinkedList m_ll;
429
430 public:
431 explicit KScopedPageTableUpdater(KPageTable* pt) : m_pt(pt) {}
432 explicit KScopedPageTableUpdater(KPageTable& pt) : KScopedPageTableUpdater(&pt) {}
433 ~KScopedPageTableUpdater() {
434 m_pt->FinalizeUpdate(this->GetPageList());
435 }
436
437 PageLinkedList* GetPageList() {
438 return &m_ll;
439 }
440 };
441
442private:
334 VAddr m_address_space_start{}; 443 VAddr m_address_space_start{};
335 VAddr m_address_space_end{}; 444 VAddr m_address_space_end{};
336 VAddr m_heap_region_start{}; 445 VAddr m_heap_region_start{};
@@ -347,20 +456,27 @@ private:
347 VAddr m_alias_code_region_start{}; 456 VAddr m_alias_code_region_start{};
348 VAddr m_alias_code_region_end{}; 457 VAddr m_alias_code_region_end{};
349 458
350 size_t m_mapped_physical_memory_size{};
351 size_t m_max_heap_size{}; 459 size_t m_max_heap_size{};
352 size_t m_max_physical_memory_size{}; 460 size_t m_mapped_physical_memory_size{};
461 size_t m_mapped_unsafe_physical_memory{};
462 size_t m_mapped_insecure_memory{};
463 size_t m_mapped_ipc_server_memory{};
353 size_t m_address_space_width{}; 464 size_t m_address_space_width{};
354 465
355 KMemoryBlockManager m_memory_block_manager; 466 KMemoryBlockManager m_memory_block_manager;
467 u32 m_allocate_option{};
356 468
357 bool m_is_kernel{}; 469 bool m_is_kernel{};
358 bool m_enable_aslr{}; 470 bool m_enable_aslr{};
359 bool m_enable_device_address_space_merge{}; 471 bool m_enable_device_address_space_merge{};
360 472
361 KMemoryBlockSlabManager* m_memory_block_slab_manager{}; 473 KMemoryBlockSlabManager* m_memory_block_slab_manager{};
474 KBlockInfoManager* m_block_info_manager{};
475 KResourceLimit* m_resource_limit{};
362 476
363 u32 m_heap_fill_value{}; 477 u32 m_heap_fill_value{};
478 u32 m_ipc_fill_value{};
479 u32 m_stack_fill_value{};
364 const KMemoryRegion* m_cached_physical_heap_region{}; 480 const KMemoryRegion* m_cached_physical_heap_region{};
365 481
366 KMemoryManager::Pool m_memory_pool{KMemoryManager::Pool::Application}; 482 KMemoryManager::Pool m_memory_pool{KMemoryManager::Pool::Application};
diff --git a/src/core/hle/kernel/k_page_table_manager.h b/src/core/hle/kernel/k_page_table_manager.h
new file mode 100644
index 000000000..91a45cde3
--- /dev/null
+++ b/src/core/hle/kernel/k_page_table_manager.h
@@ -0,0 +1,55 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include <atomic>
7
8#include "common/common_types.h"
9#include "core/hle/kernel/k_dynamic_resource_manager.h"
10#include "core/hle/kernel/k_page_table_slab_heap.h"
11
12namespace Kernel {
13
14class KPageTableManager : public KDynamicResourceManager<impl::PageTablePage, true> {
15public:
16 using RefCount = KPageTableSlabHeap::RefCount;
17 static constexpr size_t PageTableSize = KPageTableSlabHeap::PageTableSize;
18
19public:
20 KPageTableManager() = default;
21
22 void Initialize(KDynamicPageManager* page_allocator, KPageTableSlabHeap* pt_heap) {
23 m_pt_heap = pt_heap;
24
25 static_assert(std::derived_from<KPageTableSlabHeap, DynamicSlabType>);
26 BaseHeap::Initialize(page_allocator, pt_heap);
27 }
28
29 VAddr Allocate() {
30 return VAddr(BaseHeap::Allocate());
31 }
32
33 RefCount GetRefCount(VAddr addr) const {
34 return m_pt_heap->GetRefCount(addr);
35 }
36
37 void Open(VAddr addr, int count) {
38 return m_pt_heap->Open(addr, count);
39 }
40
41 bool Close(VAddr addr, int count) {
42 return m_pt_heap->Close(addr, count);
43 }
44
45 bool IsInPageTableHeap(VAddr addr) const {
46 return m_pt_heap->IsInRange(addr);
47 }
48
49private:
50 using BaseHeap = KDynamicResourceManager<impl::PageTablePage, true>;
51
52 KPageTableSlabHeap* m_pt_heap{};
53};
54
55} // namespace Kernel
diff --git a/src/core/hle/kernel/k_page_table_slab_heap.h b/src/core/hle/kernel/k_page_table_slab_heap.h
new file mode 100644
index 000000000..a9543cbd0
--- /dev/null
+++ b/src/core/hle/kernel/k_page_table_slab_heap.h
@@ -0,0 +1,93 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include <array>
7#include <vector>
8
9#include "common/common_types.h"
10#include "core/hle/kernel/k_dynamic_slab_heap.h"
11#include "core/hle/kernel/slab_helpers.h"
12
13namespace Kernel {
14
15namespace impl {
16
17class PageTablePage {
18public:
19 // Do not initialize anything.
20 PageTablePage() = default;
21
22private:
23 std::array<u8, PageSize> m_buffer{};
24};
25static_assert(sizeof(PageTablePage) == PageSize);
26
27} // namespace impl
28
29class KPageTableSlabHeap : public KDynamicSlabHeap<impl::PageTablePage, true> {
30public:
31 using RefCount = u16;
32 static constexpr size_t PageTableSize = sizeof(impl::PageTablePage);
33 static_assert(PageTableSize == PageSize);
34
35public:
36 KPageTableSlabHeap() = default;
37
38 static constexpr size_t CalculateReferenceCountSize(size_t size) {
39 return (size / PageSize) * sizeof(RefCount);
40 }
41
42 void Initialize(KDynamicPageManager* page_allocator, size_t object_count, RefCount* rc) {
43 BaseHeap::Initialize(page_allocator, object_count);
44 this->Initialize(rc);
45 }
46
47 RefCount GetRefCount(VAddr addr) {
48 ASSERT(this->IsInRange(addr));
49 return *this->GetRefCountPointer(addr);
50 }
51
52 void Open(VAddr addr, int count) {
53 ASSERT(this->IsInRange(addr));
54
55 *this->GetRefCountPointer(addr) += static_cast<RefCount>(count);
56
57 ASSERT(this->GetRefCount(addr) > 0);
58 }
59
60 bool Close(VAddr addr, int count) {
61 ASSERT(this->IsInRange(addr));
62 ASSERT(this->GetRefCount(addr) >= count);
63
64 *this->GetRefCountPointer(addr) -= static_cast<RefCount>(count);
65 return this->GetRefCount(addr) == 0;
66 }
67
68 bool IsInPageTableHeap(VAddr addr) const {
69 return this->IsInRange(addr);
70 }
71
72private:
73 void Initialize([[maybe_unused]] RefCount* rc) {
74 // TODO(bunnei): Use rc once we support kernel virtual memory allocations.
75 const auto count = this->GetSize() / PageSize;
76 m_ref_counts.resize(count);
77
78 for (size_t i = 0; i < count; i++) {
79 m_ref_counts[i] = 0;
80 }
81 }
82
83 RefCount* GetRefCountPointer(VAddr addr) {
84 return m_ref_counts.data() + ((addr - this->GetAddress()) / PageSize);
85 }
86
87private:
88 using BaseHeap = KDynamicSlabHeap<impl::PageTablePage, true>;
89
90 std::vector<RefCount> m_ref_counts;
91};
92
93} // namespace Kernel
diff --git a/src/core/hle/kernel/k_port.cpp b/src/core/hle/kernel/k_port.cpp
index 7a5a9dc2a..77d00ae2c 100644
--- a/src/core/hle/kernel/k_port.cpp
+++ b/src/core/hle/kernel/k_port.cpp
@@ -57,12 +57,6 @@ Result KPort::EnqueueSession(KServerSession* session) {
57 57
58 server.EnqueueSession(session); 58 server.EnqueueSession(session);
59 59
60 if (auto session_ptr = server.GetSessionRequestHandler().lock()) {
61 session_ptr->ClientConnected(server.AcceptSession());
62 } else {
63 ASSERT(false);
64 }
65
66 return ResultSuccess; 60 return ResultSuccess;
67} 61}
68 62
diff --git a/src/core/hle/kernel/k_process.cpp b/src/core/hle/kernel/k_process.cpp
index 8c3495e5a..55a9c5fae 100644
--- a/src/core/hle/kernel/k_process.cpp
+++ b/src/core/hle/kernel/k_process.cpp
@@ -38,7 +38,7 @@ namespace {
38 */ 38 */
39void SetupMainThread(Core::System& system, KProcess& owner_process, u32 priority, VAddr stack_top) { 39void SetupMainThread(Core::System& system, KProcess& owner_process, u32 priority, VAddr stack_top) {
40 const VAddr entry_point = owner_process.PageTable().GetCodeRegionStart(); 40 const VAddr entry_point = owner_process.PageTable().GetCodeRegionStart();
41 ASSERT(owner_process.GetResourceLimit()->Reserve(LimitableResource::Threads, 1)); 41 ASSERT(owner_process.GetResourceLimit()->Reserve(LimitableResource::ThreadCountMax, 1));
42 42
43 KThread* thread = KThread::Create(system.Kernel()); 43 KThread* thread = KThread::Create(system.Kernel());
44 SCOPE_EXIT({ thread->Close(); }); 44 SCOPE_EXIT({ thread->Close(); });
@@ -124,7 +124,7 @@ void KProcess::DecrementRunningThreadCount() {
124} 124}
125 125
126u64 KProcess::GetTotalPhysicalMemoryAvailable() { 126u64 KProcess::GetTotalPhysicalMemoryAvailable() {
127 const u64 capacity{resource_limit->GetFreeValue(LimitableResource::PhysicalMemory) + 127 const u64 capacity{resource_limit->GetFreeValue(LimitableResource::PhysicalMemoryMax) +
128 page_table.GetNormalMemorySize() + GetSystemResourceSize() + image_size + 128 page_table.GetNormalMemorySize() + GetSystemResourceSize() + image_size +
129 main_thread_stack_size}; 129 main_thread_stack_size};
130 if (const auto pool_size = kernel.MemoryManager().GetSize(KMemoryManager::Pool::Application); 130 if (const auto pool_size = kernel.MemoryManager().GetSize(KMemoryManager::Pool::Application);
@@ -349,8 +349,8 @@ Result KProcess::LoadFromMetadata(const FileSys::ProgramMetadata& metadata, std:
349 // We currently do not support process-specific system resource 349 // We currently do not support process-specific system resource
350 UNIMPLEMENTED_IF(system_resource_size != 0); 350 UNIMPLEMENTED_IF(system_resource_size != 0);
351 351
352 KScopedResourceReservation memory_reservation(resource_limit, LimitableResource::PhysicalMemory, 352 KScopedResourceReservation memory_reservation(
353 code_size + system_resource_size); 353 resource_limit, LimitableResource::PhysicalMemoryMax, code_size + system_resource_size);
354 if (!memory_reservation.Succeeded()) { 354 if (!memory_reservation.Succeeded()) {
355 LOG_ERROR(Kernel, "Could not reserve process memory requirements of size {:X} bytes", 355 LOG_ERROR(Kernel, "Could not reserve process memory requirements of size {:X} bytes",
356 code_size + system_resource_size); 356 code_size + system_resource_size);
@@ -358,8 +358,8 @@ Result KProcess::LoadFromMetadata(const FileSys::ProgramMetadata& metadata, std:
358 } 358 }
359 // Initialize proces address space 359 // Initialize proces address space
360 if (const Result result{page_table.InitializeForProcess( 360 if (const Result result{page_table.InitializeForProcess(
361 metadata.GetAddressSpaceType(), false, 0x8000000, code_size, 361 metadata.GetAddressSpaceType(), false, false, false, KMemoryManager::Pool::Application,
362 &kernel.GetApplicationMemoryBlockManager(), KMemoryManager::Pool::Application)}; 362 0x8000000, code_size, &kernel.GetSystemSystemResource(), resource_limit)};
363 result.IsError()) { 363 result.IsError()) {
364 R_RETURN(result); 364 R_RETURN(result);
365 } 365 }
@@ -406,8 +406,8 @@ Result KProcess::LoadFromMetadata(const FileSys::ProgramMetadata& metadata, std:
406 406
407void KProcess::Run(s32 main_thread_priority, u64 stack_size) { 407void KProcess::Run(s32 main_thread_priority, u64 stack_size) {
408 AllocateMainThreadStack(stack_size); 408 AllocateMainThreadStack(stack_size);
409 resource_limit->Reserve(LimitableResource::Threads, 1); 409 resource_limit->Reserve(LimitableResource::ThreadCountMax, 1);
410 resource_limit->Reserve(LimitableResource::PhysicalMemory, main_thread_stack_size); 410 resource_limit->Reserve(LimitableResource::PhysicalMemoryMax, main_thread_stack_size);
411 411
412 const std::size_t heap_capacity{memory_usage_capacity - (main_thread_stack_size + image_size)}; 412 const std::size_t heap_capacity{memory_usage_capacity - (main_thread_stack_size + image_size)};
413 ASSERT(!page_table.SetMaxHeapSize(heap_capacity).IsError()); 413 ASSERT(!page_table.SetMaxHeapSize(heap_capacity).IsError());
@@ -442,7 +442,7 @@ void KProcess::PrepareForTermination() {
442 plr_address = 0; 442 plr_address = 0;
443 443
444 if (resource_limit) { 444 if (resource_limit) {
445 resource_limit->Release(LimitableResource::PhysicalMemory, 445 resource_limit->Release(LimitableResource::PhysicalMemoryMax,
446 main_thread_stack_size + image_size); 446 main_thread_stack_size + image_size);
447 } 447 }
448 448
diff --git a/src/core/hle/kernel/k_resource_limit.cpp b/src/core/hle/kernel/k_resource_limit.cpp
index 010dcf99e..b9d22b414 100644
--- a/src/core/hle/kernel/k_resource_limit.cpp
+++ b/src/core/hle/kernel/k_resource_limit.cpp
@@ -159,12 +159,13 @@ KResourceLimit* CreateResourceLimitForProcess(Core::System& system, s64 physical
159 // TODO(bunnei): These values are the system defaults, the limits for service processes are 159 // TODO(bunnei): These values are the system defaults, the limits for service processes are
160 // lower. These should use the correct limit values. 160 // lower. These should use the correct limit values.
161 161
162 ASSERT(resource_limit->SetLimitValue(LimitableResource::PhysicalMemory, physical_memory_size) 162 ASSERT(resource_limit->SetLimitValue(LimitableResource::PhysicalMemoryMax, physical_memory_size)
163 .IsSuccess()); 163 .IsSuccess());
164 ASSERT(resource_limit->SetLimitValue(LimitableResource::Threads, 800).IsSuccess()); 164 ASSERT(resource_limit->SetLimitValue(LimitableResource::ThreadCountMax, 800).IsSuccess());
165 ASSERT(resource_limit->SetLimitValue(LimitableResource::Events, 900).IsSuccess()); 165 ASSERT(resource_limit->SetLimitValue(LimitableResource::EventCountMax, 900).IsSuccess());
166 ASSERT(resource_limit->SetLimitValue(LimitableResource::TransferMemory, 200).IsSuccess()); 166 ASSERT(
167 ASSERT(resource_limit->SetLimitValue(LimitableResource::Sessions, 1133).IsSuccess()); 167 resource_limit->SetLimitValue(LimitableResource::TransferMemoryCountMax, 200).IsSuccess());
168 ASSERT(resource_limit->SetLimitValue(LimitableResource::SessionCountMax, 1133).IsSuccess());
168 169
169 return resource_limit; 170 return resource_limit;
170} 171}
diff --git a/src/core/hle/kernel/k_resource_limit.h b/src/core/hle/kernel/k_resource_limit.h
index 65c98c979..2573d1b7c 100644
--- a/src/core/hle/kernel/k_resource_limit.h
+++ b/src/core/hle/kernel/k_resource_limit.h
@@ -16,15 +16,8 @@ class CoreTiming;
16 16
17namespace Kernel { 17namespace Kernel {
18class KernelCore; 18class KernelCore;
19enum class LimitableResource : u32 { 19
20 PhysicalMemory = 0, 20using LimitableResource = Svc::LimitableResource;
21 Threads = 1,
22 Events = 2,
23 TransferMemory = 3,
24 Sessions = 4,
25
26 Count,
27};
28 21
29constexpr bool IsValidResourceType(LimitableResource type) { 22constexpr bool IsValidResourceType(LimitableResource type) {
30 return type < LimitableResource::Count; 23 return type < LimitableResource::Count;
diff --git a/src/core/hle/kernel/k_scheduler.cpp b/src/core/hle/kernel/k_scheduler.cpp
index c34ce7a17..d6676904b 100644
--- a/src/core/hle/kernel/k_scheduler.cpp
+++ b/src/core/hle/kernel/k_scheduler.cpp
@@ -81,8 +81,8 @@ void KScheduler::RescheduleCurrentHLEThread(KernelCore& kernel) {
81 // HACK: we cannot schedule from this thread, it is not a core thread 81 // HACK: we cannot schedule from this thread, it is not a core thread
82 ASSERT(GetCurrentThread(kernel).GetDisableDispatchCount() == 1); 82 ASSERT(GetCurrentThread(kernel).GetDisableDispatchCount() == 1);
83 83
84 // Special case to ensure dummy threads that are waiting block 84 // Ensure dummy threads that are waiting block.
85 GetCurrentThread(kernel).IfDummyThreadTryWait(); 85 GetCurrentThread(kernel).DummyThreadBeginWait();
86 86
87 ASSERT(GetCurrentThread(kernel).GetState() != ThreadState::Waiting); 87 ASSERT(GetCurrentThread(kernel).GetState() != ThreadState::Waiting);
88 GetCurrentThread(kernel).EnableDispatch(); 88 GetCurrentThread(kernel).EnableDispatch();
@@ -314,6 +314,16 @@ u64 KScheduler::UpdateHighestPriorityThreadsImpl(KernelCore& kernel) {
314 idle_cores &= ~(1ULL << core_id); 314 idle_cores &= ~(1ULL << core_id);
315 } 315 }
316 316
317 // HACK: any waiting dummy threads can wake up now.
318 kernel.GlobalSchedulerContext().WakeupWaitingDummyThreads();
319
320 // HACK: if we are a dummy thread, and we need to go sleep, indicate
321 // that for when the lock is released.
322 KThread* const cur_thread = GetCurrentThreadPointer(kernel);
323 if (cur_thread->IsDummyThread() && cur_thread->GetState() != ThreadState::Runnable) {
324 cur_thread->RequestDummyThreadWait();
325 }
326
317 return cores_needing_scheduling; 327 return cores_needing_scheduling;
318} 328}
319 329
@@ -374,7 +384,8 @@ void KScheduler::SwitchThread(KThread* next_thread) {
374 384
375void KScheduler::ScheduleImpl() { 385void KScheduler::ScheduleImpl() {
376 // First, clear the needs scheduling bool. 386 // First, clear the needs scheduling bool.
377 m_state.needs_scheduling.store(false, std::memory_order_seq_cst); 387 m_state.needs_scheduling.store(false, std::memory_order_relaxed);
388 std::atomic_thread_fence(std::memory_order_seq_cst);
378 389
379 // Load the appropriate thread pointers for scheduling. 390 // Load the appropriate thread pointers for scheduling.
380 KThread* const cur_thread{GetCurrentThreadPointer(kernel)}; 391 KThread* const cur_thread{GetCurrentThreadPointer(kernel)};
@@ -390,7 +401,8 @@ void KScheduler::ScheduleImpl() {
390 // If there aren't, we want to check if the highest priority thread is the same as the current 401 // If there aren't, we want to check if the highest priority thread is the same as the current
391 // thread. 402 // thread.
392 if (highest_priority_thread == cur_thread) { 403 if (highest_priority_thread == cur_thread) {
393 // If they're the same, then we can just return. 404 // If they're the same, then we can just issue a memory barrier and return.
405 std::atomic_thread_fence(std::memory_order_seq_cst);
394 return; 406 return;
395 } 407 }
396 408
@@ -466,7 +478,8 @@ void KScheduler::ScheduleImplFiber() {
466 478
467 // We failed to successfully do the context switch, and need to retry. 479 // We failed to successfully do the context switch, and need to retry.
468 // Clear needs_scheduling. 480 // Clear needs_scheduling.
469 m_state.needs_scheduling.store(false, std::memory_order_seq_cst); 481 m_state.needs_scheduling.store(false, std::memory_order_relaxed);
482 std::atomic_thread_fence(std::memory_order_seq_cst);
470 483
471 // Refresh the highest priority thread. 484 // Refresh the highest priority thread.
472 highest_priority_thread = m_state.highest_priority_thread; 485 highest_priority_thread = m_state.highest_priority_thread;
@@ -531,11 +544,23 @@ void KScheduler::OnThreadStateChanged(KernelCore& kernel, KThread* thread, Threa
531 GetPriorityQueue(kernel).Remove(thread); 544 GetPriorityQueue(kernel).Remove(thread);
532 IncrementScheduledCount(thread); 545 IncrementScheduledCount(thread);
533 SetSchedulerUpdateNeeded(kernel); 546 SetSchedulerUpdateNeeded(kernel);
547
548 if (thread->IsDummyThread()) {
549 // HACK: if this is a dummy thread, it should no longer wake up when the
550 // scheduler lock is released.
551 kernel.GlobalSchedulerContext().UnregisterDummyThreadForWakeup(thread);
552 }
534 } else if (cur_state == ThreadState::Runnable) { 553 } else if (cur_state == ThreadState::Runnable) {
535 // If we're now runnable, then we weren't previously, and we should add. 554 // If we're now runnable, then we weren't previously, and we should add.
536 GetPriorityQueue(kernel).PushBack(thread); 555 GetPriorityQueue(kernel).PushBack(thread);
537 IncrementScheduledCount(thread); 556 IncrementScheduledCount(thread);
538 SetSchedulerUpdateNeeded(kernel); 557 SetSchedulerUpdateNeeded(kernel);
558
559 if (thread->IsDummyThread()) {
560 // HACK: if this is a dummy thread, it should wake up when the scheduler
561 // lock is released.
562 kernel.GlobalSchedulerContext().RegisterDummyThreadForWakeup(thread);
563 }
539 } 564 }
540} 565}
541 566
diff --git a/src/core/hle/kernel/k_scheduler_lock.h b/src/core/hle/kernel/k_scheduler_lock.h
index 73314b45e..129d60472 100644
--- a/src/core/hle/kernel/k_scheduler_lock.h
+++ b/src/core/hle/kernel/k_scheduler_lock.h
@@ -60,6 +60,9 @@ public:
60 60
61 // Release an instance of the lock. 61 // Release an instance of the lock.
62 if ((--lock_count) == 0) { 62 if ((--lock_count) == 0) {
63 // Perform a memory barrier here.
64 std::atomic_thread_fence(std::memory_order_seq_cst);
65
63 // We're no longer going to hold the lock. Take note of what cores need scheduling. 66 // We're no longer going to hold the lock. Take note of what cores need scheduling.
64 const u64 cores_needing_scheduling = 67 const u64 cores_needing_scheduling =
65 SchedulerType::UpdateHighestPriorityThreads(kernel); 68 SchedulerType::UpdateHighestPriorityThreads(kernel);
diff --git a/src/core/hle/kernel/k_server_port.cpp b/src/core/hle/kernel/k_server_port.cpp
index e968f26ad..16968ba97 100644
--- a/src/core/hle/kernel/k_server_port.cpp
+++ b/src/core/hle/kernel/k_server_port.cpp
@@ -61,12 +61,6 @@ void KServerPort::Destroy() {
61 61
62 // Close our reference to our parent. 62 // Close our reference to our parent.
63 parent->Close(); 63 parent->Close();
64
65 // Release host emulation members.
66 session_handler.reset();
67
68 // Ensure that the global list tracking server objects does not hold on to a reference.
69 kernel.UnregisterServerObject(this);
70} 64}
71 65
72bool KServerPort::IsSignaled() const { 66bool KServerPort::IsSignaled() const {
diff --git a/src/core/hle/kernel/k_server_port.h b/src/core/hle/kernel/k_server_port.h
index fd4f4bd20..5fc7ee683 100644
--- a/src/core/hle/kernel/k_server_port.h
+++ b/src/core/hle/kernel/k_server_port.h
@@ -27,24 +27,6 @@ public:
27 27
28 void Initialize(KPort* parent_port_, std::string&& name_); 28 void Initialize(KPort* parent_port_, std::string&& name_);
29 29
30 /// Whether or not this server port has an HLE handler available.
31 bool HasSessionRequestHandler() const {
32 return !session_handler.expired();
33 }
34
35 /// Gets the HLE handler for this port.
36 SessionRequestHandlerWeakPtr GetSessionRequestHandler() const {
37 return session_handler;
38 }
39
40 /**
41 * Sets the HLE handler template for the port. ServerSessions crated by connecting to this port
42 * will inherit a reference to this handler.
43 */
44 void SetSessionHandler(SessionRequestHandlerWeakPtr&& handler) {
45 session_handler = std::move(handler);
46 }
47
48 void EnqueueSession(KServerSession* pending_session); 30 void EnqueueSession(KServerSession* pending_session);
49 31
50 KServerSession* AcceptSession(); 32 KServerSession* AcceptSession();
@@ -65,7 +47,6 @@ private:
65 void CleanupSessions(); 47 void CleanupSessions();
66 48
67 SessionList session_list; 49 SessionList session_list;
68 SessionRequestHandlerWeakPtr session_handler;
69 KPort* parent{}; 50 KPort* parent{};
70}; 51};
71 52
diff --git a/src/core/hle/kernel/k_server_session.cpp b/src/core/hle/kernel/k_server_session.cpp
index faf03fcc8..aa1941f01 100644
--- a/src/core/hle/kernel/k_server_session.cpp
+++ b/src/core/hle/kernel/k_server_session.cpp
@@ -1,4 +1,4 @@
1// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project 1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later 2// SPDX-License-Identifier: GPL-2.0-or-later
3 3
4#include <tuple> 4#include <tuple>
@@ -33,12 +33,10 @@ KServerSession::KServerSession(KernelCore& kernel_)
33 33
34KServerSession::~KServerSession() = default; 34KServerSession::~KServerSession() = default;
35 35
36void KServerSession::Initialize(KSession* parent_session_, std::string&& name_, 36void KServerSession::Initialize(KSession* parent_session_, std::string&& name_) {
37 std::shared_ptr<SessionRequestManager> manager_) {
38 // Set member variables. 37 // Set member variables.
39 parent = parent_session_; 38 parent = parent_session_;
40 name = std::move(name_); 39 name = std::move(name_);
41 manager = manager_;
42} 40}
43 41
44void KServerSession::Destroy() { 42void KServerSession::Destroy() {
@@ -47,18 +45,99 @@ void KServerSession::Destroy() {
47 this->CleanupRequests(); 45 this->CleanupRequests();
48 46
49 parent->Close(); 47 parent->Close();
50
51 // Release host emulation members.
52 manager.reset();
53
54 // Ensure that the global list tracking server objects does not hold on to a reference.
55 kernel.UnregisterServerObject(this);
56} 48}
57 49
58void KServerSession::OnClientClosed() { 50void KServerSession::OnClientClosed() {
59 if (manager && manager->HasSessionHandler()) { 51 KScopedLightLock lk{m_lock};
60 manager->SessionHandler().ClientDisconnected(this); 52
53 // Handle any pending requests.
54 KSessionRequest* prev_request = nullptr;
55 while (true) {
56 // Declare variables for processing the request.
57 KSessionRequest* request = nullptr;
58 KEvent* event = nullptr;
59 KThread* thread = nullptr;
60 bool cur_request = false;
61 bool terminate = false;
62
63 // Get the next request.
64 {
65 KScopedSchedulerLock sl{kernel};
66
67 if (m_current_request != nullptr && m_current_request != prev_request) {
68 // Set the request, open a reference as we process it.
69 request = m_current_request;
70 request->Open();
71 cur_request = true;
72
73 // Get thread and event for the request.
74 thread = request->GetThread();
75 event = request->GetEvent();
76
77 // If the thread is terminating, handle that.
78 if (thread->IsTerminationRequested()) {
79 request->ClearThread();
80 request->ClearEvent();
81 terminate = true;
82 }
83
84 prev_request = request;
85 } else if (!m_request_list.empty()) {
86 // Pop the request from the front of the list.
87 request = std::addressof(m_request_list.front());
88 m_request_list.pop_front();
89
90 // Get thread and event for the request.
91 thread = request->GetThread();
92 event = request->GetEvent();
93 }
94 }
95
96 // If there are no requests, we're done.
97 if (request == nullptr) {
98 break;
99 }
100
101 // All requests must have threads.
102 ASSERT(thread != nullptr);
103
104 // Ensure that we close the request when done.
105 SCOPE_EXIT({ request->Close(); });
106
107 // If we're terminating, close a reference to the thread and event.
108 if (terminate) {
109 thread->Close();
110 if (event != nullptr) {
111 event->Close();
112 }
113 }
114
115 // If we need to, reply.
116 if (event != nullptr && !cur_request) {
117 // There must be no mappings.
118 ASSERT(request->GetSendCount() == 0);
119 ASSERT(request->GetReceiveCount() == 0);
120 ASSERT(request->GetExchangeCount() == 0);
121
122 // // Get the process and page table.
123 // KProcess *client_process = thread->GetOwnerProcess();
124 // auto &client_pt = client_process->GetPageTable();
125
126 // // Reply to the request.
127 // ReplyAsyncError(client_process, request->GetAddress(), request->GetSize(),
128 // ResultSessionClosed);
129
130 // // Unlock the buffer.
131 // // NOTE: Nintendo does not check the result of this.
132 // client_pt.UnlockForIpcUserBuffer(request->GetAddress(), request->GetSize());
133
134 // Signal the event.
135 event->Signal();
136 }
61 } 137 }
138
139 // Notify.
140 this->NotifyAvailable(ResultSessionClosed);
62} 141}
63 142
64bool KServerSession::IsSignaled() const { 143bool KServerSession::IsSignaled() const {
@@ -73,24 +152,6 @@ bool KServerSession::IsSignaled() const {
73 return !m_request_list.empty() && m_current_request == nullptr; 152 return !m_request_list.empty() && m_current_request == nullptr;
74} 153}
75 154
76Result KServerSession::QueueSyncRequest(KThread* thread, Core::Memory::Memory& memory) {
77 u32* cmd_buf{reinterpret_cast<u32*>(memory.GetPointer(thread->GetTLSAddress()))};
78 auto context = std::make_shared<HLERequestContext>(kernel, memory, this, thread);
79
80 context->PopulateFromIncomingCommandBuffer(kernel.CurrentProcess()->GetHandleTable(), cmd_buf);
81
82 return manager->QueueSyncRequest(parent, std::move(context));
83}
84
85Result KServerSession::CompleteSyncRequest(HLERequestContext& context) {
86 Result result = manager->CompleteSyncRequest(this, context);
87
88 // The calling thread is waiting for this request to complete, so wake it up.
89 context.GetThread().EndWait(result);
90
91 return result;
92}
93
94Result KServerSession::OnRequest(KSessionRequest* request) { 155Result KServerSession::OnRequest(KSessionRequest* request) {
95 // Create the wait queue. 156 // Create the wait queue.
96 ThreadQueueImplForKServerSessionRequest wait_queue{kernel}; 157 ThreadQueueImplForKServerSessionRequest wait_queue{kernel};
@@ -105,24 +166,16 @@ Result KServerSession::OnRequest(KSessionRequest* request) {
105 // Check that we're not terminating. 166 // Check that we're not terminating.
106 R_UNLESS(!GetCurrentThread(kernel).IsTerminationRequested(), ResultTerminationRequested); 167 R_UNLESS(!GetCurrentThread(kernel).IsTerminationRequested(), ResultTerminationRequested);
107 168
108 if (manager) { 169 // Get whether we're empty.
109 // HLE request. 170 const bool was_empty = m_request_list.empty();
110 auto& memory{kernel.System().Memory()};
111 this->QueueSyncRequest(GetCurrentThreadPointer(kernel), memory);
112 } else {
113 // Non-HLE request.
114
115 // Get whether we're empty.
116 const bool was_empty = m_request_list.empty();
117 171
118 // Add the request to the list. 172 // Add the request to the list.
119 request->Open(); 173 request->Open();
120 m_request_list.push_back(*request); 174 m_request_list.push_back(*request);
121 175
122 // If we were empty, signal. 176 // If we were empty, signal.
123 if (was_empty) { 177 if (was_empty) {
124 this->NotifyAvailable(); 178 this->NotifyAvailable();
125 }
126 } 179 }
127 180
128 // If we have a request event, this is asynchronous, and we don't need to wait. 181 // If we have a request event, this is asynchronous, and we don't need to wait.
@@ -136,7 +189,7 @@ Result KServerSession::OnRequest(KSessionRequest* request) {
136 return GetCurrentThread(kernel).GetWaitResult(); 189 return GetCurrentThread(kernel).GetWaitResult();
137} 190}
138 191
139Result KServerSession::SendReply() { 192Result KServerSession::SendReply(bool is_hle) {
140 // Lock the session. 193 // Lock the session.
141 KScopedLightLock lk{m_lock}; 194 KScopedLightLock lk{m_lock};
142 195
@@ -171,13 +224,18 @@ Result KServerSession::SendReply() {
171 Result result = ResultSuccess; 224 Result result = ResultSuccess;
172 if (!closed) { 225 if (!closed) {
173 // If we're not closed, send the reply. 226 // If we're not closed, send the reply.
174 Core::Memory::Memory& memory{kernel.System().Memory()}; 227 if (is_hle) {
175 KThread* server_thread{GetCurrentThreadPointer(kernel)}; 228 // HLE servers write directly to a pointer to the thread command buffer. Therefore
176 UNIMPLEMENTED_IF(server_thread->GetOwnerProcess() != client_thread->GetOwnerProcess()); 229 // the reply has already been written in this case.
230 } else {
231 Core::Memory::Memory& memory{kernel.System().Memory()};
232 KThread* server_thread{GetCurrentThreadPointer(kernel)};
233 UNIMPLEMENTED_IF(server_thread->GetOwnerProcess() != client_thread->GetOwnerProcess());
177 234
178 auto* src_msg_buffer = memory.GetPointer(server_thread->GetTLSAddress()); 235 auto* src_msg_buffer = memory.GetPointer(server_thread->GetTLSAddress());
179 auto* dst_msg_buffer = memory.GetPointer(client_message); 236 auto* dst_msg_buffer = memory.GetPointer(client_message);
180 std::memcpy(dst_msg_buffer, src_msg_buffer, client_buffer_size); 237 std::memcpy(dst_msg_buffer, src_msg_buffer, client_buffer_size);
238 }
181 } else { 239 } else {
182 result = ResultSessionClosed; 240 result = ResultSessionClosed;
183 } 241 }
@@ -223,7 +281,8 @@ Result KServerSession::SendReply() {
223 return result; 281 return result;
224} 282}
225 283
226Result KServerSession::ReceiveRequest() { 284Result KServerSession::ReceiveRequest(std::shared_ptr<HLERequestContext>* out_context,
285 std::weak_ptr<SessionRequestManager> manager) {
227 // Lock the session. 286 // Lock the session.
228 KScopedLightLock lk{m_lock}; 287 KScopedLightLock lk{m_lock};
229 288
@@ -267,12 +326,22 @@ Result KServerSession::ReceiveRequest() {
267 326
268 // Receive the message. 327 // Receive the message.
269 Core::Memory::Memory& memory{kernel.System().Memory()}; 328 Core::Memory::Memory& memory{kernel.System().Memory()};
270 KThread* server_thread{GetCurrentThreadPointer(kernel)}; 329 if (out_context != nullptr) {
271 UNIMPLEMENTED_IF(server_thread->GetOwnerProcess() != client_thread->GetOwnerProcess()); 330 // HLE request.
331 u32* cmd_buf{reinterpret_cast<u32*>(memory.GetPointer(client_message))};
332 *out_context = std::make_shared<HLERequestContext>(kernel, memory, this, client_thread);
333 (*out_context)->SetSessionRequestManager(manager);
334 (*out_context)
335 ->PopulateFromIncomingCommandBuffer(client_thread->GetOwnerProcess()->GetHandleTable(),
336 cmd_buf);
337 } else {
338 KThread* server_thread{GetCurrentThreadPointer(kernel)};
339 UNIMPLEMENTED_IF(server_thread->GetOwnerProcess() != client_thread->GetOwnerProcess());
272 340
273 auto* src_msg_buffer = memory.GetPointer(client_message); 341 auto* src_msg_buffer = memory.GetPointer(client_message);
274 auto* dst_msg_buffer = memory.GetPointer(server_thread->GetTLSAddress()); 342 auto* dst_msg_buffer = memory.GetPointer(server_thread->GetTLSAddress());
275 std::memcpy(dst_msg_buffer, src_msg_buffer, client_buffer_size); 343 std::memcpy(dst_msg_buffer, src_msg_buffer, client_buffer_size);
344 }
276 345
277 // We succeeded. 346 // We succeeded.
278 return ResultSuccess; 347 return ResultSuccess;
diff --git a/src/core/hle/kernel/k_server_session.h b/src/core/hle/kernel/k_server_session.h
index 32135473b..6e189af8b 100644
--- a/src/core/hle/kernel/k_server_session.h
+++ b/src/core/hle/kernel/k_server_session.h
@@ -1,4 +1,4 @@
1// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project 1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later 2// SPDX-License-Identifier: GPL-2.0-or-later
3 3
4#pragma once 4#pragma once
@@ -16,21 +16,11 @@
16#include "core/hle/kernel/k_synchronization_object.h" 16#include "core/hle/kernel/k_synchronization_object.h"
17#include "core/hle/result.h" 17#include "core/hle/result.h"
18 18
19namespace Core::Memory {
20class Memory;
21}
22
23namespace Core::Timing {
24class CoreTiming;
25struct EventType;
26} // namespace Core::Timing
27
28namespace Kernel { 19namespace Kernel {
29 20
30class HLERequestContext; 21class HLERequestContext;
31class KernelCore; 22class KernelCore;
32class KSession; 23class KSession;
33class SessionRequestHandler;
34class SessionRequestManager; 24class SessionRequestManager;
35class KThread; 25class KThread;
36 26
@@ -46,8 +36,7 @@ public:
46 36
47 void Destroy() override; 37 void Destroy() override;
48 38
49 void Initialize(KSession* parent_session_, std::string&& name_, 39 void Initialize(KSession* parent_session_, std::string&& name_);
50 std::shared_ptr<SessionRequestManager> manager_);
51 40
52 KSession* GetParent() { 41 KSession* GetParent() {
53 return parent; 42 return parent;
@@ -60,38 +49,26 @@ public:
60 bool IsSignaled() const override; 49 bool IsSignaled() const override;
61 void OnClientClosed(); 50 void OnClientClosed();
62 51
63 /// Gets the session request manager, which forwards requests to the underlying service
64 std::shared_ptr<SessionRequestManager>& GetSessionRequestManager() {
65 return manager;
66 }
67
68 /// TODO: flesh these out to match the real kernel 52 /// TODO: flesh these out to match the real kernel
69 Result OnRequest(KSessionRequest* request); 53 Result OnRequest(KSessionRequest* request);
70 Result SendReply(); 54 Result SendReply(bool is_hle = false);
71 Result ReceiveRequest(); 55 Result ReceiveRequest(std::shared_ptr<HLERequestContext>* out_context = nullptr,
56 std::weak_ptr<SessionRequestManager> manager = {});
57
58 Result SendReplyHLE() {
59 return SendReply(true);
60 }
72 61
73private: 62private:
74 /// Frees up waiting client sessions when this server session is about to die 63 /// Frees up waiting client sessions when this server session is about to die
75 void CleanupRequests(); 64 void CleanupRequests();
76 65
77 /// Queues a sync request from the emulated application.
78 Result QueueSyncRequest(KThread* thread, Core::Memory::Memory& memory);
79
80 /// Completes a sync request from the emulated application.
81 Result CompleteSyncRequest(HLERequestContext& context);
82
83 /// This session's HLE request handlers; if nullptr, this is not an HLE server
84 std::shared_ptr<SessionRequestManager> manager;
85
86 /// When set to True, converts the session to a domain at the end of the command
87 bool convert_to_domain{};
88
89 /// KSession that owns this KServerSession 66 /// KSession that owns this KServerSession
90 KSession* parent{}; 67 KSession* parent{};
91 68
92 /// List of threads which are pending a reply. 69 /// List of threads which are pending a reply.
93 boost::intrusive::list<KSessionRequest> m_request_list; 70 boost::intrusive::list<KSessionRequest> m_request_list;
94 KSessionRequest* m_current_request; 71 KSessionRequest* m_current_request{};
95 72
96 KLightLock m_lock; 73 KLightLock m_lock;
97}; 74};
diff --git a/src/core/hle/kernel/k_session.cpp b/src/core/hle/kernel/k_session.cpp
index ee05aa282..b6f6fe9d9 100644
--- a/src/core/hle/kernel/k_session.cpp
+++ b/src/core/hle/kernel/k_session.cpp
@@ -13,8 +13,7 @@ KSession::KSession(KernelCore& kernel_)
13 : KAutoObjectWithSlabHeapAndContainer{kernel_}, server{kernel_}, client{kernel_} {} 13 : KAutoObjectWithSlabHeapAndContainer{kernel_}, server{kernel_}, client{kernel_} {}
14KSession::~KSession() = default; 14KSession::~KSession() = default;
15 15
16void KSession::Initialize(KClientPort* port_, const std::string& name_, 16void KSession::Initialize(KClientPort* port_, const std::string& name_) {
17 std::shared_ptr<SessionRequestManager> manager_) {
18 // Increment reference count. 17 // Increment reference count.
19 // Because reference count is one on creation, this will result 18 // Because reference count is one on creation, this will result
20 // in a reference count of two. Thus, when both server and client are closed 19 // in a reference count of two. Thus, when both server and client are closed
@@ -26,7 +25,7 @@ void KSession::Initialize(KClientPort* port_, const std::string& name_,
26 KAutoObject::Create(std::addressof(client)); 25 KAutoObject::Create(std::addressof(client));
27 26
28 // Initialize our sub sessions. 27 // Initialize our sub sessions.
29 server.Initialize(this, name_ + ":Server", manager_); 28 server.Initialize(this, name_ + ":Server");
30 client.Initialize(this, name_ + ":Client"); 29 client.Initialize(this, name_ + ":Client");
31 30
32 // Set state and name. 31 // Set state and name.
@@ -77,7 +76,7 @@ void KSession::OnClientClosed() {
77void KSession::PostDestroy(uintptr_t arg) { 76void KSession::PostDestroy(uintptr_t arg) {
78 // Release the session count resource the owner process holds. 77 // Release the session count resource the owner process holds.
79 KProcess* owner = reinterpret_cast<KProcess*>(arg); 78 KProcess* owner = reinterpret_cast<KProcess*>(arg);
80 owner->GetResourceLimit()->Release(LimitableResource::Sessions, 1); 79 owner->GetResourceLimit()->Release(LimitableResource::SessionCountMax, 1);
81 owner->Close(); 80 owner->Close();
82} 81}
83 82
diff --git a/src/core/hle/kernel/k_session.h b/src/core/hle/kernel/k_session.h
index c6ead403b..93e5e6f71 100644
--- a/src/core/hle/kernel/k_session.h
+++ b/src/core/hle/kernel/k_session.h
@@ -21,8 +21,7 @@ public:
21 explicit KSession(KernelCore& kernel_); 21 explicit KSession(KernelCore& kernel_);
22 ~KSession() override; 22 ~KSession() override;
23 23
24 void Initialize(KClientPort* port_, const std::string& name_, 24 void Initialize(KClientPort* port_, const std::string& name_);
25 std::shared_ptr<SessionRequestManager> manager_ = nullptr);
26 25
27 void Finalize() override; 26 void Finalize() override;
28 27
diff --git a/src/core/hle/kernel/k_shared_memory.cpp b/src/core/hle/kernel/k_shared_memory.cpp
index a039cc591..10cd4c43d 100644
--- a/src/core/hle/kernel/k_shared_memory.cpp
+++ b/src/core/hle/kernel/k_shared_memory.cpp
@@ -14,7 +14,7 @@ namespace Kernel {
14KSharedMemory::KSharedMemory(KernelCore& kernel_) : KAutoObjectWithSlabHeapAndContainer{kernel_} {} 14KSharedMemory::KSharedMemory(KernelCore& kernel_) : KAutoObjectWithSlabHeapAndContainer{kernel_} {}
15 15
16KSharedMemory::~KSharedMemory() { 16KSharedMemory::~KSharedMemory() {
17 kernel.GetSystemResourceLimit()->Release(LimitableResource::PhysicalMemory, size); 17 kernel.GetSystemResourceLimit()->Release(LimitableResource::PhysicalMemoryMax, size);
18} 18}
19 19
20Result KSharedMemory::Initialize(Core::DeviceMemory& device_memory_, KProcess* owner_process_, 20Result KSharedMemory::Initialize(Core::DeviceMemory& device_memory_, KProcess* owner_process_,
@@ -35,7 +35,7 @@ Result KSharedMemory::Initialize(Core::DeviceMemory& device_memory_, KProcess* o
35 KResourceLimit* reslimit = kernel.GetSystemResourceLimit(); 35 KResourceLimit* reslimit = kernel.GetSystemResourceLimit();
36 36
37 // Reserve memory for ourselves. 37 // Reserve memory for ourselves.
38 KScopedResourceReservation memory_reservation(reslimit, LimitableResource::PhysicalMemory, 38 KScopedResourceReservation memory_reservation(reslimit, LimitableResource::PhysicalMemoryMax,
39 size_); 39 size_);
40 R_UNLESS(memory_reservation.Succeeded(), ResultLimitReached); 40 R_UNLESS(memory_reservation.Succeeded(), ResultLimitReached);
41 41
@@ -57,7 +57,7 @@ Result KSharedMemory::Initialize(Core::DeviceMemory& device_memory_, KProcess* o
57 57
58void KSharedMemory::Finalize() { 58void KSharedMemory::Finalize() {
59 // Release the memory reservation. 59 // Release the memory reservation.
60 resource_limit->Release(LimitableResource::PhysicalMemory, size); 60 resource_limit->Release(LimitableResource::PhysicalMemoryMax, size);
61 resource_limit->Close(); 61 resource_limit->Close();
62 62
63 // Perform inherited finalization. 63 // Perform inherited finalization.
diff --git a/src/core/hle/kernel/k_system_resource.cpp b/src/core/hle/kernel/k_system_resource.cpp
new file mode 100644
index 000000000..4cc377a6c
--- /dev/null
+++ b/src/core/hle/kernel/k_system_resource.cpp
@@ -0,0 +1,26 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "core/hle/kernel/k_system_resource.h"
5
6namespace Kernel {
7
8Result KSecureSystemResource::Initialize([[maybe_unused]] size_t size,
9 [[maybe_unused]] KResourceLimit* resource_limit,
10 [[maybe_unused]] KMemoryManager::Pool pool) {
11 // Unimplemented
12 UNREACHABLE();
13}
14
15void KSecureSystemResource::Finalize() {
16 // Unimplemented
17 UNREACHABLE();
18}
19
20size_t KSecureSystemResource::CalculateRequiredSecureMemorySize(
21 [[maybe_unused]] size_t size, [[maybe_unused]] KMemoryManager::Pool pool) {
22 // Unimplemented
23 UNREACHABLE();
24}
25
26} // namespace Kernel
diff --git a/src/core/hle/kernel/k_system_resource.h b/src/core/hle/kernel/k_system_resource.h
new file mode 100644
index 000000000..9a991f725
--- /dev/null
+++ b/src/core/hle/kernel/k_system_resource.h
@@ -0,0 +1,137 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include "common/assert.h"
7#include "common/common_types.h"
8#include "core/hle/kernel/k_auto_object.h"
9#include "core/hle/kernel/k_dynamic_resource_manager.h"
10#include "core/hle/kernel/k_memory_manager.h"
11#include "core/hle/kernel/k_page_table_manager.h"
12#include "core/hle/kernel/k_resource_limit.h"
13#include "core/hle/kernel/slab_helpers.h"
14
15namespace Kernel {
16
17// NOTE: Nintendo's implementation does not have the "is_secure_resource" field, and instead uses
18// virtual IsSecureResource().
19
20class KSystemResource : public KAutoObject {
21 KERNEL_AUTOOBJECT_TRAITS(KSystemResource, KAutoObject);
22
23public:
24 explicit KSystemResource(KernelCore& kernel_) : KAutoObject(kernel_) {}
25
26protected:
27 void SetSecureResource() {
28 m_is_secure_resource = true;
29 }
30
31public:
32 virtual void Destroy() override {
33 UNREACHABLE_MSG("KSystemResource::Destroy() was called");
34 }
35
36 bool IsSecureResource() const {
37 return m_is_secure_resource;
38 }
39
40 void SetManagers(KMemoryBlockSlabManager& mb, KBlockInfoManager& bi, KPageTableManager& pt) {
41 ASSERT(m_p_memory_block_slab_manager == nullptr);
42 ASSERT(m_p_block_info_manager == nullptr);
43 ASSERT(m_p_page_table_manager == nullptr);
44
45 m_p_memory_block_slab_manager = std::addressof(mb);
46 m_p_block_info_manager = std::addressof(bi);
47 m_p_page_table_manager = std::addressof(pt);
48 }
49
50 const KMemoryBlockSlabManager& GetMemoryBlockSlabManager() const {
51 return *m_p_memory_block_slab_manager;
52 }
53 const KBlockInfoManager& GetBlockInfoManager() const {
54 return *m_p_block_info_manager;
55 }
56 const KPageTableManager& GetPageTableManager() const {
57 return *m_p_page_table_manager;
58 }
59
60 KMemoryBlockSlabManager& GetMemoryBlockSlabManager() {
61 return *m_p_memory_block_slab_manager;
62 }
63 KBlockInfoManager& GetBlockInfoManager() {
64 return *m_p_block_info_manager;
65 }
66 KPageTableManager& GetPageTableManager() {
67 return *m_p_page_table_manager;
68 }
69
70 KMemoryBlockSlabManager* GetMemoryBlockSlabManagerPointer() {
71 return m_p_memory_block_slab_manager;
72 }
73 KBlockInfoManager* GetBlockInfoManagerPointer() {
74 return m_p_block_info_manager;
75 }
76 KPageTableManager* GetPageTableManagerPointer() {
77 return m_p_page_table_manager;
78 }
79
80private:
81 KMemoryBlockSlabManager* m_p_memory_block_slab_manager{};
82 KBlockInfoManager* m_p_block_info_manager{};
83 KPageTableManager* m_p_page_table_manager{};
84 bool m_is_secure_resource{false};
85};
86
87class KSecureSystemResource final
88 : public KAutoObjectWithSlabHeap<KSecureSystemResource, KSystemResource> {
89public:
90 explicit KSecureSystemResource(KernelCore& kernel_)
91 : KAutoObjectWithSlabHeap<KSecureSystemResource, KSystemResource>(kernel_) {
92 // Mark ourselves as being a secure resource.
93 this->SetSecureResource();
94 }
95
96 Result Initialize(size_t size, KResourceLimit* resource_limit, KMemoryManager::Pool pool);
97 void Finalize();
98
99 bool IsInitialized() const {
100 return m_is_initialized;
101 }
102 static void PostDestroy([[maybe_unused]] uintptr_t arg) {}
103
104 size_t CalculateRequiredSecureMemorySize() const {
105 return CalculateRequiredSecureMemorySize(m_resource_size, m_resource_pool);
106 }
107
108 size_t GetSize() const {
109 return m_resource_size;
110 }
111 size_t GetUsedSize() const {
112 return m_dynamic_page_manager.GetUsed() * PageSize;
113 }
114
115 const KDynamicPageManager& GetDynamicPageManager() const {
116 return m_dynamic_page_manager;
117 }
118
119public:
120 static size_t CalculateRequiredSecureMemorySize(size_t size, KMemoryManager::Pool pool);
121
122private:
123 bool m_is_initialized{};
124 KMemoryManager::Pool m_resource_pool{};
125 KDynamicPageManager m_dynamic_page_manager;
126 KMemoryBlockSlabManager m_memory_block_slab_manager;
127 KBlockInfoManager m_block_info_manager;
128 KPageTableManager m_page_table_manager;
129 KMemoryBlockSlabHeap m_memory_block_heap;
130 KBlockInfoSlabHeap m_block_info_heap;
131 KPageTableSlabHeap m_page_table_heap;
132 KResourceLimit* m_resource_limit{};
133 VAddr m_resource_address{};
134 size_t m_resource_size{};
135};
136
137} // namespace Kernel
diff --git a/src/core/hle/kernel/k_thread.cpp b/src/core/hle/kernel/k_thread.cpp
index b7bfcdce3..21207fe99 100644
--- a/src/core/hle/kernel/k_thread.cpp
+++ b/src/core/hle/kernel/k_thread.cpp
@@ -148,7 +148,9 @@ Result KThread::Initialize(KThreadFunction func, uintptr_t arg, VAddr user_stack
148 physical_affinity_mask.SetAffinity(phys_core, true); 148 physical_affinity_mask.SetAffinity(phys_core, true);
149 149
150 // Set the thread state. 150 // Set the thread state.
151 thread_state = (type == ThreadType::Main) ? ThreadState::Runnable : ThreadState::Initialized; 151 thread_state = (type == ThreadType::Main || type == ThreadType::Dummy)
152 ? ThreadState::Runnable
153 : ThreadState::Initialized;
152 154
153 // Set TLS address. 155 // Set TLS address.
154 tls_address = 0; 156 tls_address = 0;
@@ -261,9 +263,9 @@ Result KThread::InitializeThread(KThread* thread, KThreadFunction func, uintptr_
261 R_SUCCEED(); 263 R_SUCCEED();
262} 264}
263 265
264Result KThread::InitializeDummyThread(KThread* thread) { 266Result KThread::InitializeDummyThread(KThread* thread, KProcess* owner) {
265 // Initialize the thread. 267 // Initialize the thread.
266 R_TRY(thread->Initialize({}, {}, {}, DummyThreadPriority, 3, {}, ThreadType::Dummy)); 268 R_TRY(thread->Initialize({}, {}, {}, DummyThreadPriority, 3, owner, ThreadType::Dummy));
267 269
268 // Initialize emulation parameters. 270 // Initialize emulation parameters.
269 thread->stack_parameters.disable_count = 0; 271 thread->stack_parameters.disable_count = 0;
@@ -301,7 +303,7 @@ void KThread::PostDestroy(uintptr_t arg) {
301 const bool resource_limit_release_hint = (arg & 1); 303 const bool resource_limit_release_hint = (arg & 1);
302 const s64 hint_value = (resource_limit_release_hint ? 0 : 1); 304 const s64 hint_value = (resource_limit_release_hint ? 0 : 1);
303 if (owner != nullptr) { 305 if (owner != nullptr) {
304 owner->GetResourceLimit()->Release(LimitableResource::Threads, 1, hint_value); 306 owner->GetResourceLimit()->Release(LimitableResource::ThreadCountMax, 1, hint_value);
305 owner->Close(); 307 owner->Close();
306 } 308 }
307} 309}
@@ -1052,7 +1054,7 @@ void KThread::Exit() {
1052 1054
1053 // Release the thread resource hint, running thread count from parent. 1055 // Release the thread resource hint, running thread count from parent.
1054 if (parent != nullptr) { 1056 if (parent != nullptr) {
1055 parent->GetResourceLimit()->Release(Kernel::LimitableResource::Threads, 0, 1); 1057 parent->GetResourceLimit()->Release(Kernel::LimitableResource::ThreadCountMax, 0, 1);
1056 resource_limit_release_hint = true; 1058 resource_limit_release_hint = true;
1057 parent->DecrementRunningThreadCount(); 1059 parent->DecrementRunningThreadCount();
1058 } 1060 }
@@ -1174,30 +1176,31 @@ Result KThread::Sleep(s64 timeout) {
1174 R_SUCCEED(); 1176 R_SUCCEED();
1175} 1177}
1176 1178
1177void KThread::IfDummyThreadTryWait() { 1179void KThread::RequestDummyThreadWait() {
1178 if (!IsDummyThread()) { 1180 ASSERT(KScheduler::IsSchedulerLockedByCurrentThread(kernel));
1179 return; 1181 ASSERT(this->IsDummyThread());
1180 } 1182
1183 // We will block when the scheduler lock is released.
1184 dummy_thread_runnable.store(false);
1185}
1181 1186
1182 if (GetState() != ThreadState::Waiting) { 1187void KThread::DummyThreadBeginWait() {
1188 if (!this->IsDummyThread() || kernel.IsPhantomModeForSingleCore()) {
1189 // Occurs in single core mode.
1183 return; 1190 return;
1184 } 1191 }
1185 1192
1186 ASSERT(!kernel.IsPhantomModeForSingleCore()); 1193 // Block until runnable is no longer false.
1187 1194 dummy_thread_runnable.wait(false);
1188 // Block until we are no longer waiting.
1189 std::unique_lock lk(dummy_wait_lock);
1190 dummy_wait_cv.wait(
1191 lk, [&] { return GetState() != ThreadState::Waiting || kernel.IsShuttingDown(); });
1192} 1195}
1193 1196
1194void KThread::IfDummyThreadEndWait() { 1197void KThread::DummyThreadEndWait() {
1195 if (!IsDummyThread()) { 1198 ASSERT(KScheduler::IsSchedulerLockedByCurrentThread(kernel));
1196 return; 1199 ASSERT(this->IsDummyThread());
1197 }
1198 1200
1199 // Wake up the waiting thread. 1201 // Wake up the waiting thread.
1200 dummy_wait_cv.notify_one(); 1202 dummy_thread_runnable.store(true);
1203 dummy_thread_runnable.notify_one();
1201} 1204}
1202 1205
1203void KThread::BeginWait(KThreadQueue* queue) { 1206void KThread::BeginWait(KThreadQueue* queue) {
@@ -1231,9 +1234,6 @@ void KThread::EndWait(Result wait_result_) {
1231 } 1234 }
1232 1235
1233 wait_queue->EndWait(this, wait_result_); 1236 wait_queue->EndWait(this, wait_result_);
1234
1235 // Special case for dummy threads to wakeup if necessary.
1236 IfDummyThreadEndWait();
1237 } 1237 }
1238} 1238}
1239 1239
diff --git a/src/core/hle/kernel/k_thread.h b/src/core/hle/kernel/k_thread.h
index e2a27d603..f38c92bff 100644
--- a/src/core/hle/kernel/k_thread.h
+++ b/src/core/hle/kernel/k_thread.h
@@ -415,7 +415,7 @@ public:
415 415
416 static void PostDestroy(uintptr_t arg); 416 static void PostDestroy(uintptr_t arg);
417 417
418 [[nodiscard]] static Result InitializeDummyThread(KThread* thread); 418 [[nodiscard]] static Result InitializeDummyThread(KThread* thread, KProcess* owner);
419 419
420 [[nodiscard]] static Result InitializeMainThread(Core::System& system, KThread* thread, 420 [[nodiscard]] static Result InitializeMainThread(Core::System& system, KThread* thread,
421 s32 virt_core); 421 s32 virt_core);
@@ -643,8 +643,9 @@ public:
643 // therefore will not block on guest kernel synchronization primitives. These methods handle 643 // therefore will not block on guest kernel synchronization primitives. These methods handle
644 // blocking as needed. 644 // blocking as needed.
645 645
646 void IfDummyThreadTryWait(); 646 void RequestDummyThreadWait();
647 void IfDummyThreadEndWait(); 647 void DummyThreadBeginWait();
648 void DummyThreadEndWait();
648 649
649 [[nodiscard]] uintptr_t GetArgument() const { 650 [[nodiscard]] uintptr_t GetArgument() const {
650 return argument; 651 return argument;
@@ -777,8 +778,7 @@ private:
777 bool is_single_core{}; 778 bool is_single_core{};
778 ThreadType thread_type{}; 779 ThreadType thread_type{};
779 StepState step_state{}; 780 StepState step_state{};
780 std::mutex dummy_wait_lock; 781 std::atomic<bool> dummy_thread_runnable{true};
781 std::condition_variable dummy_wait_cv;
782 782
783 // For debugging 783 // For debugging
784 std::vector<KSynchronizationObject*> wait_objects_for_debugging; 784 std::vector<KSynchronizationObject*> wait_objects_for_debugging;
diff --git a/src/core/hle/kernel/k_transfer_memory.cpp b/src/core/hle/kernel/k_transfer_memory.cpp
index b0320eb73..9f34c2d46 100644
--- a/src/core/hle/kernel/k_transfer_memory.cpp
+++ b/src/core/hle/kernel/k_transfer_memory.cpp
@@ -37,7 +37,7 @@ void KTransferMemory::Finalize() {
37 37
38void KTransferMemory::PostDestroy(uintptr_t arg) { 38void KTransferMemory::PostDestroy(uintptr_t arg) {
39 KProcess* owner = reinterpret_cast<KProcess*>(arg); 39 KProcess* owner = reinterpret_cast<KProcess*>(arg);
40 owner->GetResourceLimit()->Release(LimitableResource::TransferMemory, 1); 40 owner->GetResourceLimit()->Release(LimitableResource::TransferMemoryCountMax, 1);
41 owner->Close(); 41 owner->Close();
42} 42}
43 43
diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp
index fdc774e30..b77723503 100644
--- a/src/core/hle/kernel/kernel.cpp
+++ b/src/core/hle/kernel/kernel.cpp
@@ -28,10 +28,12 @@
28#include "core/hle/kernel/k_handle_table.h" 28#include "core/hle/kernel/k_handle_table.h"
29#include "core/hle/kernel/k_memory_layout.h" 29#include "core/hle/kernel/k_memory_layout.h"
30#include "core/hle/kernel/k_memory_manager.h" 30#include "core/hle/kernel/k_memory_manager.h"
31#include "core/hle/kernel/k_page_buffer.h"
31#include "core/hle/kernel/k_process.h" 32#include "core/hle/kernel/k_process.h"
32#include "core/hle/kernel/k_resource_limit.h" 33#include "core/hle/kernel/k_resource_limit.h"
33#include "core/hle/kernel/k_scheduler.h" 34#include "core/hle/kernel/k_scheduler.h"
34#include "core/hle/kernel/k_shared_memory.h" 35#include "core/hle/kernel/k_shared_memory.h"
36#include "core/hle/kernel/k_system_resource.h"
35#include "core/hle/kernel/k_thread.h" 37#include "core/hle/kernel/k_thread.h"
36#include "core/hle/kernel/k_worker_task_manager.h" 38#include "core/hle/kernel/k_worker_task_manager.h"
37#include "core/hle/kernel/kernel.h" 39#include "core/hle/kernel/kernel.h"
@@ -47,6 +49,11 @@ MICROPROFILE_DEFINE(Kernel_SVC, "Kernel", "SVC", MP_RGB(70, 200, 70));
47namespace Kernel { 49namespace Kernel {
48 50
49struct KernelCore::Impl { 51struct KernelCore::Impl {
52 static constexpr size_t ApplicationMemoryBlockSlabHeapSize = 20000;
53 static constexpr size_t SystemMemoryBlockSlabHeapSize = 10000;
54 static constexpr size_t BlockInfoSlabHeapSize = 4000;
55 static constexpr size_t ReservedDynamicPageCount = 64;
56
50 explicit Impl(Core::System& system_, KernelCore& kernel_) 57 explicit Impl(Core::System& system_, KernelCore& kernel_)
51 : time_manager{system_}, service_threads_manager{1, "ServiceThreadsManager"}, 58 : time_manager{system_}, service_threads_manager{1, "ServiceThreadsManager"},
52 service_thread_barrier{2}, system{system_} {} 59 service_thread_barrier{2}, system{system_} {}
@@ -60,7 +67,6 @@ struct KernelCore::Impl {
60 global_scheduler_context = std::make_unique<Kernel::GlobalSchedulerContext>(kernel); 67 global_scheduler_context = std::make_unique<Kernel::GlobalSchedulerContext>(kernel);
61 global_handle_table = std::make_unique<Kernel::KHandleTable>(kernel); 68 global_handle_table = std::make_unique<Kernel::KHandleTable>(kernel);
62 global_handle_table->Initialize(KHandleTable::MaxTableSize); 69 global_handle_table->Initialize(KHandleTable::MaxTableSize);
63 default_service_thread = CreateServiceThread(kernel, "DefaultServiceThread");
64 70
65 is_phantom_mode_for_singlecore = false; 71 is_phantom_mode_for_singlecore = false;
66 72
@@ -72,7 +78,6 @@ struct KernelCore::Impl {
72 // Initialize kernel memory and resources. 78 // Initialize kernel memory and resources.
73 InitializeSystemResourceLimit(kernel, system.CoreTiming()); 79 InitializeSystemResourceLimit(kernel, system.CoreTiming());
74 InitializeMemoryLayout(); 80 InitializeMemoryLayout();
75 Init::InitializeKPageBufferSlabHeap(system);
76 InitializeShutdownThreads(); 81 InitializeShutdownThreads();
77 InitializePhysicalCores(); 82 InitializePhysicalCores();
78 InitializePreemption(kernel); 83 InitializePreemption(kernel);
@@ -82,10 +87,13 @@ struct KernelCore::Impl {
82 const auto& pt_heap_region = memory_layout->GetPageTableHeapRegion(); 87 const auto& pt_heap_region = memory_layout->GetPageTableHeapRegion();
83 ASSERT(pt_heap_region.GetEndAddress() != 0); 88 ASSERT(pt_heap_region.GetEndAddress() != 0);
84 89
85 InitializeResourceManagers(pt_heap_region.GetAddress(), pt_heap_region.GetSize()); 90 InitializeResourceManagers(kernel, pt_heap_region.GetAddress(),
91 pt_heap_region.GetSize());
86 } 92 }
87 93
88 RegisterHostThread(); 94 RegisterHostThread(nullptr);
95
96 default_service_thread = &CreateServiceThread(kernel, "DefaultServiceThread");
89 } 97 }
90 98
91 void InitializeCores() { 99 void InitializeCores() {
@@ -184,17 +192,6 @@ struct KernelCore::Impl {
184 } 192 }
185 193
186 void CloseServices() { 194 void CloseServices() {
187 // Close all open server sessions and ports.
188 std::unordered_set<KAutoObject*> server_objects_;
189 {
190 std::scoped_lock lk(server_objects_lock);
191 server_objects_ = server_objects;
192 server_objects.clear();
193 }
194 for (auto* server_object : server_objects_) {
195 server_object->Close();
196 }
197
198 // Ensures all service threads gracefully shutdown. 195 // Ensures all service threads gracefully shutdown.
199 ClearServiceThreads(); 196 ClearServiceThreads();
200 } 197 }
@@ -232,18 +229,22 @@ struct KernelCore::Impl {
232 const auto kernel_size{sizes.second}; 229 const auto kernel_size{sizes.second};
233 230
234 // If setting the default system values fails, then something seriously wrong has occurred. 231 // If setting the default system values fails, then something seriously wrong has occurred.
235 ASSERT(system_resource_limit->SetLimitValue(LimitableResource::PhysicalMemory, total_size) 232 ASSERT(
233 system_resource_limit->SetLimitValue(LimitableResource::PhysicalMemoryMax, total_size)
234 .IsSuccess());
235 ASSERT(system_resource_limit->SetLimitValue(LimitableResource::ThreadCountMax, 800)
236 .IsSuccess());
237 ASSERT(system_resource_limit->SetLimitValue(LimitableResource::EventCountMax, 900)
238 .IsSuccess());
239 ASSERT(system_resource_limit->SetLimitValue(LimitableResource::TransferMemoryCountMax, 200)
236 .IsSuccess()); 240 .IsSuccess());
237 ASSERT(system_resource_limit->SetLimitValue(LimitableResource::Threads, 800).IsSuccess()); 241 ASSERT(system_resource_limit->SetLimitValue(LimitableResource::SessionCountMax, 1133)
238 ASSERT(system_resource_limit->SetLimitValue(LimitableResource::Events, 900).IsSuccess());
239 ASSERT(system_resource_limit->SetLimitValue(LimitableResource::TransferMemory, 200)
240 .IsSuccess()); 242 .IsSuccess());
241 ASSERT(system_resource_limit->SetLimitValue(LimitableResource::Sessions, 1133).IsSuccess()); 243 system_resource_limit->Reserve(LimitableResource::PhysicalMemoryMax, kernel_size);
242 system_resource_limit->Reserve(LimitableResource::PhysicalMemory, kernel_size);
243 244
244 // Reserve secure applet memory, introduced in firmware 5.0.0 245 // Reserve secure applet memory, introduced in firmware 5.0.0
245 constexpr u64 secure_applet_memory_size{4_MiB}; 246 constexpr u64 secure_applet_memory_size{4_MiB};
246 ASSERT(system_resource_limit->Reserve(LimitableResource::PhysicalMemory, 247 ASSERT(system_resource_limit->Reserve(LimitableResource::PhysicalMemoryMax,
247 secure_applet_memory_size)); 248 secure_applet_memory_size));
248 } 249 }
249 250
@@ -263,16 +264,82 @@ struct KernelCore::Impl {
263 system.CoreTiming().ScheduleLoopingEvent(time_interval, time_interval, preemption_event); 264 system.CoreTiming().ScheduleLoopingEvent(time_interval, time_interval, preemption_event);
264 } 265 }
265 266
266 void InitializeResourceManagers(VAddr address, size_t size) { 267 void InitializeResourceManagers(KernelCore& kernel, VAddr address, size_t size) {
267 dynamic_page_manager = std::make_unique<KDynamicPageManager>(); 268 // Ensure that the buffer is suitable for our use.
268 memory_block_heap = std::make_unique<KMemoryBlockSlabHeap>(); 269 ASSERT(Common::IsAligned(address, PageSize));
270 ASSERT(Common::IsAligned(size, PageSize));
271
272 // Ensure that we have space for our reference counts.
273 const size_t rc_size =
274 Common::AlignUp(KPageTableSlabHeap::CalculateReferenceCountSize(size), PageSize);
275 ASSERT(rc_size < size);
276 size -= rc_size;
277
278 // Initialize the resource managers' shared page manager.
279 resource_manager_page_manager = std::make_unique<KDynamicPageManager>();
280 resource_manager_page_manager->Initialize(
281 address, size, std::max<size_t>(PageSize, KPageBufferSlabHeap::BufferSize));
282
283 // Initialize the KPageBuffer slab heap.
284 page_buffer_slab_heap.Initialize(system);
285
286 // Initialize the fixed-size slabheaps.
287 app_memory_block_heap = std::make_unique<KMemoryBlockSlabHeap>();
288 sys_memory_block_heap = std::make_unique<KMemoryBlockSlabHeap>();
289 block_info_heap = std::make_unique<KBlockInfoSlabHeap>();
290 app_memory_block_heap->Initialize(resource_manager_page_manager.get(),
291 ApplicationMemoryBlockSlabHeapSize);
292 sys_memory_block_heap->Initialize(resource_manager_page_manager.get(),
293 SystemMemoryBlockSlabHeapSize);
294 block_info_heap->Initialize(resource_manager_page_manager.get(), BlockInfoSlabHeapSize);
295
296 // Reserve all but a fixed number of remaining pages for the page table heap.
297 const size_t num_pt_pages = resource_manager_page_manager->GetCount() -
298 resource_manager_page_manager->GetUsed() -
299 ReservedDynamicPageCount;
300 page_table_heap = std::make_unique<KPageTableSlabHeap>();
301
302 // TODO(bunnei): Pass in address once we support kernel virtual memory allocations.
303 page_table_heap->Initialize(
304 resource_manager_page_manager.get(), num_pt_pages,
305 /*GetPointer<KPageTableManager::RefCount>(address + size)*/ nullptr);
306
307 // Setup the slab managers.
308 KDynamicPageManager* const app_dynamic_page_manager = nullptr;
309 KDynamicPageManager* const sys_dynamic_page_manager =
310 /*KTargetSystem::IsDynamicResourceLimitsEnabled()*/ true
311 ? resource_manager_page_manager.get()
312 : nullptr;
269 app_memory_block_manager = std::make_unique<KMemoryBlockSlabManager>(); 313 app_memory_block_manager = std::make_unique<KMemoryBlockSlabManager>();
270 314 sys_memory_block_manager = std::make_unique<KMemoryBlockSlabManager>();
271 dynamic_page_manager->Initialize(address, size); 315 app_block_info_manager = std::make_unique<KBlockInfoManager>();
272 static constexpr size_t ApplicationMemoryBlockSlabHeapSize = 20000; 316 sys_block_info_manager = std::make_unique<KBlockInfoManager>();
273 memory_block_heap->Initialize(dynamic_page_manager.get(), 317 app_page_table_manager = std::make_unique<KPageTableManager>();
274 ApplicationMemoryBlockSlabHeapSize); 318 sys_page_table_manager = std::make_unique<KPageTableManager>();
275 app_memory_block_manager->Initialize(nullptr, memory_block_heap.get()); 319
320 app_memory_block_manager->Initialize(app_dynamic_page_manager, app_memory_block_heap.get());
321 sys_memory_block_manager->Initialize(sys_dynamic_page_manager, sys_memory_block_heap.get());
322
323 app_block_info_manager->Initialize(app_dynamic_page_manager, block_info_heap.get());
324 sys_block_info_manager->Initialize(sys_dynamic_page_manager, block_info_heap.get());
325
326 app_page_table_manager->Initialize(app_dynamic_page_manager, page_table_heap.get());
327 sys_page_table_manager->Initialize(sys_dynamic_page_manager, page_table_heap.get());
328
329 // Check that we have the correct number of dynamic pages available.
330 ASSERT(resource_manager_page_manager->GetCount() -
331 resource_manager_page_manager->GetUsed() ==
332 ReservedDynamicPageCount);
333
334 // Create the system page table managers.
335 app_system_resource = std::make_unique<KSystemResource>(kernel);
336 sys_system_resource = std::make_unique<KSystemResource>(kernel);
337
338 // Set the managers for the system resources.
339 app_system_resource->SetManagers(*app_memory_block_manager, *app_block_info_manager,
340 *app_page_table_manager);
341 sys_system_resource->SetManagers(*sys_memory_block_manager, *sys_block_info_manager,
342 *sys_page_table_manager);
276 } 343 }
277 344
278 void InitializeShutdownThreads() { 345 void InitializeShutdownThreads() {
@@ -310,15 +377,18 @@ struct KernelCore::Impl {
310 } 377 }
311 378
312 // Gets the dummy KThread for the caller, allocating a new one if this is the first time 379 // Gets the dummy KThread for the caller, allocating a new one if this is the first time
313 KThread* GetHostDummyThread() { 380 KThread* GetHostDummyThread(KThread* existing_thread) {
314 auto initialize = [this](KThread* thread) { 381 auto initialize = [this](KThread* thread) {
315 ASSERT(KThread::InitializeDummyThread(thread).IsSuccess()); 382 ASSERT(KThread::InitializeDummyThread(thread, nullptr).IsSuccess());
316 thread->SetName(fmt::format("DummyThread:{}", GetHostThreadId())); 383 thread->SetName(fmt::format("DummyThread:{}", GetHostThreadId()));
317 return thread; 384 return thread;
318 }; 385 };
319 386
320 thread_local auto raw_thread = KThread(system.Kernel()); 387 thread_local KThread raw_thread{system.Kernel()};
321 thread_local auto thread = initialize(&raw_thread); 388 thread_local KThread* thread = nullptr;
389 if (thread == nullptr) {
390 thread = (existing_thread == nullptr) ? initialize(&raw_thread) : existing_thread;
391 }
322 392
323 return thread; 393 return thread;
324 } 394 }
@@ -333,9 +403,9 @@ struct KernelCore::Impl {
333 } 403 }
334 404
335 /// Registers a new host thread by allocating a host thread ID for it 405 /// Registers a new host thread by allocating a host thread ID for it
336 void RegisterHostThread() { 406 void RegisterHostThread(KThread* existing_thread) {
337 [[maybe_unused]] const auto this_id = GetHostThreadId(); 407 [[maybe_unused]] const auto this_id = GetHostThreadId();
338 [[maybe_unused]] const auto dummy_thread = GetHostDummyThread(); 408 [[maybe_unused]] const auto dummy_thread = GetHostDummyThread(existing_thread);
339 } 409 }
340 410
341 [[nodiscard]] u32 GetCurrentHostThreadID() { 411 [[nodiscard]] u32 GetCurrentHostThreadID() {
@@ -346,6 +416,8 @@ struct KernelCore::Impl {
346 return this_id; 416 return this_id;
347 } 417 }
348 418
419 static inline thread_local bool is_phantom_mode_for_singlecore{false};
420
349 bool IsPhantomModeForSingleCore() const { 421 bool IsPhantomModeForSingleCore() const {
350 return is_phantom_mode_for_singlecore; 422 return is_phantom_mode_for_singlecore;
351 } 423 }
@@ -364,7 +436,7 @@ struct KernelCore::Impl {
364 KThread* GetCurrentEmuThread() { 436 KThread* GetCurrentEmuThread() {
365 const auto thread_id = GetCurrentHostThreadID(); 437 const auto thread_id = GetCurrentHostThreadID();
366 if (thread_id >= Core::Hardware::NUM_CPU_CORES) { 438 if (thread_id >= Core::Hardware::NUM_CPU_CORES) {
367 return GetHostDummyThread(); 439 return GetHostDummyThread(nullptr);
368 } 440 }
369 441
370 return current_thread; 442 return current_thread;
@@ -454,6 +526,9 @@ struct KernelCore::Impl {
454 ASSERT(memory_layout->GetVirtualMemoryRegionTree().Insert( 526 ASSERT(memory_layout->GetVirtualMemoryRegionTree().Insert(
455 misc_region_start, misc_region_size, KMemoryRegionType_KernelMisc)); 527 misc_region_start, misc_region_size, KMemoryRegionType_KernelMisc));
456 528
529 // Determine if we'll use extra thread resources.
530 const bool use_extra_resources = KSystemControl::Init::ShouldIncreaseThreadResourceLimit();
531
457 // Setup the stack region. 532 // Setup the stack region.
458 constexpr size_t StackRegionSize = 14_MiB; 533 constexpr size_t StackRegionSize = 14_MiB;
459 constexpr size_t StackRegionAlign = KernelAslrAlignment; 534 constexpr size_t StackRegionAlign = KernelAslrAlignment;
@@ -464,7 +539,8 @@ struct KernelCore::Impl {
464 stack_region_start, StackRegionSize, KMemoryRegionType_KernelStack)); 539 stack_region_start, StackRegionSize, KMemoryRegionType_KernelStack));
465 540
466 // Determine the size of the resource region. 541 // Determine the size of the resource region.
467 const size_t resource_region_size = memory_layout->GetResourceRegionSizeForInit(); 542 const size_t resource_region_size =
543 memory_layout->GetResourceRegionSizeForInit(use_extra_resources);
468 544
469 // Determine the size of the slab region. 545 // Determine the size of the slab region.
470 const size_t slab_region_size = 546 const size_t slab_region_size =
@@ -698,54 +774,48 @@ struct KernelCore::Impl {
698 return {}; 774 return {};
699 } 775 }
700 776
701 KClientPort* port = &search->second(system.ServiceManager(), system); 777 return &search->second(system.ServiceManager(), system);
702 RegisterServerObject(&port->GetParent()->GetServerPort());
703 return port;
704 } 778 }
705 779
706 void RegisterServerObject(KAutoObject* server_object) { 780 void RegisterNamedServiceHandler(std::string name, KServerPort* server_port) {
707 std::scoped_lock lk(server_objects_lock); 781 auto search = service_interface_handlers.find(name);
708 server_objects.insert(server_object); 782 if (search == service_interface_handlers.end()) {
709 } 783 return;
784 }
710 785
711 void UnregisterServerObject(KAutoObject* server_object) { 786 search->second(system.ServiceManager(), server_port);
712 std::scoped_lock lk(server_objects_lock);
713 server_objects.erase(server_object);
714 } 787 }
715 788
716 std::weak_ptr<Kernel::ServiceThread> CreateServiceThread(KernelCore& kernel, 789 Kernel::ServiceThread& CreateServiceThread(KernelCore& kernel, const std::string& name) {
717 const std::string& name) { 790 auto* ptr = new ServiceThread(kernel, name);
718 auto service_thread = std::make_shared<Kernel::ServiceThread>(kernel, 1, name);
719 791
720 service_threads_manager.QueueWork( 792 service_threads_manager.QueueWork(
721 [this, service_thread]() { service_threads.emplace(service_thread); }); 793 [this, ptr]() { service_threads.emplace(ptr, std::unique_ptr<ServiceThread>(ptr)); });
722 794
723 return service_thread; 795 return *ptr;
724 } 796 }
725 797
726 void ReleaseServiceThread(std::weak_ptr<Kernel::ServiceThread> service_thread) { 798 void ReleaseServiceThread(Kernel::ServiceThread& service_thread) {
727 if (auto strong_ptr = service_thread.lock()) { 799 auto* ptr = &service_thread;
728 if (strong_ptr == default_service_thread.lock()) {
729 // Nothing to do here, the service is using default_service_thread, which will be
730 // released on shutdown.
731 return;
732 }
733 800
734 service_threads_manager.QueueWork( 801 if (ptr == default_service_thread) {
735 [this, strong_ptr{std::move(strong_ptr)}]() { service_threads.erase(strong_ptr); }); 802 // Nothing to do here, the service is using default_service_thread, which will be
803 // released on shutdown.
804 return;
736 } 805 }
806
807 service_threads_manager.QueueWork([this, ptr]() { service_threads.erase(ptr); });
737 } 808 }
738 809
739 void ClearServiceThreads() { 810 void ClearServiceThreads() {
740 service_threads_manager.QueueWork([this] { 811 service_threads_manager.QueueWork([this] {
741 service_threads.clear(); 812 service_threads.clear();
742 default_service_thread.reset(); 813 default_service_thread = nullptr;
743 service_thread_barrier.Sync(); 814 service_thread_barrier.Sync();
744 }); 815 });
745 service_thread_barrier.Sync(); 816 service_thread_barrier.Sync();
746 } 817 }
747 818
748 std::mutex server_objects_lock;
749 std::mutex registered_objects_lock; 819 std::mutex registered_objects_lock;
750 std::mutex registered_in_use_objects_lock; 820 std::mutex registered_in_use_objects_lock;
751 821
@@ -763,6 +833,8 @@ struct KernelCore::Impl {
763 Init::KSlabResourceCounts slab_resource_counts{}; 833 Init::KSlabResourceCounts slab_resource_counts{};
764 KResourceLimit* system_resource_limit{}; 834 KResourceLimit* system_resource_limit{};
765 835
836 KPageBufferSlabHeap page_buffer_slab_heap;
837
766 std::shared_ptr<Core::Timing::EventType> preemption_event; 838 std::shared_ptr<Core::Timing::EventType> preemption_event;
767 839
768 // This is the kernel's handle table or supervisor handle table which 840 // This is the kernel's handle table or supervisor handle table which
@@ -774,8 +846,8 @@ struct KernelCore::Impl {
774 /// Map of named ports managed by the kernel, which can be retrieved using 846 /// Map of named ports managed by the kernel, which can be retrieved using
775 /// the ConnectToPort SVC. 847 /// the ConnectToPort SVC.
776 std::unordered_map<std::string, ServiceInterfaceFactory> service_interface_factory; 848 std::unordered_map<std::string, ServiceInterfaceFactory> service_interface_factory;
849 std::unordered_map<std::string, ServiceInterfaceHandlerFn> service_interface_handlers;
777 NamedPortTable named_ports; 850 NamedPortTable named_ports;
778 std::unordered_set<KAutoObject*> server_objects;
779 std::unordered_set<KAutoObject*> registered_objects; 851 std::unordered_set<KAutoObject*> registered_objects;
780 std::unordered_set<KAutoObject*> registered_in_use_objects; 852 std::unordered_set<KAutoObject*> registered_in_use_objects;
781 853
@@ -788,10 +860,20 @@ struct KernelCore::Impl {
788 // Kernel memory management 860 // Kernel memory management
789 std::unique_ptr<KMemoryManager> memory_manager; 861 std::unique_ptr<KMemoryManager> memory_manager;
790 862
791 // Dynamic slab managers 863 // Resource managers
792 std::unique_ptr<KDynamicPageManager> dynamic_page_manager; 864 std::unique_ptr<KDynamicPageManager> resource_manager_page_manager;
793 std::unique_ptr<KMemoryBlockSlabHeap> memory_block_heap; 865 std::unique_ptr<KPageTableSlabHeap> page_table_heap;
866 std::unique_ptr<KMemoryBlockSlabHeap> app_memory_block_heap;
867 std::unique_ptr<KMemoryBlockSlabHeap> sys_memory_block_heap;
868 std::unique_ptr<KBlockInfoSlabHeap> block_info_heap;
869 std::unique_ptr<KPageTableManager> app_page_table_manager;
870 std::unique_ptr<KPageTableManager> sys_page_table_manager;
794 std::unique_ptr<KMemoryBlockSlabManager> app_memory_block_manager; 871 std::unique_ptr<KMemoryBlockSlabManager> app_memory_block_manager;
872 std::unique_ptr<KMemoryBlockSlabManager> sys_memory_block_manager;
873 std::unique_ptr<KBlockInfoManager> app_block_info_manager;
874 std::unique_ptr<KBlockInfoManager> sys_block_info_manager;
875 std::unique_ptr<KSystemResource> app_system_resource;
876 std::unique_ptr<KSystemResource> sys_system_resource;
795 877
796 // Shared memory for services 878 // Shared memory for services
797 Kernel::KSharedMemory* hid_shared_mem{}; 879 Kernel::KSharedMemory* hid_shared_mem{};
@@ -804,8 +886,8 @@ struct KernelCore::Impl {
804 std::unique_ptr<KMemoryLayout> memory_layout; 886 std::unique_ptr<KMemoryLayout> memory_layout;
805 887
806 // Threads used for services 888 // Threads used for services
807 std::unordered_set<std::shared_ptr<ServiceThread>> service_threads; 889 std::unordered_map<ServiceThread*, std::unique_ptr<ServiceThread>> service_threads;
808 std::weak_ptr<ServiceThread> default_service_thread; 890 ServiceThread* default_service_thread{};
809 Common::ThreadWorker service_threads_manager; 891 Common::ThreadWorker service_threads_manager;
810 Common::Barrier service_thread_barrier; 892 Common::Barrier service_thread_barrier;
811 893
@@ -814,7 +896,6 @@ struct KernelCore::Impl {
814 896
815 bool is_multicore{}; 897 bool is_multicore{};
816 std::atomic_bool is_shutting_down{}; 898 std::atomic_bool is_shutting_down{};
817 bool is_phantom_mode_for_singlecore{};
818 u32 single_core_thread_id{}; 899 u32 single_core_thread_id{};
819 900
820 std::array<u64, Core::Hardware::NUM_CPU_CORES> svc_ticks{}; 901 std::array<u64, Core::Hardware::NUM_CPU_CORES> svc_ticks{};
@@ -981,16 +1062,17 @@ void KernelCore::RegisterNamedService(std::string name, ServiceInterfaceFactory&
981 impl->service_interface_factory.emplace(std::move(name), factory); 1062 impl->service_interface_factory.emplace(std::move(name), factory);
982} 1063}
983 1064
984KClientPort* KernelCore::CreateNamedServicePort(std::string name) { 1065void KernelCore::RegisterInterfaceForNamedService(std::string name,
985 return impl->CreateNamedServicePort(std::move(name)); 1066 ServiceInterfaceHandlerFn&& handler) {
1067 impl->service_interface_handlers.emplace(std::move(name), handler);
986} 1068}
987 1069
988void KernelCore::RegisterServerObject(KAutoObject* server_object) { 1070KClientPort* KernelCore::CreateNamedServicePort(std::string name) {
989 impl->RegisterServerObject(server_object); 1071 return impl->CreateNamedServicePort(std::move(name));
990} 1072}
991 1073
992void KernelCore::UnregisterServerObject(KAutoObject* server_object) { 1074void KernelCore::RegisterNamedServiceHandler(std::string name, KServerPort* server_port) {
993 impl->UnregisterServerObject(server_object); 1075 impl->RegisterNamedServiceHandler(std::move(name), server_port);
994} 1076}
995 1077
996void KernelCore::RegisterKernelObject(KAutoObject* object) { 1078void KernelCore::RegisterKernelObject(KAutoObject* object) {
@@ -1045,8 +1127,12 @@ void KernelCore::RegisterCoreThread(std::size_t core_id) {
1045 impl->RegisterCoreThread(core_id); 1127 impl->RegisterCoreThread(core_id);
1046} 1128}
1047 1129
1048void KernelCore::RegisterHostThread() { 1130void KernelCore::RegisterHostThread(KThread* existing_thread) {
1049 impl->RegisterHostThread(); 1131 impl->RegisterHostThread(existing_thread);
1132
1133 if (existing_thread != nullptr) {
1134 ASSERT(GetCurrentEmuThread() == existing_thread);
1135 }
1050} 1136}
1051 1137
1052u32 KernelCore::GetCurrentHostThreadID() const { 1138u32 KernelCore::GetCurrentHostThreadID() const {
@@ -1069,12 +1155,12 @@ const KMemoryManager& KernelCore::MemoryManager() const {
1069 return *impl->memory_manager; 1155 return *impl->memory_manager;
1070} 1156}
1071 1157
1072KMemoryBlockSlabManager& KernelCore::GetApplicationMemoryBlockManager() { 1158KSystemResource& KernelCore::GetSystemSystemResource() {
1073 return *impl->app_memory_block_manager; 1159 return *impl->sys_system_resource;
1074} 1160}
1075 1161
1076const KMemoryBlockSlabManager& KernelCore::GetApplicationMemoryBlockManager() const { 1162const KSystemResource& KernelCore::GetSystemSystemResource() const {
1077 return *impl->app_memory_block_manager; 1163 return *impl->sys_system_resource;
1078} 1164}
1079 1165
1080Kernel::KSharedMemory& KernelCore::GetHidSharedMem() { 1166Kernel::KSharedMemory& KernelCore::GetHidSharedMem() {
@@ -1121,16 +1207,28 @@ void KernelCore::Suspend(bool suspended) {
1121 const bool should_suspend{exception_exited || suspended}; 1207 const bool should_suspend{exception_exited || suspended};
1122 const auto activity = should_suspend ? ProcessActivity::Paused : ProcessActivity::Runnable; 1208 const auto activity = should_suspend ? ProcessActivity::Paused : ProcessActivity::Runnable;
1123 1209
1124 for (auto* process : GetProcessList()) { 1210 std::vector<KScopedAutoObject<KThread>> process_threads;
1125 process->SetActivity(activity); 1211 {
1212 KScopedSchedulerLock sl{*this};
1213
1214 if (auto* process = CurrentProcess(); process != nullptr) {
1215 process->SetActivity(activity);
1216
1217 if (!should_suspend) {
1218 // Runnable now; no need to wait.
1219 return;
1220 }
1126 1221
1127 if (should_suspend) {
1128 // Wait for execution to stop
1129 for (auto* thread : process->GetThreadList()) { 1222 for (auto* thread : process->GetThreadList()) {
1130 thread->WaitUntilSuspended(); 1223 process_threads.emplace_back(thread);
1131 } 1224 }
1132 } 1225 }
1133 } 1226 }
1227
1228 // Wait for execution to stop.
1229 for (auto& thread : process_threads) {
1230 thread->WaitUntilSuspended();
1231 }
1134} 1232}
1135 1233
1136void KernelCore::ShutdownCores() { 1234void KernelCore::ShutdownCores() {
@@ -1162,15 +1260,15 @@ void KernelCore::ExitSVCProfile() {
1162 MicroProfileLeave(MICROPROFILE_TOKEN(Kernel_SVC), impl->svc_ticks[CurrentPhysicalCoreIndex()]); 1260 MicroProfileLeave(MICROPROFILE_TOKEN(Kernel_SVC), impl->svc_ticks[CurrentPhysicalCoreIndex()]);
1163} 1261}
1164 1262
1165std::weak_ptr<Kernel::ServiceThread> KernelCore::CreateServiceThread(const std::string& name) { 1263Kernel::ServiceThread& KernelCore::CreateServiceThread(const std::string& name) {
1166 return impl->CreateServiceThread(*this, name); 1264 return impl->CreateServiceThread(*this, name);
1167} 1265}
1168 1266
1169std::weak_ptr<Kernel::ServiceThread> KernelCore::GetDefaultServiceThread() const { 1267Kernel::ServiceThread& KernelCore::GetDefaultServiceThread() const {
1170 return impl->default_service_thread; 1268 return *impl->default_service_thread;
1171} 1269}
1172 1270
1173void KernelCore::ReleaseServiceThread(std::weak_ptr<Kernel::ServiceThread> service_thread) { 1271void KernelCore::ReleaseServiceThread(Kernel::ServiceThread& service_thread) {
1174 impl->ReleaseServiceThread(service_thread); 1272 impl->ReleaseServiceThread(service_thread);
1175} 1273}
1176 1274
diff --git a/src/core/hle/kernel/kernel.h b/src/core/hle/kernel/kernel.h
index 266be2bc4..2e22fe0f6 100644
--- a/src/core/hle/kernel/kernel.h
+++ b/src/core/hle/kernel/kernel.h
@@ -34,22 +34,27 @@ class KClientPort;
34class GlobalSchedulerContext; 34class GlobalSchedulerContext;
35class KAutoObjectWithListContainer; 35class KAutoObjectWithListContainer;
36class KClientSession; 36class KClientSession;
37class KDebug;
38class KDynamicPageManager;
37class KEvent; 39class KEvent;
40class KEventInfo;
38class KHandleTable; 41class KHandleTable;
39class KLinkedListNode; 42class KLinkedListNode;
40class KMemoryBlockSlabManager;
41class KMemoryLayout; 43class KMemoryLayout;
42class KMemoryManager; 44class KMemoryManager;
43class KPageBuffer; 45class KPageBuffer;
46class KPageBufferSlabHeap;
44class KPort; 47class KPort;
45class KProcess; 48class KProcess;
46class KResourceLimit; 49class KResourceLimit;
47class KScheduler; 50class KScheduler;
51class KServerPort;
48class KServerSession; 52class KServerSession;
49class KSession; 53class KSession;
50class KSessionRequest; 54class KSessionRequest;
51class KSharedMemory; 55class KSharedMemory;
52class KSharedMemoryInfo; 56class KSharedMemoryInfo;
57class KSecureSystemResource;
53class KThread; 58class KThread;
54class KThreadLocalPage; 59class KThreadLocalPage;
55class KTransferMemory; 60class KTransferMemory;
@@ -63,6 +68,8 @@ class TimeManager;
63using ServiceInterfaceFactory = 68using ServiceInterfaceFactory =
64 std::function<KClientPort&(Service::SM::ServiceManager&, Core::System&)>; 69 std::function<KClientPort&(Service::SM::ServiceManager&, Core::System&)>;
65 70
71using ServiceInterfaceHandlerFn = std::function<void(Service::SM::ServiceManager&, KServerPort*)>;
72
66namespace Init { 73namespace Init {
67struct KSlabResourceCounts; 74struct KSlabResourceCounts;
68} 75}
@@ -192,16 +199,14 @@ public:
192 /// Registers a named HLE service, passing a factory used to open a port to that service. 199 /// Registers a named HLE service, passing a factory used to open a port to that service.
193 void RegisterNamedService(std::string name, ServiceInterfaceFactory&& factory); 200 void RegisterNamedService(std::string name, ServiceInterfaceFactory&& factory);
194 201
202 /// Registers a setup function for the named HLE service.
203 void RegisterInterfaceForNamedService(std::string name, ServiceInterfaceHandlerFn&& handler);
204
195 /// Opens a port to a service previously registered with RegisterNamedService. 205 /// Opens a port to a service previously registered with RegisterNamedService.
196 KClientPort* CreateNamedServicePort(std::string name); 206 KClientPort* CreateNamedServicePort(std::string name);
197 207
198 /// Registers a server session or port with the gobal emulation state, to be freed on shutdown. 208 /// Accepts a session on a port created by CreateNamedServicePort.
199 /// This is necessary because we do not emulate processes for HLE sessions and ports. 209 void RegisterNamedServiceHandler(std::string name, KServerPort* server_port);
200 void RegisterServerObject(KAutoObject* server_object);
201
202 /// Unregisters a server session or port previously registered with RegisterServerSession when
203 /// it was destroyed during the current emulation session.
204 void UnregisterServerObject(KAutoObject* server_object);
205 210
206 /// Registers all kernel objects with the global emulation state, this is purely for tracking 211 /// Registers all kernel objects with the global emulation state, this is purely for tracking
207 /// leaks after emulation has been shutdown. 212 /// leaks after emulation has been shutdown.
@@ -235,7 +240,7 @@ public:
235 void RegisterCoreThread(std::size_t core_id); 240 void RegisterCoreThread(std::size_t core_id);
236 241
237 /// Register the current thread as a non CPU core thread. 242 /// Register the current thread as a non CPU core thread.
238 void RegisterHostThread(); 243 void RegisterHostThread(KThread* existing_thread = nullptr);
239 244
240 /// Gets the virtual memory manager for the kernel. 245 /// Gets the virtual memory manager for the kernel.
241 KMemoryManager& MemoryManager(); 246 KMemoryManager& MemoryManager();
@@ -243,11 +248,11 @@ public:
243 /// Gets the virtual memory manager for the kernel. 248 /// Gets the virtual memory manager for the kernel.
244 const KMemoryManager& MemoryManager() const; 249 const KMemoryManager& MemoryManager() const;
245 250
246 /// Gets the application memory block manager for the kernel. 251 /// Gets the system resource manager.
247 KMemoryBlockSlabManager& GetApplicationMemoryBlockManager(); 252 KSystemResource& GetSystemSystemResource();
248 253
249 /// Gets the application memory block manager for the kernel. 254 /// Gets the system resource manager.
250 const KMemoryBlockSlabManager& GetApplicationMemoryBlockManager() const; 255 const KSystemResource& GetSystemSystemResource() const;
251 256
252 /// Gets the shared memory object for HID services. 257 /// Gets the shared memory object for HID services.
253 Kernel::KSharedMemory& GetHidSharedMem(); 258 Kernel::KSharedMemory& GetHidSharedMem();
@@ -304,24 +309,24 @@ public:
304 * See GetDefaultServiceThread. 309 * See GetDefaultServiceThread.
305 * @param name String name for the ServerSession creating this thread, used for debug 310 * @param name String name for the ServerSession creating this thread, used for debug
306 * purposes. 311 * purposes.
307 * @returns The a weak pointer newly created service thread. 312 * @returns A reference to the newly created service thread.
308 */ 313 */
309 std::weak_ptr<Kernel::ServiceThread> CreateServiceThread(const std::string& name); 314 Kernel::ServiceThread& CreateServiceThread(const std::string& name);
310 315
311 /** 316 /**
312 * Gets the default host service thread, which executes HLE service requests. Unless service 317 * Gets the default host service thread, which executes HLE service requests. Unless service
313 * requests need to block on the host, the default service thread should be used in favor of 318 * requests need to block on the host, the default service thread should be used in favor of
314 * creating a new service thread. 319 * creating a new service thread.
315 * @returns The a weak pointer for the default service thread. 320 * @returns A reference to the default service thread.
316 */ 321 */
317 std::weak_ptr<Kernel::ServiceThread> GetDefaultServiceThread() const; 322 Kernel::ServiceThread& GetDefaultServiceThread() const;
318 323
319 /** 324 /**
320 * Releases a HLE service thread, instructing KernelCore to free it. This should be called when 325 * Releases a HLE service thread, instructing KernelCore to free it. This should be called when
321 * the ServerSession associated with the thread is destroyed. 326 * the ServerSession associated with the thread is destroyed.
322 * @param service_thread Service thread to release. 327 * @param service_thread Service thread to release.
323 */ 328 */
324 void ReleaseServiceThread(std::weak_ptr<Kernel::ServiceThread> service_thread); 329 void ReleaseServiceThread(Kernel::ServiceThread& service_thread);
325 330
326 /// Workaround for single-core mode when preempting threads while idle. 331 /// Workaround for single-core mode when preempting threads while idle.
327 bool IsPhantomModeForSingleCore() const; 332 bool IsPhantomModeForSingleCore() const;
@@ -363,6 +368,12 @@ public:
363 return slab_heap_container->thread_local_page; 368 return slab_heap_container->thread_local_page;
364 } else if constexpr (std::is_same_v<T, KSessionRequest>) { 369 } else if constexpr (std::is_same_v<T, KSessionRequest>) {
365 return slab_heap_container->session_request; 370 return slab_heap_container->session_request;
371 } else if constexpr (std::is_same_v<T, KSecureSystemResource>) {
372 return slab_heap_container->secure_system_resource;
373 } else if constexpr (std::is_same_v<T, KEventInfo>) {
374 return slab_heap_container->event_info;
375 } else if constexpr (std::is_same_v<T, KDebug>) {
376 return slab_heap_container->debug;
366 } 377 }
367 } 378 }
368 379
@@ -426,6 +437,9 @@ private:
426 KSlabHeap<KPageBuffer> page_buffer; 437 KSlabHeap<KPageBuffer> page_buffer;
427 KSlabHeap<KThreadLocalPage> thread_local_page; 438 KSlabHeap<KThreadLocalPage> thread_local_page;
428 KSlabHeap<KSessionRequest> session_request; 439 KSlabHeap<KSessionRequest> session_request;
440 KSlabHeap<KSecureSystemResource> secure_system_resource;
441 KSlabHeap<KEventInfo> event_info;
442 KSlabHeap<KDebug> debug;
429 }; 443 };
430 444
431 std::unique_ptr<SlabHeapContainer> slab_heap_container; 445 std::unique_ptr<SlabHeapContainer> slab_heap_container;
diff --git a/src/core/hle/kernel/physical_core.cpp b/src/core/hle/kernel/physical_core.cpp
index d4375962f..3044922ac 100644
--- a/src/core/hle/kernel/physical_core.cpp
+++ b/src/core/hle/kernel/physical_core.cpp
@@ -12,7 +12,7 @@ namespace Kernel {
12 12
13PhysicalCore::PhysicalCore(std::size_t core_index_, Core::System& system_, KScheduler& scheduler_) 13PhysicalCore::PhysicalCore(std::size_t core_index_, Core::System& system_, KScheduler& scheduler_)
14 : core_index{core_index_}, system{system_}, scheduler{scheduler_} { 14 : core_index{core_index_}, system{system_}, scheduler{scheduler_} {
15#ifdef ARCHITECTURE_x86_64 15#if defined(ARCHITECTURE_x86_64) || defined(ARCHITECTURE_arm64)
16 // TODO(bunnei): Initialization relies on a core being available. We may later replace this with 16 // TODO(bunnei): Initialization relies on a core being available. We may later replace this with
17 // a 32-bit instance of Dynarmic. This should be abstracted out to a CPU manager. 17 // a 32-bit instance of Dynarmic. This should be abstracted out to a CPU manager.
18 auto& kernel = system.Kernel(); 18 auto& kernel = system.Kernel();
@@ -26,7 +26,7 @@ PhysicalCore::PhysicalCore(std::size_t core_index_, Core::System& system_, KSche
26PhysicalCore::~PhysicalCore() = default; 26PhysicalCore::~PhysicalCore() = default;
27 27
28void PhysicalCore::Initialize([[maybe_unused]] bool is_64_bit) { 28void PhysicalCore::Initialize([[maybe_unused]] bool is_64_bit) {
29#ifdef ARCHITECTURE_x86_64 29#if defined(ARCHITECTURE_x86_64) || defined(ARCHITECTURE_arm64)
30 auto& kernel = system.Kernel(); 30 auto& kernel = system.Kernel();
31 if (!is_64_bit) { 31 if (!is_64_bit) {
32 // We already initialized a 64-bit core, replace with a 32-bit one. 32 // We already initialized a 64-bit core, replace with a 32-bit one.
diff --git a/src/core/hle/kernel/service_thread.cpp b/src/core/hle/kernel/service_thread.cpp
index d23d76706..f5c2ab23f 100644
--- a/src/core/hle/kernel/service_thread.cpp
+++ b/src/core/hle/kernel/service_thread.cpp
@@ -1,15 +1,18 @@
1// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project 1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later 2// SPDX-License-Identifier: GPL-2.0-or-later
3 3
4#include <condition_variable>
5#include <functional> 4#include <functional>
5#include <map>
6#include <mutex> 6#include <mutex>
7#include <thread> 7#include <thread>
8#include <vector> 8#include <vector>
9#include <queue>
10 9
11#include "common/scope_exit.h" 10#include "common/scope_exit.h"
12#include "common/thread.h" 11#include "common/thread.h"
12#include "core/hle/ipc_helpers.h"
13#include "core/hle/kernel/hle_ipc.h"
14#include "core/hle/kernel/k_event.h"
15#include "core/hle/kernel/k_scoped_resource_reservation.h"
13#include "core/hle/kernel/k_session.h" 16#include "core/hle/kernel/k_session.h"
14#include "core/hle/kernel/k_thread.h" 17#include "core/hle/kernel/k_thread.h"
15#include "core/hle/kernel/kernel.h" 18#include "core/hle/kernel/kernel.h"
@@ -19,101 +22,210 @@ namespace Kernel {
19 22
20class ServiceThread::Impl final { 23class ServiceThread::Impl final {
21public: 24public:
22 explicit Impl(KernelCore& kernel, std::size_t num_threads, const std::string& name); 25 explicit Impl(KernelCore& kernel, const std::string& service_name);
23 ~Impl(); 26 ~Impl();
24 27
25 void QueueSyncRequest(KSession& session, std::shared_ptr<HLERequestContext>&& context); 28 void WaitAndProcessImpl();
29 void SessionClosed(KServerSession* server_session,
30 std::shared_ptr<SessionRequestManager> manager);
31 void LoopProcess();
32
33 void RegisterServerSession(KServerSession* session,
34 std::shared_ptr<SessionRequestManager> manager);
26 35
27private: 36private:
28 std::vector<std::jthread> threads; 37 KernelCore& kernel;
29 std::queue<std::function<void()>> requests; 38
30 std::mutex queue_mutex; 39 std::jthread m_host_thread;
31 std::condition_variable_any condition; 40 std::mutex m_session_mutex;
32 const std::string service_name; 41 std::map<KServerSession*, std::shared_ptr<SessionRequestManager>> m_sessions;
42 KEvent* m_wakeup_event;
43 KProcess* m_process;
44 KThread* m_thread;
45 std::atomic<bool> m_shutdown_requested;
46 const std::string m_service_name;
33}; 47};
34 48
35ServiceThread::Impl::Impl(KernelCore& kernel, std::size_t num_threads, const std::string& name) 49void ServiceThread::Impl::WaitAndProcessImpl() {
36 : service_name{name} { 50 // Create local list of waitable sessions.
37 for (std::size_t i = 0; i < num_threads; ++i) { 51 std::vector<KSynchronizationObject*> objs;
38 threads.emplace_back([this, &kernel](std::stop_token stop_token) { 52 std::vector<std::shared_ptr<SessionRequestManager>> managers;
39 Common::SetCurrentThreadName(std::string{service_name}.c_str()); 53
54 {
55 // Lock to get the set.
56 std::scoped_lock lk{m_session_mutex};
57
58 // Reserve the needed quantity.
59 objs.reserve(m_sessions.size() + 1);
60 managers.reserve(m_sessions.size());
61
62 // Copy to our local list.
63 for (const auto& [session, manager] : m_sessions) {
64 objs.push_back(session);
65 managers.push_back(manager);
66 }
40 67
41 // Wait for first request before trying to acquire a render context 68 // Insert the wakeup event at the end.
42 { 69 objs.push_back(&m_wakeup_event->GetReadableEvent());
43 std::unique_lock lock{queue_mutex}; 70 }
44 condition.wait(lock, stop_token, [this] { return !requests.empty(); }); 71
45 } 72 // Wait on the list of sessions.
73 s32 index{-1};
74 Result rc = KSynchronizationObject::Wait(kernel, &index, objs.data(),
75 static_cast<s32>(objs.size()), -1);
76 ASSERT(!rc.IsFailure());
77
78 // If this was the wakeup event, clear it and finish.
79 if (index >= static_cast<s64>(objs.size() - 1)) {
80 m_wakeup_event->Clear();
81 return;
82 }
46 83
47 if (stop_token.stop_requested()) { 84 // This event is from a server session.
48 return; 85 auto* server_session = static_cast<KServerSession*>(objs[index]);
49 } 86 auto& manager = managers[index];
50 87
51 // Allocate a dummy guest thread for this host thread. 88 // Fetch the HLE request context.
52 kernel.RegisterHostThread(); 89 std::shared_ptr<HLERequestContext> context;
90 rc = server_session->ReceiveRequest(&context, manager);
53 91
54 while (true) { 92 // If the session was closed, handle that.
55 std::function<void()> task; 93 if (rc == ResultSessionClosed) {
94 SessionClosed(server_session, manager);
56 95
57 { 96 // Finish.
58 std::unique_lock lock{queue_mutex}; 97 return;
59 condition.wait(lock, stop_token, [this] { return !requests.empty(); }); 98 }
60 99
61 if (stop_token.stop_requested()) { 100 // TODO: handle other cases
62 return; 101 ASSERT(rc == ResultSuccess);
63 }
64 102
65 if (requests.empty()) { 103 // Perform the request.
66 continue; 104 Result service_rc = manager->CompleteSyncRequest(server_session, *context);
67 }
68 105
69 task = std::move(requests.front()); 106 // Reply to the client.
70 requests.pop(); 107 rc = server_session->SendReplyHLE();
71 }
72 108
73 task(); 109 if (rc == ResultSessionClosed || service_rc == IPC::ERR_REMOTE_PROCESS_DEAD) {
74 } 110 SessionClosed(server_session, manager);
75 }); 111 return;
76 } 112 }
113
114 // TODO: handle other cases
115 ASSERT(rc == ResultSuccess);
116 ASSERT(service_rc == ResultSuccess);
77} 117}
78 118
79void ServiceThread::Impl::QueueSyncRequest(KSession& session, 119void ServiceThread::Impl::SessionClosed(KServerSession* server_session,
80 std::shared_ptr<HLERequestContext>&& context) { 120 std::shared_ptr<SessionRequestManager> manager) {
81 { 121 {
82 std::unique_lock lock{queue_mutex}; 122 // Lock to get the set.
123 std::scoped_lock lk{m_session_mutex};
83 124
84 auto* server_session{&session.GetServerSession()}; 125 // Erase the session.
126 ASSERT(m_sessions.erase(server_session) == 1);
127 }
128
129 // Close our reference to the server session.
130 server_session->Close();
131}
132
133void ServiceThread::Impl::LoopProcess() {
134 Common::SetCurrentThreadName(m_service_name.c_str());
135
136 kernel.RegisterHostThread(m_thread);
137
138 while (!m_shutdown_requested.load()) {
139 WaitAndProcessImpl();
140 }
141}
85 142
86 // Open a reference to the session to ensure it is not closes while the service request 143void ServiceThread::Impl::RegisterServerSession(KServerSession* server_session,
87 // completes asynchronously. 144 std::shared_ptr<SessionRequestManager> manager) {
88 server_session->Open(); 145 // Open the server session.
146 server_session->Open();
89 147
90 requests.emplace([server_session, context{std::move(context)}]() { 148 {
91 // Close the reference. 149 // Lock to get the set.
92 SCOPE_EXIT({ server_session->Close(); }); 150 std::scoped_lock lk{m_session_mutex};
93 151
94 // Complete the service request. 152 // Insert the session and manager.
95 server_session->CompleteSyncRequest(*context); 153 m_sessions[server_session] = manager;
96 });
97 } 154 }
98 condition.notify_one(); 155
156 // Signal the wakeup event.
157 m_wakeup_event->Signal();
99} 158}
100 159
101ServiceThread::Impl::~Impl() { 160ServiceThread::Impl::~Impl() {
102 condition.notify_all(); 161 // Shut down the processing thread.
103 for (auto& thread : threads) { 162 m_shutdown_requested.store(true);
104 thread.request_stop(); 163 m_wakeup_event->Signal();
105 thread.join(); 164 m_host_thread.join();
165
166 // Lock mutex.
167 m_session_mutex.lock();
168
169 // Close all remaining sessions.
170 for (const auto& [server_session, manager] : m_sessions) {
171 server_session->Close();
106 } 172 }
173
174 // Destroy remaining managers.
175 m_sessions.clear();
176
177 // Close event.
178 m_wakeup_event->GetReadableEvent().Close();
179 m_wakeup_event->Close();
180
181 // Close thread.
182 m_thread->Close();
183
184 // Close process.
185 m_process->Close();
186}
187
188ServiceThread::Impl::Impl(KernelCore& kernel_, const std::string& service_name)
189 : kernel{kernel_}, m_service_name{service_name} {
190 // Initialize process.
191 m_process = KProcess::Create(kernel);
192 KProcess::Initialize(m_process, kernel.System(), service_name,
193 KProcess::ProcessType::KernelInternal, kernel.GetSystemResourceLimit());
194
195 // Reserve a new event from the process resource limit
196 KScopedResourceReservation event_reservation(m_process, LimitableResource::EventCountMax);
197 ASSERT(event_reservation.Succeeded());
198
199 // Initialize event.
200 m_wakeup_event = KEvent::Create(kernel);
201 m_wakeup_event->Initialize(m_process);
202
203 // Commit the event reservation.
204 event_reservation.Commit();
205
206 // Reserve a new thread from the process resource limit
207 KScopedResourceReservation thread_reservation(m_process, LimitableResource::ThreadCountMax);
208 ASSERT(thread_reservation.Succeeded());
209
210 // Initialize thread.
211 m_thread = KThread::Create(kernel);
212 ASSERT(KThread::InitializeDummyThread(m_thread, m_process).IsSuccess());
213
214 // Commit the thread reservation.
215 thread_reservation.Commit();
216
217 // Start thread.
218 m_host_thread = std::jthread([this] { LoopProcess(); });
107} 219}
108 220
109ServiceThread::ServiceThread(KernelCore& kernel, std::size_t num_threads, const std::string& name) 221ServiceThread::ServiceThread(KernelCore& kernel, const std::string& name)
110 : impl{std::make_unique<Impl>(kernel, num_threads, name)} {} 222 : impl{std::make_unique<Impl>(kernel, name)} {}
111 223
112ServiceThread::~ServiceThread() = default; 224ServiceThread::~ServiceThread() = default;
113 225
114void ServiceThread::QueueSyncRequest(KSession& session, 226void ServiceThread::RegisterServerSession(KServerSession* session,
115 std::shared_ptr<HLERequestContext>&& context) { 227 std::shared_ptr<SessionRequestManager> manager) {
116 impl->QueueSyncRequest(session, std::move(context)); 228 impl->RegisterServerSession(session, manager);
117} 229}
118 230
119} // namespace Kernel 231} // namespace Kernel
diff --git a/src/core/hle/kernel/service_thread.h b/src/core/hle/kernel/service_thread.h
index c5896f2bd..fb4325531 100644
--- a/src/core/hle/kernel/service_thread.h
+++ b/src/core/hle/kernel/service_thread.h
@@ -11,13 +11,15 @@ namespace Kernel {
11class HLERequestContext; 11class HLERequestContext;
12class KernelCore; 12class KernelCore;
13class KSession; 13class KSession;
14class SessionRequestManager;
14 15
15class ServiceThread final { 16class ServiceThread final {
16public: 17public:
17 explicit ServiceThread(KernelCore& kernel, std::size_t num_threads, const std::string& name); 18 explicit ServiceThread(KernelCore& kernel, const std::string& name);
18 ~ServiceThread(); 19 ~ServiceThread();
19 20
20 void QueueSyncRequest(KSession& session, std::shared_ptr<HLERequestContext>&& context); 21 void RegisterServerSession(KServerSession* session,
22 std::shared_ptr<SessionRequestManager> manager);
21 23
22private: 24private:
23 class Impl; 25 class Impl;
diff --git a/src/core/hle/kernel/slab_helpers.h b/src/core/hle/kernel/slab_helpers.h
index 06b51e919..0228ce188 100644
--- a/src/core/hle/kernel/slab_helpers.h
+++ b/src/core/hle/kernel/slab_helpers.h
@@ -53,6 +53,84 @@ public:
53}; 53};
54 54
55template <typename Derived, typename Base> 55template <typename Derived, typename Base>
56class KAutoObjectWithSlabHeap : public Base {
57 static_assert(std::is_base_of<KAutoObject, Base>::value);
58
59private:
60 static Derived* Allocate(KernelCore& kernel) {
61 return kernel.SlabHeap<Derived>().Allocate(kernel);
62 }
63
64 static void Free(KernelCore& kernel, Derived* obj) {
65 kernel.SlabHeap<Derived>().Free(obj);
66 }
67
68public:
69 explicit KAutoObjectWithSlabHeap(KernelCore& kernel_) : Base(kernel_), kernel(kernel_) {}
70 virtual ~KAutoObjectWithSlabHeap() = default;
71
72 virtual void Destroy() override {
73 const bool is_initialized = this->IsInitialized();
74 uintptr_t arg = 0;
75 if (is_initialized) {
76 arg = this->GetPostDestroyArgument();
77 this->Finalize();
78 }
79 Free(kernel, static_cast<Derived*>(this));
80 if (is_initialized) {
81 Derived::PostDestroy(arg);
82 }
83 }
84
85 virtual bool IsInitialized() const {
86 return true;
87 }
88 virtual uintptr_t GetPostDestroyArgument() const {
89 return 0;
90 }
91
92 size_t GetSlabIndex() const {
93 return SlabHeap<Derived>(kernel).GetObjectIndex(static_cast<const Derived*>(this));
94 }
95
96public:
97 static void InitializeSlabHeap(KernelCore& kernel, void* memory, size_t memory_size) {
98 kernel.SlabHeap<Derived>().Initialize(memory, memory_size);
99 }
100
101 static Derived* Create(KernelCore& kernel) {
102 Derived* obj = Allocate(kernel);
103 if (obj != nullptr) {
104 KAutoObject::Create(obj);
105 }
106 return obj;
107 }
108
109 static size_t GetObjectSize(KernelCore& kernel) {
110 return kernel.SlabHeap<Derived>().GetObjectSize();
111 }
112
113 static size_t GetSlabHeapSize(KernelCore& kernel) {
114 return kernel.SlabHeap<Derived>().GetSlabHeapSize();
115 }
116
117 static size_t GetPeakIndex(KernelCore& kernel) {
118 return kernel.SlabHeap<Derived>().GetPeakIndex();
119 }
120
121 static uintptr_t GetSlabHeapAddress(KernelCore& kernel) {
122 return kernel.SlabHeap<Derived>().GetSlabHeapAddress();
123 }
124
125 static size_t GetNumRemaining(KernelCore& kernel) {
126 return kernel.SlabHeap<Derived>().GetNumRemaining();
127 }
128
129protected:
130 KernelCore& kernel;
131};
132
133template <typename Derived, typename Base>
56class KAutoObjectWithSlabHeapAndContainer : public Base { 134class KAutoObjectWithSlabHeapAndContainer : public Base {
57 static_assert(std::is_base_of<KAutoObjectWithList, Base>::value); 135 static_assert(std::is_base_of<KAutoObjectWithList, Base>::value);
58 136
diff --git a/src/core/hle/kernel/svc.cpp b/src/core/hle/kernel/svc.cpp
index 4aca5b27d..9962ad171 100644
--- a/src/core/hle/kernel/svc.cpp
+++ b/src/core/hle/kernel/svc.cpp
@@ -24,6 +24,7 @@
24#include "core/hle/kernel/k_memory_block.h" 24#include "core/hle/kernel/k_memory_block.h"
25#include "core/hle/kernel/k_memory_layout.h" 25#include "core/hle/kernel/k_memory_layout.h"
26#include "core/hle/kernel/k_page_table.h" 26#include "core/hle/kernel/k_page_table.h"
27#include "core/hle/kernel/k_port.h"
27#include "core/hle/kernel/k_process.h" 28#include "core/hle/kernel/k_process.h"
28#include "core/hle/kernel/k_readable_event.h" 29#include "core/hle/kernel/k_readable_event.h"
29#include "core/hle/kernel/k_resource_limit.h" 30#include "core/hle/kernel/k_resource_limit.h"
@@ -266,7 +267,7 @@ Result CreateSession(Core::System& system, Handle* out_server, Handle* out_clien
266 267
267 // Reserve a new session from the process resource limit. 268 // Reserve a new session from the process resource limit.
268 // FIXME: LimitableResource_SessionCountMax 269 // FIXME: LimitableResource_SessionCountMax
269 KScopedResourceReservation session_reservation(&process, LimitableResource::Sessions); 270 KScopedResourceReservation session_reservation(&process, LimitableResource::SessionCountMax);
270 if (session_reservation.Succeeded()) { 271 if (session_reservation.Succeeded()) {
271 session = T::Create(system.Kernel()); 272 session = T::Create(system.Kernel());
272 } else { 273 } else {
@@ -297,7 +298,7 @@ Result CreateSession(Core::System& system, Handle* out_server, Handle* out_clien
297 298
298 // We successfully allocated a session, so add the object we allocated to the resource 299 // We successfully allocated a session, so add the object we allocated to the resource
299 // limit. 300 // limit.
300 // system.Kernel().GetSystemResourceLimit().Reserve(LimitableResource::Sessions, 1); 301 // system.Kernel().GetSystemResourceLimit().Reserve(LimitableResource::SessionCountMax, 1);
301 } 302 }
302 303
303 // Check that we successfully created a session. 304 // Check that we successfully created a session.
@@ -382,9 +383,9 @@ static Result ConnectToNamedPort(Core::System& system, Handle* out, VAddr port_n
382 383
383 // Create a session. 384 // Create a session.
384 KClientSession* session{}; 385 KClientSession* session{};
385 R_TRY(port->CreateSession(std::addressof(session), 386 R_TRY(port->CreateSession(std::addressof(session)));
386 std::make_shared<SessionRequestManager>(kernel))); 387
387 port->Close(); 388 kernel.RegisterNamedServiceHandler(port_name, &port->GetParent()->GetServerPort());
388 389
389 // Register the session in the table, close the extra reference. 390 // Register the session in the table, close the extra reference.
390 handle_table.Register(*out, session); 391 handle_table.Register(*out, session);
@@ -655,27 +656,12 @@ static Result ArbitrateUnlock32(Core::System& system, u32 address) {
655 return ArbitrateUnlock(system, address); 656 return ArbitrateUnlock(system, address);
656} 657}
657 658
658enum class BreakType : u32 {
659 Panic = 0,
660 AssertionFailed = 1,
661 PreNROLoad = 3,
662 PostNROLoad = 4,
663 PreNROUnload = 5,
664 PostNROUnload = 6,
665 CppException = 7,
666};
667
668struct BreakReason {
669 union {
670 u32 raw;
671 BitField<0, 30, BreakType> break_type;
672 BitField<31, 1, u32> signal_debugger;
673 };
674};
675
676/// Break program execution 659/// Break program execution
677static void Break(Core::System& system, u32 reason, u64 info1, u64 info2) { 660static void Break(Core::System& system, u32 reason, u64 info1, u64 info2) {
678 BreakReason break_reason{reason}; 661 BreakReason break_reason =
662 static_cast<BreakReason>(reason & ~static_cast<u32>(BreakReason::NotificationOnlyFlag));
663 bool notification_only = (reason & static_cast<u32>(BreakReason::NotificationOnlyFlag)) != 0;
664
679 bool has_dumped_buffer{}; 665 bool has_dumped_buffer{};
680 std::vector<u8> debug_buffer; 666 std::vector<u8> debug_buffer;
681 667
@@ -704,57 +690,56 @@ static void Break(Core::System& system, u32 reason, u64 info1, u64 info2) {
704 } 690 }
705 has_dumped_buffer = true; 691 has_dumped_buffer = true;
706 }; 692 };
707 switch (break_reason.break_type) { 693 switch (break_reason) {
708 case BreakType::Panic: 694 case BreakReason::Panic:
709 LOG_CRITICAL(Debug_Emulated, "Signalling debugger, PANIC! info1=0x{:016X}, info2=0x{:016X}", 695 LOG_CRITICAL(Debug_Emulated, "Userspace PANIC! info1=0x{:016X}, info2=0x{:016X}", info1,
710 info1, info2); 696 info2);
711 handle_debug_buffer(info1, info2); 697 handle_debug_buffer(info1, info2);
712 break; 698 break;
713 case BreakType::AssertionFailed: 699 case BreakReason::Assert:
714 LOG_CRITICAL(Debug_Emulated, 700 LOG_CRITICAL(Debug_Emulated, "Userspace Assertion failed! info1=0x{:016X}, info2=0x{:016X}",
715 "Signalling debugger, Assertion failed! info1=0x{:016X}, info2=0x{:016X}",
716 info1, info2); 701 info1, info2);
717 handle_debug_buffer(info1, info2); 702 handle_debug_buffer(info1, info2);
718 break; 703 break;
719 case BreakType::PreNROLoad: 704 case BreakReason::User:
720 LOG_WARNING( 705 LOG_WARNING(Debug_Emulated, "Userspace Break! 0x{:016X} with size 0x{:016X}", info1, info2);
721 Debug_Emulated, 706 handle_debug_buffer(info1, info2);
722 "Signalling debugger, Attempting to load an NRO at 0x{:016X} with size 0x{:016X}",
723 info1, info2);
724 break; 707 break;
725 case BreakType::PostNROLoad: 708 case BreakReason::PreLoadDll:
726 LOG_WARNING(Debug_Emulated, 709 LOG_INFO(Debug_Emulated,
727 "Signalling debugger, Loaded an NRO at 0x{:016X} with size 0x{:016X}", info1, 710 "Userspace Attempting to load an NRO at 0x{:016X} with size 0x{:016X}", info1,
728 info2); 711 info2);
729 break; 712 break;
730 case BreakType::PreNROUnload: 713 case BreakReason::PostLoadDll:
731 LOG_WARNING( 714 LOG_INFO(Debug_Emulated, "Userspace Loaded an NRO at 0x{:016X} with size 0x{:016X}", info1,
732 Debug_Emulated, 715 info2);
733 "Signalling debugger, Attempting to unload an NRO at 0x{:016X} with size 0x{:016X}",
734 info1, info2);
735 break; 716 break;
736 case BreakType::PostNROUnload: 717 case BreakReason::PreUnloadDll:
737 LOG_WARNING(Debug_Emulated, 718 LOG_INFO(Debug_Emulated,
738 "Signalling debugger, Unloaded an NRO at 0x{:016X} with size 0x{:016X}", info1, 719 "Userspace Attempting to unload an NRO at 0x{:016X} with size 0x{:016X}", info1,
739 info2); 720 info2);
740 break; 721 break;
741 case BreakType::CppException: 722 case BreakReason::PostUnloadDll:
723 LOG_INFO(Debug_Emulated, "Userspace Unloaded an NRO at 0x{:016X} with size 0x{:016X}",
724 info1, info2);
725 break;
726 case BreakReason::CppException:
742 LOG_CRITICAL(Debug_Emulated, "Signalling debugger. Uncaught C++ exception encountered."); 727 LOG_CRITICAL(Debug_Emulated, "Signalling debugger. Uncaught C++ exception encountered.");
743 break; 728 break;
744 default: 729 default:
745 LOG_WARNING( 730 LOG_WARNING(
746 Debug_Emulated, 731 Debug_Emulated,
747 "Signalling debugger, Unknown break reason {}, info1=0x{:016X}, info2=0x{:016X}", 732 "Signalling debugger, Unknown break reason {:#X}, info1=0x{:016X}, info2=0x{:016X}",
748 static_cast<u32>(break_reason.break_type.Value()), info1, info2); 733 reason, info1, info2);
749 handle_debug_buffer(info1, info2); 734 handle_debug_buffer(info1, info2);
750 break; 735 break;
751 } 736 }
752 737
753 system.GetReporter().SaveSvcBreakReport( 738 system.GetReporter().SaveSvcBreakReport(reason, notification_only, info1, info2,
754 static_cast<u32>(break_reason.break_type.Value()), break_reason.signal_debugger.As<bool>(), 739 has_dumped_buffer ? std::make_optional(debug_buffer)
755 info1, info2, has_dumped_buffer ? std::make_optional(debug_buffer) : std::nullopt); 740 : std::nullopt);
756 741
757 if (!break_reason.signal_debugger) { 742 if (!notification_only) {
758 LOG_CRITICAL( 743 LOG_CRITICAL(
759 Debug_Emulated, 744 Debug_Emulated,
760 "Emulated program broke execution! reason=0x{:016X}, info1=0x{:016X}, info2=0x{:016X}", 745 "Emulated program broke execution! reason=0x{:016X}, info1=0x{:016X}, info2=0x{:016X}",
@@ -1715,13 +1700,13 @@ static Result QueryProcessMemory(Core::System& system, VAddr memory_info_address
1715 auto& memory{system.Memory()}; 1700 auto& memory{system.Memory()};
1716 const auto memory_info{process->PageTable().QueryInfo(address).GetSvcMemoryInfo()}; 1701 const auto memory_info{process->PageTable().QueryInfo(address).GetSvcMemoryInfo()};
1717 1702
1718 memory.Write64(memory_info_address + 0x00, memory_info.addr); 1703 memory.Write64(memory_info_address + 0x00, memory_info.base_address);
1719 memory.Write64(memory_info_address + 0x08, memory_info.size); 1704 memory.Write64(memory_info_address + 0x08, memory_info.size);
1720 memory.Write32(memory_info_address + 0x10, static_cast<u32>(memory_info.state) & 0xff); 1705 memory.Write32(memory_info_address + 0x10, static_cast<u32>(memory_info.state) & 0xff);
1721 memory.Write32(memory_info_address + 0x14, static_cast<u32>(memory_info.attr)); 1706 memory.Write32(memory_info_address + 0x14, static_cast<u32>(memory_info.attribute));
1722 memory.Write32(memory_info_address + 0x18, static_cast<u32>(memory_info.perm)); 1707 memory.Write32(memory_info_address + 0x18, static_cast<u32>(memory_info.permission));
1723 memory.Write32(memory_info_address + 0x1c, memory_info.ipc_refcount); 1708 memory.Write32(memory_info_address + 0x1c, memory_info.ipc_count);
1724 memory.Write32(memory_info_address + 0x20, memory_info.device_refcount); 1709 memory.Write32(memory_info_address + 0x20, memory_info.device_count);
1725 memory.Write32(memory_info_address + 0x24, 0); 1710 memory.Write32(memory_info_address + 0x24, 0);
1726 1711
1727 // Page info appears to be currently unused by the kernel and is always set to zero. 1712 // Page info appears to be currently unused by the kernel and is always set to zero.
@@ -1942,7 +1927,7 @@ static Result CreateThread(Core::System& system, Handle* out_handle, VAddr entry
1942 1927
1943 // Reserve a new thread from the process resource limit (waiting up to 100ms). 1928 // Reserve a new thread from the process resource limit (waiting up to 100ms).
1944 KScopedResourceReservation thread_reservation( 1929 KScopedResourceReservation thread_reservation(
1945 kernel.CurrentProcess(), LimitableResource::Threads, 1, 1930 kernel.CurrentProcess(), LimitableResource::ThreadCountMax, 1,
1946 system.CoreTiming().GetGlobalTimeNs().count() + 100000000); 1931 system.CoreTiming().GetGlobalTimeNs().count() + 100000000);
1947 if (!thread_reservation.Succeeded()) { 1932 if (!thread_reservation.Succeeded()) {
1948 LOG_ERROR(Kernel_SVC, "Could not reserve a new thread"); 1933 LOG_ERROR(Kernel_SVC, "Could not reserve a new thread");
@@ -2246,7 +2231,7 @@ static u64 GetSystemTick(Core::System& system) {
2246 auto& core_timing = system.CoreTiming(); 2231 auto& core_timing = system.CoreTiming();
2247 2232
2248 // Returns the value of cntpct_el0 (https://switchbrew.org/wiki/SVC#svcGetSystemTick) 2233 // Returns the value of cntpct_el0 (https://switchbrew.org/wiki/SVC#svcGetSystemTick)
2249 const u64 result{system.CoreTiming().GetClockTicks()}; 2234 const u64 result{core_timing.GetClockTicks()};
2250 2235
2251 if (!system.Kernel().IsMulticore()) { 2236 if (!system.Kernel().IsMulticore()) {
2252 core_timing.AddTicks(400U); 2237 core_timing.AddTicks(400U);
@@ -2343,7 +2328,7 @@ static Result CreateTransferMemory(Core::System& system, Handle* out, VAddr addr
2343 2328
2344 // Reserve a new transfer memory from the process resource limit. 2329 // Reserve a new transfer memory from the process resource limit.
2345 KScopedResourceReservation trmem_reservation(kernel.CurrentProcess(), 2330 KScopedResourceReservation trmem_reservation(kernel.CurrentProcess(),
2346 LimitableResource::TransferMemory); 2331 LimitableResource::TransferMemoryCountMax);
2347 R_UNLESS(trmem_reservation.Succeeded(), ResultLimitReached); 2332 R_UNLESS(trmem_reservation.Succeeded(), ResultLimitReached);
2348 2333
2349 // Create the transfer memory. 2334 // Create the transfer memory.
@@ -2495,7 +2480,7 @@ static Result CreateEvent(Core::System& system, Handle* out_write, Handle* out_r
2495 2480
2496 // Reserve a new event from the process resource limit 2481 // Reserve a new event from the process resource limit
2497 KScopedResourceReservation event_reservation(kernel.CurrentProcess(), 2482 KScopedResourceReservation event_reservation(kernel.CurrentProcess(),
2498 LimitableResource::Events); 2483 LimitableResource::EventCountMax);
2499 R_UNLESS(event_reservation.Succeeded(), ResultLimitReached); 2484 R_UNLESS(event_reservation.Succeeded(), ResultLimitReached);
2500 2485
2501 // Create a new event. 2486 // Create a new event.
@@ -2538,11 +2523,6 @@ static Result CreateEvent32(Core::System& system, Handle* out_write, Handle* out
2538static Result GetProcessInfo(Core::System& system, u64* out, Handle process_handle, u32 type) { 2523static Result GetProcessInfo(Core::System& system, u64* out, Handle process_handle, u32 type) {
2539 LOG_DEBUG(Kernel_SVC, "called, handle=0x{:08X}, type=0x{:X}", process_handle, type); 2524 LOG_DEBUG(Kernel_SVC, "called, handle=0x{:08X}, type=0x{:X}", process_handle, type);
2540 2525
2541 // This function currently only allows retrieving a process' status.
2542 enum class InfoType {
2543 Status,
2544 };
2545
2546 const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable(); 2526 const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable();
2547 KScopedAutoObject process = handle_table.GetObject<KProcess>(process_handle); 2527 KScopedAutoObject process = handle_table.GetObject<KProcess>(process_handle);
2548 if (process.IsNull()) { 2528 if (process.IsNull()) {
@@ -2551,9 +2531,9 @@ static Result GetProcessInfo(Core::System& system, u64* out, Handle process_hand
2551 return ResultInvalidHandle; 2531 return ResultInvalidHandle;
2552 } 2532 }
2553 2533
2554 const auto info_type = static_cast<InfoType>(type); 2534 const auto info_type = static_cast<ProcessInfoType>(type);
2555 if (info_type != InfoType::Status) { 2535 if (info_type != ProcessInfoType::ProcessState) {
2556 LOG_ERROR(Kernel_SVC, "Expected info_type to be Status but got {} instead", type); 2536 LOG_ERROR(Kernel_SVC, "Expected info_type to be ProcessState but got {} instead", type);
2557 return ResultInvalidEnumValue; 2537 return ResultInvalidEnumValue;
2558 } 2538 }
2559 2539
diff --git a/src/core/hle/kernel/svc_results.h b/src/core/hle/kernel/svc_results.h
index f27cade33..b7ca53085 100644
--- a/src/core/hle/kernel/svc_results.h
+++ b/src/core/hle/kernel/svc_results.h
@@ -37,6 +37,7 @@ constexpr Result ResultInvalidState{ErrorModule::Kernel, 125};
37constexpr Result ResultReservedUsed{ErrorModule::Kernel, 126}; 37constexpr Result ResultReservedUsed{ErrorModule::Kernel, 126};
38constexpr Result ResultPortClosed{ErrorModule::Kernel, 131}; 38constexpr Result ResultPortClosed{ErrorModule::Kernel, 131};
39constexpr Result ResultLimitReached{ErrorModule::Kernel, 132}; 39constexpr Result ResultLimitReached{ErrorModule::Kernel, 132};
40constexpr Result ResultOutOfAddressSpace{ErrorModule::Kernel, 259};
40constexpr Result ResultInvalidId{ErrorModule::Kernel, 519}; 41constexpr Result ResultInvalidId{ErrorModule::Kernel, 519};
41 42
42} // namespace Kernel 43} // namespace Kernel
diff --git a/src/core/hle/kernel/svc_types.h b/src/core/hle/kernel/svc_types.h
index abb9847fe..33eebcef6 100644
--- a/src/core/hle/kernel/svc_types.h
+++ b/src/core/hle/kernel/svc_types.h
@@ -8,6 +8,8 @@
8 8
9namespace Kernel::Svc { 9namespace Kernel::Svc {
10 10
11using Handle = u32;
12
11enum class MemoryState : u32 { 13enum class MemoryState : u32 {
12 Free = 0x00, 14 Free = 0x00,
13 Io = 0x01, 15 Io = 0x01,
@@ -22,8 +24,8 @@ enum class MemoryState : u32 {
22 Ipc = 0x0A, 24 Ipc = 0x0A,
23 Stack = 0x0B, 25 Stack = 0x0B,
24 ThreadLocal = 0x0C, 26 ThreadLocal = 0x0C,
25 Transferred = 0x0D, 27 Transfered = 0x0D,
26 SharedTransferred = 0x0E, 28 SharedTransfered = 0x0E,
27 SharedCode = 0x0F, 29 SharedCode = 0x0F,
28 Inaccessible = 0x10, 30 Inaccessible = 0x10,
29 NonSecureIpc = 0x11, 31 NonSecureIpc = 0x11,
@@ -32,6 +34,7 @@ enum class MemoryState : u32 {
32 GeneratedCode = 0x14, 34 GeneratedCode = 0x14,
33 CodeOut = 0x15, 35 CodeOut = 0x15,
34 Coverage = 0x16, 36 Coverage = 0x16,
37 Insecure = 0x17,
35}; 38};
36DECLARE_ENUM_FLAG_OPERATORS(MemoryState); 39DECLARE_ENUM_FLAG_OPERATORS(MemoryState);
37 40
@@ -54,17 +57,6 @@ enum class MemoryPermission : u32 {
54}; 57};
55DECLARE_ENUM_FLAG_OPERATORS(MemoryPermission); 58DECLARE_ENUM_FLAG_OPERATORS(MemoryPermission);
56 59
57struct MemoryInfo {
58 u64 addr{};
59 u64 size{};
60 MemoryState state{};
61 MemoryAttribute attr{};
62 MemoryPermission perm{};
63 u32 ipc_refcount{};
64 u32 device_refcount{};
65 u32 padding{};
66};
67
68enum class SignalType : u32 { 60enum class SignalType : u32 {
69 Signal = 0, 61 Signal = 0,
70 SignalAndIncrementIfEqual = 1, 62 SignalAndIncrementIfEqual = 1,
@@ -83,6 +75,13 @@ enum class YieldType : s64 {
83 ToAnyThread = -2, 75 ToAnyThread = -2,
84}; 76};
85 77
78enum class ThreadExitReason : u32 {
79 ExitThread = 0,
80 TerminateThread = 1,
81 ExitProcess = 2,
82 TerminateProcess = 3,
83};
84
86enum class ThreadActivity : u32 { 85enum class ThreadActivity : u32 {
87 Runnable = 0, 86 Runnable = 0,
88 Paused = 1, 87 Paused = 1,
@@ -108,6 +107,489 @@ enum class ProcessState : u32 {
108 DebugBreak = 7, 107 DebugBreak = 7,
109}; 108};
110 109
110enum class ProcessExitReason : u32 {
111 ExitProcess = 0,
112 TerminateProcess = 1,
113 Exception = 2,
114};
115
111constexpr inline size_t ThreadLocalRegionSize = 0x200; 116constexpr inline size_t ThreadLocalRegionSize = 0x200;
112 117
118struct PageInfo {
119 u32 flags;
120};
121
122// Info Types.
123enum class InfoType : u32 {
124 CoreMask = 0,
125 PriorityMask = 1,
126 AliasRegionAddress = 2,
127 AliasRegionSize = 3,
128 HeapRegionAddress = 4,
129 HeapRegionSize = 5,
130 TotalMemorySize = 6,
131 UsedMemorySize = 7,
132 DebuggerAttached = 8,
133 ResourceLimit = 9,
134 IdleTickCount = 10,
135 RandomEntropy = 11,
136 AslrRegionAddress = 12,
137 AslrRegionSize = 13,
138 StackRegionAddress = 14,
139 StackRegionSize = 15,
140 SystemResourceSizeTotal = 16,
141 SystemResourceSizeUsed = 17,
142 ProgramId = 18,
143 InitialProcessIdRange = 19,
144 UserExceptionContextAddress = 20,
145 TotalNonSystemMemorySize = 21,
146 UsedNonSystemMemorySize = 22,
147 IsApplication = 23,
148 FreeThreadCount = 24,
149 ThreadTickCount = 25,
150 IsSvcPermitted = 26,
151
152 MesosphereMeta = 65000,
153 MesosphereCurrentProcess = 65001,
154};
155
156enum class BreakReason : u32 {
157 Panic = 0,
158 Assert = 1,
159 User = 2,
160 PreLoadDll = 3,
161 PostLoadDll = 4,
162 PreUnloadDll = 5,
163 PostUnloadDll = 6,
164 CppException = 7,
165
166 NotificationOnlyFlag = 0x80000000,
167};
168
169enum class DebugEvent : u32 {
170 CreateProcess = 0,
171 CreateThread = 1,
172 ExitProcess = 2,
173 ExitThread = 3,
174 Exception = 4,
175};
176
177enum class DebugThreadParam : u32 {
178 Priority = 0,
179 State = 1,
180 IdealCore = 2,
181 CurrentCore = 3,
182 AffinityMask = 4,
183};
184
185enum class DebugException : u32 {
186 UndefinedInstruction = 0,
187 InstructionAbort = 1,
188 DataAbort = 2,
189 AlignmentFault = 3,
190 DebuggerAttached = 4,
191 BreakPoint = 5,
192 UserBreak = 6,
193 DebuggerBreak = 7,
194 UndefinedSystemCall = 8,
195 MemorySystemError = 9,
196};
197
198enum class DebugEventFlag : u32 {
199 Stopped = (1u << 0),
200};
201
202enum class BreakPointType : u32 {
203 HardwareInstruction = 0,
204 HardwareData = 1,
205};
206
207enum class HardwareBreakPointRegisterName : u32 {
208 I0 = 0,
209 I1 = 1,
210 I2 = 2,
211 I3 = 3,
212 I4 = 4,
213 I5 = 5,
214 I6 = 6,
215 I7 = 7,
216 I8 = 8,
217 I9 = 9,
218 I10 = 10,
219 I11 = 11,
220 I12 = 12,
221 I13 = 13,
222 I14 = 14,
223 I15 = 15,
224 D0 = 16,
225 D1 = 17,
226 D2 = 18,
227 D3 = 19,
228 D4 = 20,
229 D5 = 21,
230 D6 = 22,
231 D7 = 23,
232 D8 = 24,
233 D9 = 25,
234 D10 = 26,
235 D11 = 27,
236 D12 = 28,
237 D13 = 29,
238 D14 = 30,
239 D15 = 31,
240};
241
242namespace lp64 {
243struct LastThreadContext {
244 u64 fp;
245 u64 sp;
246 u64 lr;
247 u64 pc;
248};
249
250struct PhysicalMemoryInfo {
251 PAddr physical_address;
252 u64 virtual_address;
253 u64 size;
254};
255
256struct DebugInfoCreateProcess {
257 u64 program_id;
258 u64 process_id;
259 std::array<char, 0xC> name;
260 u32 flags;
261 u64 user_exception_context_address; // 5.0.0+
262};
263
264struct DebugInfoCreateThread {
265 u64 thread_id;
266 u64 tls_address;
267 // Removed in 11.0.0 u64 entrypoint;
268};
269
270struct DebugInfoExitProcess {
271 ProcessExitReason reason;
272};
273
274struct DebugInfoExitThread {
275 ThreadExitReason reason;
276};
277
278struct DebugInfoUndefinedInstructionException {
279 u32 insn;
280};
281
282struct DebugInfoDataAbortException {
283 u64 address;
284};
285
286struct DebugInfoAlignmentFaultException {
287 u64 address;
288};
289
290struct DebugInfoBreakPointException {
291 BreakPointType type;
292 u64 address;
293};
294
295struct DebugInfoUserBreakException {
296 BreakReason break_reason;
297 u64 address;
298 u64 size;
299};
300
301struct DebugInfoDebuggerBreakException {
302 std::array<u64, 4> active_thread_ids;
303};
304
305struct DebugInfoUndefinedSystemCallException {
306 u32 id;
307};
308
309union DebugInfoSpecificException {
310 DebugInfoUndefinedInstructionException undefined_instruction;
311 DebugInfoDataAbortException data_abort;
312 DebugInfoAlignmentFaultException alignment_fault;
313 DebugInfoBreakPointException break_point;
314 DebugInfoUserBreakException user_break;
315 DebugInfoDebuggerBreakException debugger_break;
316 DebugInfoUndefinedSystemCallException undefined_system_call;
317 u64 raw;
318};
319
320struct DebugInfoException {
321 DebugException type;
322 u64 address;
323 DebugInfoSpecificException specific;
324};
325
326union DebugInfo {
327 DebugInfoCreateProcess create_process;
328 DebugInfoCreateThread create_thread;
329 DebugInfoExitProcess exit_process;
330 DebugInfoExitThread exit_thread;
331 DebugInfoException exception;
332};
333
334struct DebugEventInfo {
335 DebugEvent type;
336 u32 flags;
337 u64 thread_id;
338 DebugInfo info;
339};
340static_assert(sizeof(DebugEventInfo) >= 0x40);
341
342struct SecureMonitorArguments {
343 std::array<u64, 8> r;
344};
345static_assert(sizeof(SecureMonitorArguments) == 0x40);
346} // namespace lp64
347
348namespace ilp32 {
349struct LastThreadContext {
350 u32 fp;
351 u32 sp;
352 u32 lr;
353 u32 pc;
354};
355
356struct PhysicalMemoryInfo {
357 PAddr physical_address;
358 u32 virtual_address;
359 u32 size;
360};
361
362struct DebugInfoCreateProcess {
363 u64 program_id;
364 u64 process_id;
365 std::array<char, 0xC> name;
366 u32 flags;
367 u32 user_exception_context_address; // 5.0.0+
368};
369
370struct DebugInfoCreateThread {
371 u64 thread_id;
372 u32 tls_address;
373 // Removed in 11.0.0 u32 entrypoint;
374};
375
376struct DebugInfoExitProcess {
377 ProcessExitReason reason;
378};
379
380struct DebugInfoExitThread {
381 ThreadExitReason reason;
382};
383
384struct DebugInfoUndefinedInstructionException {
385 u32 insn;
386};
387
388struct DebugInfoDataAbortException {
389 u32 address;
390};
391
392struct DebugInfoAlignmentFaultException {
393 u32 address;
394};
395
396struct DebugInfoBreakPointException {
397 BreakPointType type;
398 u32 address;
399};
400
401struct DebugInfoUserBreakException {
402 BreakReason break_reason;
403 u32 address;
404 u32 size;
405};
406
407struct DebugInfoDebuggerBreakException {
408 std::array<u64, 4> active_thread_ids;
409};
410
411struct DebugInfoUndefinedSystemCallException {
412 u32 id;
413};
414
415union DebugInfoSpecificException {
416 DebugInfoUndefinedInstructionException undefined_instruction;
417 DebugInfoDataAbortException data_abort;
418 DebugInfoAlignmentFaultException alignment_fault;
419 DebugInfoBreakPointException break_point;
420 DebugInfoUserBreakException user_break;
421 DebugInfoDebuggerBreakException debugger_break;
422 DebugInfoUndefinedSystemCallException undefined_system_call;
423 u64 raw;
424};
425
426struct DebugInfoException {
427 DebugException type;
428 u32 address;
429 DebugInfoSpecificException specific;
430};
431
432union DebugInfo {
433 DebugInfoCreateProcess create_process;
434 DebugInfoCreateThread create_thread;
435 DebugInfoExitProcess exit_process;
436 DebugInfoExitThread exit_thread;
437 DebugInfoException exception;
438};
439
440struct DebugEventInfo {
441 DebugEvent type;
442 u32 flags;
443 u64 thread_id;
444 DebugInfo info;
445};
446
447struct SecureMonitorArguments {
448 std::array<u32, 8> r;
449};
450static_assert(sizeof(SecureMonitorArguments) == 0x20);
451} // namespace ilp32
452
453struct ThreadContext {
454 std::array<u64, 29> r;
455 u64 fp;
456 u64 lr;
457 u64 sp;
458 u64 pc;
459 u32 pstate;
460 u32 padding;
461 std::array<u128, 32> v;
462 u32 fpcr;
463 u32 fpsr;
464 u64 tpidr;
465};
466static_assert(sizeof(ThreadContext) == 0x320);
467
468struct MemoryInfo {
469 u64 base_address;
470 u64 size;
471 MemoryState state;
472 MemoryAttribute attribute;
473 MemoryPermission permission;
474 u32 ipc_count;
475 u32 device_count;
476 u32 padding;
477};
478
479enum class LimitableResource : u32 {
480 PhysicalMemoryMax = 0,
481 ThreadCountMax = 1,
482 EventCountMax = 2,
483 TransferMemoryCountMax = 3,
484 SessionCountMax = 4,
485 Count,
486};
487
488enum class IoPoolType : u32 {
489 // Not supported.
490 Count = 0,
491};
492
493enum class MemoryMapping : u32 {
494 IoRegister = 0,
495 Uncached = 1,
496 Memory = 2,
497};
498
499enum class KernelDebugType : u32 {
500 Thread = 0,
501 ThreadCallStack = 1,
502 KernelObject = 2,
503 Handle_ = 3,
504 Memory = 4,
505 PageTable = 5,
506 CpuUtilization = 6,
507 Process = 7,
508 SuspendProcess = 8,
509 ResumeProcess = 9,
510 Port = 10,
511};
512
513enum class KernelTraceState : u32 {
514 Disabled = 0,
515 Enabled = 1,
516};
517
518enum class CodeMemoryOperation : u32 {
519 Map = 0,
520 MapToOwner = 1,
521 Unmap = 2,
522 UnmapFromOwner = 3,
523};
524
525enum class InterruptType : u32 {
526 Edge = 0,
527 Level = 1,
528};
529
530enum class DeviceName {
531 Afi = 0,
532 Avpc = 1,
533 Dc = 2,
534 Dcb = 3,
535 Hc = 4,
536 Hda = 5,
537 Isp2 = 6,
538 MsencNvenc = 7,
539 Nv = 8,
540 Nv2 = 9,
541 Ppcs = 10,
542 Sata = 11,
543 Vi = 12,
544 Vic = 13,
545 XusbHost = 14,
546 XusbDev = 15,
547 Tsec = 16,
548 Ppcs1 = 17,
549 Dc1 = 18,
550 Sdmmc1a = 19,
551 Sdmmc2a = 20,
552 Sdmmc3a = 21,
553 Sdmmc4a = 22,
554 Isp2b = 23,
555 Gpu = 24,
556 Gpub = 25,
557 Ppcs2 = 26,
558 Nvdec = 27,
559 Ape = 28,
560 Se = 29,
561 Nvjpg = 30,
562 Hc1 = 31,
563 Se1 = 32,
564 Axiap = 33,
565 Etr = 34,
566 Tsecb = 35,
567 Tsec1 = 36,
568 Tsecb1 = 37,
569 Nvdec1 = 38,
570 Count,
571};
572
573enum class SystemInfoType : u32 {
574 TotalPhysicalMemorySize = 0,
575 UsedPhysicalMemorySize = 1,
576 InitialProcessIdRange = 2,
577};
578
579enum class ProcessInfoType : u32 {
580 ProcessState = 0,
581};
582
583struct CreateProcessParameter {
584 std::array<char, 12> name;
585 u32 version;
586 u64 program_id;
587 u64 code_address;
588 s32 code_num_pages;
589 u32 flags;
590 Handle reslimit;
591 s32 system_resource_num_pages;
592};
593static_assert(sizeof(CreateProcessParameter) == 0x30);
594
113} // namespace Kernel::Svc 595} // namespace Kernel::Svc
diff --git a/src/core/hle/result.h b/src/core/hle/result.h
index ef4b2d417..56c990728 100644
--- a/src/core/hle/result.h
+++ b/src/core/hle/result.h
@@ -423,16 +423,17 @@ constexpr void UpdateCurrentResultReference<const Result>(Result result_referenc
423} // namespace ResultImpl 423} // namespace ResultImpl
424 424
425#define DECLARE_CURRENT_RESULT_REFERENCE_AND_STORAGE(COUNTER_VALUE) \ 425#define DECLARE_CURRENT_RESULT_REFERENCE_AND_STORAGE(COUNTER_VALUE) \
426 [[maybe_unused]] constexpr bool HasPrevRef_##COUNTER_VALUE = \ 426 [[maybe_unused]] constexpr bool CONCAT2(HasPrevRef_, COUNTER_VALUE) = \
427 std::same_as<decltype(__TmpCurrentResultReference), Result&>; \ 427 std::same_as<decltype(__TmpCurrentResultReference), Result&>; \
428 [[maybe_unused]] auto& PrevRef_##COUNTER_VALUE = __TmpCurrentResultReference; \ 428 [[maybe_unused]] Result CONCAT2(PrevRef_, COUNTER_VALUE) = __TmpCurrentResultReference; \
429 [[maybe_unused]] Result __tmp_result_##COUNTER_VALUE = ResultSuccess; \ 429 [[maybe_unused]] Result CONCAT2(__tmp_result_, COUNTER_VALUE) = ResultSuccess; \
430 Result& __TmpCurrentResultReference = \ 430 Result& __TmpCurrentResultReference = CONCAT2(HasPrevRef_, COUNTER_VALUE) \
431 HasPrevRef_##COUNTER_VALUE ? PrevRef_##COUNTER_VALUE : __tmp_result_##COUNTER_VALUE 431 ? CONCAT2(PrevRef_, COUNTER_VALUE) \
432 : CONCAT2(__tmp_result_, COUNTER_VALUE)
432 433
433#define ON_RESULT_RETURN_IMPL(...) \ 434#define ON_RESULT_RETURN_IMPL(...) \
434 static_assert(std::same_as<decltype(__TmpCurrentResultReference), Result&>); \ 435 static_assert(std::same_as<decltype(__TmpCurrentResultReference), Result&>); \
435 auto RESULT_GUARD_STATE_##__COUNTER__ = \ 436 auto CONCAT2(RESULT_GUARD_STATE_, __COUNTER__) = \
436 ResultImpl::ResultReferenceForScopedResultGuard<__VA_ARGS__>( \ 437 ResultImpl::ResultReferenceForScopedResultGuard<__VA_ARGS__>( \
437 __TmpCurrentResultReference) + \ 438 __TmpCurrentResultReference) + \
438 [&]() 439 [&]()
diff --git a/src/core/hle/service/acc/acc.cpp b/src/core/hle/service/acc/acc.cpp
index bb838e285..85a3f0802 100644
--- a/src/core/hle/service/acc/acc.cpp
+++ b/src/core/hle/service/acc/acc.cpp
@@ -512,10 +512,11 @@ protected:
512 512
513class IManagerForApplication final : public ServiceFramework<IManagerForApplication> { 513class IManagerForApplication final : public ServiceFramework<IManagerForApplication> {
514public: 514public:
515 explicit IManagerForApplication(Core::System& system_, Common::UUID user_id_) 515 explicit IManagerForApplication(Core::System& system_,
516 const std::shared_ptr<ProfileManager>& profile_manager_)
516 : ServiceFramework{system_, "IManagerForApplication"}, 517 : ServiceFramework{system_, "IManagerForApplication"},
517 ensure_token_id{std::make_shared<EnsureTokenIdCacheAsyncInterface>(system)}, 518 ensure_token_id{std::make_shared<EnsureTokenIdCacheAsyncInterface>(system)},
518 user_id{user_id_} { 519 profile_manager{profile_manager_} {
519 // clang-format off 520 // clang-format off
520 static const FunctionInfo functions[] = { 521 static const FunctionInfo functions[] = {
521 {0, &IManagerForApplication::CheckAvailability, "CheckAvailability"}, 522 {0, &IManagerForApplication::CheckAvailability, "CheckAvailability"},
@@ -545,7 +546,7 @@ private:
545 546
546 IPC::ResponseBuilder rb{ctx, 4}; 547 IPC::ResponseBuilder rb{ctx, 4};
547 rb.Push(ResultSuccess); 548 rb.Push(ResultSuccess);
548 rb.PushRaw<u64>(user_id.Hash()); 549 rb.PushRaw<u64>(profile_manager->GetLastOpenedUser().Hash());
549 } 550 }
550 551
551 void EnsureIdTokenCacheAsync(Kernel::HLERequestContext& ctx) { 552 void EnsureIdTokenCacheAsync(Kernel::HLERequestContext& ctx) {
@@ -575,17 +576,20 @@ private:
575 576
576 IPC::ResponseBuilder rb{ctx, 4}; 577 IPC::ResponseBuilder rb{ctx, 4};
577 rb.Push(ResultSuccess); 578 rb.Push(ResultSuccess);
578 rb.PushRaw<u64>(user_id.Hash()); 579 rb.PushRaw<u64>(profile_manager->GetLastOpenedUser().Hash());
579 } 580 }
580 581
581 void StoreOpenContext(Kernel::HLERequestContext& ctx) { 582 void StoreOpenContext(Kernel::HLERequestContext& ctx) {
582 LOG_WARNING(Service_ACC, "(STUBBED) called"); 583 LOG_DEBUG(Service_ACC, "called");
584
585 profile_manager->StoreOpenedUsers();
586
583 IPC::ResponseBuilder rb{ctx, 2}; 587 IPC::ResponseBuilder rb{ctx, 2};
584 rb.Push(ResultSuccess); 588 rb.Push(ResultSuccess);
585 } 589 }
586 590
587 std::shared_ptr<EnsureTokenIdCacheAsyncInterface> ensure_token_id{}; 591 std::shared_ptr<EnsureTokenIdCacheAsyncInterface> ensure_token_id{};
588 Common::UUID user_id{}; 592 std::shared_ptr<ProfileManager> profile_manager;
589}; 593};
590 594
591// 6.0.0+ 595// 6.0.0+
@@ -790,7 +794,7 @@ void Module::Interface::GetBaasAccountManagerForApplication(Kernel::HLERequestCo
790 LOG_DEBUG(Service_ACC, "called"); 794 LOG_DEBUG(Service_ACC, "called");
791 IPC::ResponseBuilder rb{ctx, 2, 0, 1}; 795 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
792 rb.Push(ResultSuccess); 796 rb.Push(ResultSuccess);
793 rb.PushIpcInterface<IManagerForApplication>(system, profile_manager->GetLastOpenedUser()); 797 rb.PushIpcInterface<IManagerForApplication>(system, profile_manager);
794} 798}
795 799
796void Module::Interface::IsUserAccountSwitchLocked(Kernel::HLERequestContext& ctx) { 800void Module::Interface::IsUserAccountSwitchLocked(Kernel::HLERequestContext& ctx) {
@@ -849,22 +853,10 @@ void Module::Interface::ListQualifiedUsers(Kernel::HLERequestContext& ctx) {
849 rb.Push(ResultSuccess); 853 rb.Push(ResultSuccess);
850} 854}
851 855
852void Module::Interface::LoadOpenContext(Kernel::HLERequestContext& ctx) {
853 LOG_WARNING(Service_ACC, "(STUBBED) called");
854
855 // This is similar to GetBaasAccountManagerForApplication
856 // This command is used concurrently with ListOpenContextStoredUsers
857 // TODO: Find the differences between this and GetBaasAccountManagerForApplication
858 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
859 rb.Push(ResultSuccess);
860 rb.PushIpcInterface<IManagerForApplication>(system, profile_manager->GetLastOpenedUser());
861}
862
863void Module::Interface::ListOpenContextStoredUsers(Kernel::HLERequestContext& ctx) { 856void Module::Interface::ListOpenContextStoredUsers(Kernel::HLERequestContext& ctx) {
864 LOG_WARNING(Service_ACC, "(STUBBED) called"); 857 LOG_DEBUG(Service_ACC, "called");
865 858
866 // TODO(ogniK): Handle open contexts 859 ctx.WriteBuffer(profile_manager->GetStoredOpenedUsers());
867 ctx.WriteBuffer(profile_manager->GetOpenUsers());
868 IPC::ResponseBuilder rb{ctx, 2}; 860 IPC::ResponseBuilder rb{ctx, 2};
869 rb.Push(ResultSuccess); 861 rb.Push(ResultSuccess);
870} 862}
diff --git a/src/core/hle/service/acc/acc.h b/src/core/hle/service/acc/acc.h
index 1621e7c0a..9411b0b92 100644
--- a/src/core/hle/service/acc/acc.h
+++ b/src/core/hle/service/acc/acc.h
@@ -35,7 +35,6 @@ public:
35 void InitializeApplicationInfoV2(Kernel::HLERequestContext& ctx); 35 void InitializeApplicationInfoV2(Kernel::HLERequestContext& ctx);
36 void GetProfileEditor(Kernel::HLERequestContext& ctx); 36 void GetProfileEditor(Kernel::HLERequestContext& ctx);
37 void ListQualifiedUsers(Kernel::HLERequestContext& ctx); 37 void ListQualifiedUsers(Kernel::HLERequestContext& ctx);
38 void LoadOpenContext(Kernel::HLERequestContext& ctx);
39 void ListOpenContextStoredUsers(Kernel::HLERequestContext& ctx); 38 void ListOpenContextStoredUsers(Kernel::HLERequestContext& ctx);
40 void StoreSaveDataThumbnailApplication(Kernel::HLERequestContext& ctx); 39 void StoreSaveDataThumbnailApplication(Kernel::HLERequestContext& ctx);
41 void StoreSaveDataThumbnailSystem(Kernel::HLERequestContext& ctx); 40 void StoreSaveDataThumbnailSystem(Kernel::HLERequestContext& ctx);
diff --git a/src/core/hle/service/acc/acc_u0.cpp b/src/core/hle/service/acc/acc_u0.cpp
index 65023b8c2..54844bfe7 100644
--- a/src/core/hle/service/acc/acc_u0.cpp
+++ b/src/core/hle/service/acc/acc_u0.cpp
@@ -28,7 +28,7 @@ ACC_U0::ACC_U0(std::shared_ptr<Module> module_, std::shared_ptr<ProfileManager>
28 {110, &ACC_U0::StoreSaveDataThumbnailApplication, "StoreSaveDataThumbnail"}, 28 {110, &ACC_U0::StoreSaveDataThumbnailApplication, "StoreSaveDataThumbnail"},
29 {111, nullptr, "ClearSaveDataThumbnail"}, 29 {111, nullptr, "ClearSaveDataThumbnail"},
30 {120, nullptr, "CreateGuestLoginRequest"}, 30 {120, nullptr, "CreateGuestLoginRequest"},
31 {130, &ACC_U0::LoadOpenContext, "LoadOpenContext"}, // 5.0.0+ 31 {130, nullptr, "LoadOpenContext"}, // 5.0.0+
32 {131, &ACC_U0::ListOpenContextStoredUsers, "ListOpenContextStoredUsers"}, // 6.0.0+ 32 {131, &ACC_U0::ListOpenContextStoredUsers, "ListOpenContextStoredUsers"}, // 6.0.0+
33 {140, &ACC_U0::InitializeApplicationInfoRestricted, "InitializeApplicationInfoRestricted"}, // 6.0.0+ 33 {140, &ACC_U0::InitializeApplicationInfoRestricted, "InitializeApplicationInfoRestricted"}, // 6.0.0+
34 {141, &ACC_U0::ListQualifiedUsers, "ListQualifiedUsers"}, // 6.0.0+ 34 {141, &ACC_U0::ListQualifiedUsers, "ListQualifiedUsers"}, // 6.0.0+
diff --git a/src/core/hle/service/acc/profile_manager.cpp b/src/core/hle/service/acc/profile_manager.cpp
index a58da4d5f..481e0d141 100644
--- a/src/core/hle/service/acc/profile_manager.cpp
+++ b/src/core/hle/service/acc/profile_manager.cpp
@@ -261,6 +261,31 @@ UUID ProfileManager::GetLastOpenedUser() const {
261 return last_opened_user; 261 return last_opened_user;
262} 262}
263 263
264/// Gets the list of stored opened users.
265UserIDArray ProfileManager::GetStoredOpenedUsers() const {
266 UserIDArray output{};
267 std::ranges::transform(stored_opened_profiles, output.begin(), [](const ProfileInfo& p) {
268 if (p.is_open)
269 return p.user_uuid;
270 return Common::InvalidUUID;
271 });
272 std::stable_partition(output.begin(), output.end(),
273 [](const UUID& uuid) { return uuid.IsValid(); });
274 return output;
275}
276
277/// Captures the opened users, which can be queried across process launches with
278/// ListOpenContextStoredUsers.
279void ProfileManager::StoreOpenedUsers() {
280 size_t profile_index{};
281 stored_opened_profiles = {};
282 std::for_each(profiles.begin(), profiles.end(), [&](const auto& profile) {
283 if (profile.is_open) {
284 stored_opened_profiles[profile_index++] = profile;
285 }
286 });
287}
288
264/// Return the users profile base and the unknown arbitary data. 289/// Return the users profile base and the unknown arbitary data.
265bool ProfileManager::GetProfileBaseAndData(std::optional<std::size_t> index, ProfileBase& profile, 290bool ProfileManager::GetProfileBaseAndData(std::optional<std::size_t> index, ProfileBase& profile,
266 UserData& data) const { 291 UserData& data) const {
diff --git a/src/core/hle/service/acc/profile_manager.h b/src/core/hle/service/acc/profile_manager.h
index 135f7d0d5..993a5a57a 100644
--- a/src/core/hle/service/acc/profile_manager.h
+++ b/src/core/hle/service/acc/profile_manager.h
@@ -86,6 +86,8 @@ public:
86 UserIDArray GetOpenUsers() const; 86 UserIDArray GetOpenUsers() const;
87 UserIDArray GetAllUsers() const; 87 UserIDArray GetAllUsers() const;
88 Common::UUID GetLastOpenedUser() const; 88 Common::UUID GetLastOpenedUser() const;
89 UserIDArray GetStoredOpenedUsers() const;
90 void StoreOpenedUsers();
89 91
90 bool CanSystemRegisterUser() const; 92 bool CanSystemRegisterUser() const;
91 93
@@ -101,6 +103,7 @@ private:
101 bool RemoveProfileAtIndex(std::size_t index); 103 bool RemoveProfileAtIndex(std::size_t index);
102 104
103 std::array<ProfileInfo, MAX_USERS> profiles{}; 105 std::array<ProfileInfo, MAX_USERS> profiles{};
106 std::array<ProfileInfo, MAX_USERS> stored_opened_profiles{};
104 std::size_t user_count{}; 107 std::size_t user_count{};
105 Common::UUID last_opened_user{}; 108 Common::UUID last_opened_user{};
106}; 109};
diff --git a/src/core/hle/service/am/am.cpp b/src/core/hle/service/am/am.cpp
index e55233054..8ea7fd760 100644
--- a/src/core/hle/service/am/am.cpp
+++ b/src/core/hle/service/am/am.cpp
@@ -299,7 +299,7 @@ ISelfController::ISelfController(Core::System& system_, NVFlinger::NVFlinger& nv
299 {100, &ISelfController::SetAlbumImageTakenNotificationEnabled, "SetAlbumImageTakenNotificationEnabled"}, 299 {100, &ISelfController::SetAlbumImageTakenNotificationEnabled, "SetAlbumImageTakenNotificationEnabled"},
300 {110, nullptr, "SetApplicationAlbumUserData"}, 300 {110, nullptr, "SetApplicationAlbumUserData"},
301 {120, &ISelfController::SaveCurrentScreenshot, "SaveCurrentScreenshot"}, 301 {120, &ISelfController::SaveCurrentScreenshot, "SaveCurrentScreenshot"},
302 {130, nullptr, "SetRecordVolumeMuted"}, 302 {130, &ISelfController::SetRecordVolumeMuted, "SetRecordVolumeMuted"},
303 {1000, nullptr, "GetDebugStorageChannel"}, 303 {1000, nullptr, "GetDebugStorageChannel"},
304 }; 304 };
305 // clang-format on 305 // clang-format on
@@ -597,6 +597,17 @@ void ISelfController::SaveCurrentScreenshot(Kernel::HLERequestContext& ctx) {
597 rb.Push(ResultSuccess); 597 rb.Push(ResultSuccess);
598} 598}
599 599
600void ISelfController::SetRecordVolumeMuted(Kernel::HLERequestContext& ctx) {
601 IPC::RequestParser rp{ctx};
602
603 const auto is_record_volume_muted = rp.Pop<bool>();
604
605 LOG_WARNING(Service_AM, "(STUBBED) called. is_record_volume_muted={}", is_record_volume_muted);
606
607 IPC::ResponseBuilder rb{ctx, 2};
608 rb.Push(ResultSuccess);
609}
610
600AppletMessageQueue::AppletMessageQueue(Core::System& system) 611AppletMessageQueue::AppletMessageQueue(Core::System& system)
601 : service_context{system, "AppletMessageQueue"} { 612 : service_context{system, "AppletMessageQueue"} {
602 on_new_message = service_context.CreateEvent("AMMessageQueue:OnMessageReceived"); 613 on_new_message = service_context.CreateEvent("AMMessageQueue:OnMessageReceived");
diff --git a/src/core/hle/service/am/am.h b/src/core/hle/service/am/am.h
index bb75c6281..a0fbfcfc5 100644
--- a/src/core/hle/service/am/am.h
+++ b/src/core/hle/service/am/am.h
@@ -182,6 +182,7 @@ private:
182 void GetAccumulatedSuspendedTickChangedEvent(Kernel::HLERequestContext& ctx); 182 void GetAccumulatedSuspendedTickChangedEvent(Kernel::HLERequestContext& ctx);
183 void SetAlbumImageTakenNotificationEnabled(Kernel::HLERequestContext& ctx); 183 void SetAlbumImageTakenNotificationEnabled(Kernel::HLERequestContext& ctx);
184 void SaveCurrentScreenshot(Kernel::HLERequestContext& ctx); 184 void SaveCurrentScreenshot(Kernel::HLERequestContext& ctx);
185 void SetRecordVolumeMuted(Kernel::HLERequestContext& ctx);
185 186
186 enum class ScreenshotPermission : u32 { 187 enum class ScreenshotPermission : u32 {
187 Inherit = 0, 188 Inherit = 0,
diff --git a/src/core/hle/service/audio/audin_u.cpp b/src/core/hle/service/audio/audin_u.cpp
index 48a9a73a0..608925dfc 100644
--- a/src/core/hle/service/audio/audin_u.cpp
+++ b/src/core/hle/service/audio/audin_u.cpp
@@ -17,7 +17,7 @@ using namespace AudioCore::AudioIn;
17class IAudioIn final : public ServiceFramework<IAudioIn> { 17class IAudioIn final : public ServiceFramework<IAudioIn> {
18public: 18public:
19 explicit IAudioIn(Core::System& system_, Manager& manager, size_t session_id, 19 explicit IAudioIn(Core::System& system_, Manager& manager, size_t session_id,
20 std::string& device_name, const AudioInParameter& in_params, u32 handle, 20 const std::string& device_name, const AudioInParameter& in_params, u32 handle,
21 u64 applet_resource_user_id) 21 u64 applet_resource_user_id)
22 : ServiceFramework{system_, "IAudioIn"}, 22 : ServiceFramework{system_, "IAudioIn"},
23 service_context{system_, "IAudioIn"}, event{service_context.CreateEvent("AudioInEvent")}, 23 service_context{system_, "IAudioIn"}, event{service_context.CreateEvent("AudioInEvent")},
diff --git a/src/core/hle/service/audio/audout_u.cpp b/src/core/hle/service/audio/audout_u.cpp
index 49c092301..122290c6a 100644
--- a/src/core/hle/service/audio/audout_u.cpp
+++ b/src/core/hle/service/audio/audout_u.cpp
@@ -24,7 +24,7 @@ using namespace AudioCore::AudioOut;
24class IAudioOut final : public ServiceFramework<IAudioOut> { 24class IAudioOut final : public ServiceFramework<IAudioOut> {
25public: 25public:
26 explicit IAudioOut(Core::System& system_, AudioCore::AudioOut::Manager& manager, 26 explicit IAudioOut(Core::System& system_, AudioCore::AudioOut::Manager& manager,
27 size_t session_id, std::string& device_name, 27 size_t session_id, const std::string& device_name,
28 const AudioOutParameter& in_params, u32 handle, u64 applet_resource_user_id) 28 const AudioOutParameter& in_params, u32 handle, u64 applet_resource_user_id)
29 : ServiceFramework{system_, "IAudioOut", ServiceThreadType::CreateNew}, 29 : ServiceFramework{system_, "IAudioOut", ServiceThreadType::CreateNew},
30 service_context{system_, "IAudioOut"}, event{service_context.CreateEvent( 30 service_context{system_, "IAudioOut"}, event{service_context.CreateEvent(
diff --git a/src/core/hle/service/hid/controllers/npad.cpp b/src/core/hle/service/hid/controllers/npad.cpp
index 3b26e96de..2f871de31 100644
--- a/src/core/hle/service/hid/controllers/npad.cpp
+++ b/src/core/hle/service/hid/controllers/npad.cpp
@@ -868,7 +868,7 @@ bool Controller_NPad::VibrateControllerAtIndex(Core::HID::NpadIdType npad_id,
868 return false; 868 return false;
869 } 869 }
870 870
871 if (!controller.device->IsVibrationEnabled()) { 871 if (!controller.device->IsVibrationEnabled(device_index)) {
872 if (controller.vibration[device_index].latest_vibration_value.low_amplitude != 0.0f || 872 if (controller.vibration[device_index].latest_vibration_value.low_amplitude != 0.0f ||
873 controller.vibration[device_index].latest_vibration_value.high_amplitude != 0.0f) { 873 controller.vibration[device_index].latest_vibration_value.high_amplitude != 0.0f) {
874 // Send an empty vibration to stop any vibrations. 874 // Send an empty vibration to stop any vibrations.
@@ -1001,7 +1001,7 @@ void Controller_NPad::InitializeVibrationDeviceAtIndex(Core::HID::NpadIdType npa
1001 } 1001 }
1002 1002
1003 controller.vibration[device_index].device_mounted = 1003 controller.vibration[device_index].device_mounted =
1004 controller.device->TestVibration(device_index); 1004 controller.device->IsVibrationEnabled(device_index);
1005} 1005}
1006 1006
1007void Controller_NPad::SetPermitVibrationSession(bool permit_vibration_session) { 1007void Controller_NPad::SetPermitVibrationSession(bool permit_vibration_session) {
diff --git a/src/core/hle/service/kernel_helpers.cpp b/src/core/hle/service/kernel_helpers.cpp
index af133af93..42991928e 100644
--- a/src/core/hle/service/kernel_helpers.cpp
+++ b/src/core/hle/service/kernel_helpers.cpp
@@ -31,7 +31,7 @@ ServiceContext::~ServiceContext() {
31Kernel::KEvent* ServiceContext::CreateEvent(std::string&& name) { 31Kernel::KEvent* ServiceContext::CreateEvent(std::string&& name) {
32 // Reserve a new event from the process resource limit 32 // Reserve a new event from the process resource limit
33 Kernel::KScopedResourceReservation event_reservation(process, 33 Kernel::KScopedResourceReservation event_reservation(process,
34 Kernel::LimitableResource::Events); 34 Kernel::LimitableResource::EventCountMax);
35 if (!event_reservation.Succeeded()) { 35 if (!event_reservation.Succeeded()) {
36 LOG_CRITICAL(Service, "Resource limit reached!"); 36 LOG_CRITICAL(Service, "Resource limit reached!");
37 return {}; 37 return {};
diff --git a/src/core/hle/service/nfp/amiibo_crypto.cpp b/src/core/hle/service/nfp/amiibo_crypto.cpp
index c32a6816b..167e29572 100644
--- a/src/core/hle/service/nfp/amiibo_crypto.cpp
+++ b/src/core/hle/service/nfp/amiibo_crypto.cpp
@@ -9,6 +9,7 @@
9#include <mbedtls/hmac_drbg.h> 9#include <mbedtls/hmac_drbg.h>
10 10
11#include "common/fs/file.h" 11#include "common/fs/file.h"
12#include "common/fs/fs.h"
12#include "common/fs/path_util.h" 13#include "common/fs/path_util.h"
13#include "common/logging/log.h" 14#include "common/logging/log.h"
14#include "core/hle/service/mii/mii_manager.h" 15#include "core/hle/service/mii/mii_manager.h"
@@ -279,7 +280,7 @@ bool LoadKeys(InternalKey& locked_secret, InternalKey& unfixed_info) {
279 Common::FS::FileType::BinaryFile}; 280 Common::FS::FileType::BinaryFile};
280 281
281 if (!keys_file.IsOpen()) { 282 if (!keys_file.IsOpen()) {
282 LOG_ERROR(Service_NFP, "No keys detected"); 283 LOG_ERROR(Service_NFP, "Failed to open key file");
283 return false; 284 return false;
284 } 285 }
285 286
@@ -295,6 +296,11 @@ bool LoadKeys(InternalKey& locked_secret, InternalKey& unfixed_info) {
295 return true; 296 return true;
296} 297}
297 298
299bool IsKeyAvailable() {
300 const auto yuzu_keys_dir = Common::FS::GetYuzuPath(Common::FS::YuzuPath::KeysDir);
301 return Common::FS::Exists(yuzu_keys_dir / "key_retail.bin");
302}
303
298bool DecodeAmiibo(const EncryptedNTAG215File& encrypted_tag_data, NTAG215File& tag_data) { 304bool DecodeAmiibo(const EncryptedNTAG215File& encrypted_tag_data, NTAG215File& tag_data) {
299 InternalKey locked_secret{}; 305 InternalKey locked_secret{};
300 InternalKey unfixed_info{}; 306 InternalKey unfixed_info{};
diff --git a/src/core/hle/service/nfp/amiibo_crypto.h b/src/core/hle/service/nfp/amiibo_crypto.h
index 0175ced91..1fa61174e 100644
--- a/src/core/hle/service/nfp/amiibo_crypto.h
+++ b/src/core/hle/service/nfp/amiibo_crypto.h
@@ -91,6 +91,9 @@ void Cipher(const DerivedKeys& keys, const NTAG215File& in_data, NTAG215File& ou
91/// Loads both amiibo keys from key_retail.bin 91/// Loads both amiibo keys from key_retail.bin
92bool LoadKeys(InternalKey& locked_secret, InternalKey& unfixed_info); 92bool LoadKeys(InternalKey& locked_secret, InternalKey& unfixed_info);
93 93
94/// Returns true if key_retail.bin exist
95bool IsKeyAvailable();
96
94/// Decodes encripted amiibo data returns true if output is valid 97/// Decodes encripted amiibo data returns true if output is valid
95bool DecodeAmiibo(const EncryptedNTAG215File& encrypted_tag_data, NTAG215File& tag_data); 98bool DecodeAmiibo(const EncryptedNTAG215File& encrypted_tag_data, NTAG215File& tag_data);
96 99
diff --git a/src/core/hle/service/nfp/nfp_device.cpp b/src/core/hle/service/nfp/nfp_device.cpp
index 76f8a267a..b19672560 100644
--- a/src/core/hle/service/nfp/nfp_device.cpp
+++ b/src/core/hle/service/nfp/nfp_device.cpp
@@ -17,6 +17,7 @@
17#include "core/hle/ipc_helpers.h" 17#include "core/hle/ipc_helpers.h"
18#include "core/hle/kernel/k_event.h" 18#include "core/hle/kernel/k_event.h"
19#include "core/hle/service/mii/mii_manager.h" 19#include "core/hle/service/mii/mii_manager.h"
20#include "core/hle/service/mii/types.h"
20#include "core/hle/service/nfp/amiibo_crypto.h" 21#include "core/hle/service/nfp/amiibo_crypto.h"
21#include "core/hle/service/nfp/nfp.h" 22#include "core/hle/service/nfp/nfp.h"
22#include "core/hle/service/nfp/nfp_device.h" 23#include "core/hle/service/nfp/nfp_device.h"
@@ -233,6 +234,14 @@ Result NfpDevice::Mount(MountTarget mount_target_) {
233 return NotAnAmiibo; 234 return NotAnAmiibo;
234 } 235 }
235 236
237 // Mark amiibos as read only when keys are missing
238 if (!AmiiboCrypto::IsKeyAvailable()) {
239 LOG_ERROR(Service_NFP, "No keys detected");
240 device_state = DeviceState::TagMounted;
241 mount_target = MountTarget::Rom;
242 return ResultSuccess;
243 }
244
236 if (!AmiiboCrypto::DecodeAmiibo(encrypted_tag_data, tag_data)) { 245 if (!AmiiboCrypto::DecodeAmiibo(encrypted_tag_data, tag_data)) {
237 LOG_ERROR(Service_NFP, "Can't decode amiibo {}", device_state); 246 LOG_ERROR(Service_NFP, "Can't decode amiibo {}", device_state);
238 return CorruptedData; 247 return CorruptedData;
diff --git a/src/core/hle/service/nfp/nfp_device.h b/src/core/hle/service/nfp/nfp_device.h
index a5b72cf19..76d0e9ae4 100644
--- a/src/core/hle/service/nfp/nfp_device.h
+++ b/src/core/hle/service/nfp/nfp_device.h
@@ -8,7 +8,6 @@
8 8
9#include "common/common_funcs.h" 9#include "common/common_funcs.h"
10#include "core/hle/service/kernel_helpers.h" 10#include "core/hle/service/kernel_helpers.h"
11#include "core/hle/service/mii/types.h"
12#include "core/hle/service/nfp/nfp_types.h" 11#include "core/hle/service/nfp/nfp_types.h"
13#include "core/hle/service/service.h" 12#include "core/hle/service/service.h"
14 13
diff --git a/src/core/hle/service/nfp/nfp_types.h b/src/core/hle/service/nfp/nfp_types.h
index c09f9ddb6..63d5917cb 100644
--- a/src/core/hle/service/nfp/nfp_types.h
+++ b/src/core/hle/service/nfp/nfp_types.h
@@ -17,11 +17,6 @@ enum class ServiceType : u32 {
17 System, 17 System,
18}; 18};
19 19
20enum class State : u32 {
21 NonInitialized,
22 Initialized,
23};
24
25enum class DeviceState : u32 { 20enum class DeviceState : u32 {
26 Initialized, 21 Initialized,
27 SearchingForTag, 22 SearchingForTag,
diff --git a/src/core/hle/service/nfp/nfp_user.cpp b/src/core/hle/service/nfp/nfp_user.cpp
index 4ed53b534..33e2ef518 100644
--- a/src/core/hle/service/nfp/nfp_user.cpp
+++ b/src/core/hle/service/nfp/nfp_user.cpp
@@ -6,12 +6,9 @@
6 6
7#include "common/logging/log.h" 7#include "common/logging/log.h"
8#include "core/core.h" 8#include "core/core.h"
9#include "core/hid/emulated_controller.h"
10#include "core/hid/hid_core.h"
11#include "core/hid/hid_types.h" 9#include "core/hid/hid_types.h"
12#include "core/hle/ipc_helpers.h" 10#include "core/hle/ipc_helpers.h"
13#include "core/hle/kernel/k_event.h" 11#include "core/hle/kernel/k_event.h"
14#include "core/hle/service/mii/mii_manager.h"
15#include "core/hle/service/nfp/nfp_device.h" 12#include "core/hle/service/nfp/nfp_device.h"
16#include "core/hle/service/nfp/nfp_result.h" 13#include "core/hle/service/nfp/nfp_result.h"
17#include "core/hle/service/nfp/nfp_user.h" 14#include "core/hle/service/nfp/nfp_user.h"
diff --git a/src/core/hle/service/nfp/nfp_user.h b/src/core/hle/service/nfp/nfp_user.h
index 68c60ae82..47aff3695 100644
--- a/src/core/hle/service/nfp/nfp_user.h
+++ b/src/core/hle/service/nfp/nfp_user.h
@@ -4,8 +4,7 @@
4#pragma once 4#pragma once
5 5
6#include "core/hle/service/kernel_helpers.h" 6#include "core/hle/service/kernel_helpers.h"
7#include "core/hle/service/nfp/nfp.h" 7#include "core/hle/service/service.h"
8#include "core/hle/service/nfp/nfp_types.h"
9 8
10namespace Service::NFP { 9namespace Service::NFP {
11class NfpDevice; 10class NfpDevice;
@@ -15,6 +14,11 @@ public:
15 explicit IUser(Core::System& system_); 14 explicit IUser(Core::System& system_);
16 15
17private: 16private:
17 enum class State : u32 {
18 NonInitialized,
19 Initialized,
20 };
21
18 void Initialize(Kernel::HLERequestContext& ctx); 22 void Initialize(Kernel::HLERequestContext& ctx);
19 void Finalize(Kernel::HLERequestContext& ctx); 23 void Finalize(Kernel::HLERequestContext& ctx);
20 void ListDevices(Kernel::HLERequestContext& ctx); 24 void ListDevices(Kernel::HLERequestContext& ctx);
diff --git a/src/core/hle/service/nvdrv/core/nvmap.cpp b/src/core/hle/service/nvdrv/core/nvmap.cpp
index fbd8a74a5..a51ca5444 100644
--- a/src/core/hle/service/nvdrv/core/nvmap.cpp
+++ b/src/core/hle/service/nvdrv/core/nvmap.cpp
@@ -255,15 +255,16 @@ std::optional<NvMap::FreeInfo> NvMap::FreeHandle(Handle::Id handle, bool interna
255 .address = handle_description->address, 255 .address = handle_description->address,
256 .size = handle_description->size, 256 .size = handle_description->size,
257 .was_uncached = handle_description->flags.map_uncached.Value() != 0, 257 .was_uncached = handle_description->flags.map_uncached.Value() != 0,
258 .can_unlock = true,
258 }; 259 };
259 } else { 260 } else {
260 return std::nullopt; 261 return std::nullopt;
261 } 262 }
262 263
263 // Handle hasn't been freed from memory, set address to 0 to mark that the handle wasn't freed 264 // If the handle hasn't been freed from memory, mark that
264 if (!hWeak.expired()) { 265 if (!hWeak.expired()) {
265 LOG_DEBUG(Service_NVDRV, "nvmap handle: {} wasn't freed as it is still in use", handle); 266 LOG_DEBUG(Service_NVDRV, "nvmap handle: {} wasn't freed as it is still in use", handle);
266 freeInfo.address = 0; 267 freeInfo.can_unlock = false;
267 } 268 }
268 269
269 return freeInfo; 270 return freeInfo;
diff --git a/src/core/hle/service/nvdrv/core/nvmap.h b/src/core/hle/service/nvdrv/core/nvmap.h
index b9dd3801f..a8e573890 100644
--- a/src/core/hle/service/nvdrv/core/nvmap.h
+++ b/src/core/hle/service/nvdrv/core/nvmap.h
@@ -105,6 +105,7 @@ public:
105 u64 address; //!< Address the handle referred to before deletion 105 u64 address; //!< Address the handle referred to before deletion
106 u64 size; //!< Page-aligned handle size 106 u64 size; //!< Page-aligned handle size
107 bool was_uncached; //!< If the handle was allocated as uncached 107 bool was_uncached; //!< If the handle was allocated as uncached
108 bool can_unlock; //!< If the address region is ready to be unlocked
108 }; 109 };
109 110
110 explicit NvMap(Tegra::Host1x::Host1x& host1x); 111 explicit NvMap(Tegra::Host1x::Host1x& host1x);
diff --git a/src/core/hle/service/nvdrv/devices/nvmap.cpp b/src/core/hle/service/nvdrv/devices/nvmap.cpp
index b60679021..fa29db758 100644
--- a/src/core/hle/service/nvdrv/devices/nvmap.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvmap.cpp
@@ -126,10 +126,12 @@ NvResult nvmap::IocAlloc(const std::vector<u8>& input, std::vector<u8>& output)
126 LOG_CRITICAL(Service_NVDRV, "Object failed to allocate, handle={:08X}", params.handle); 126 LOG_CRITICAL(Service_NVDRV, "Object failed to allocate, handle={:08X}", params.handle);
127 return result; 127 return result;
128 } 128 }
129 bool is_out_io{};
129 ASSERT(system.CurrentProcess() 130 ASSERT(system.CurrentProcess()
130 ->PageTable() 131 ->PageTable()
131 .LockForMapDeviceAddressSpace(handle_description->address, handle_description->size, 132 .LockForMapDeviceAddressSpace(&is_out_io, handle_description->address,
132 Kernel::KMemoryPermission::None, true) 133 handle_description->size,
134 Kernel::KMemoryPermission::None, true, false)
133 .IsSuccess()); 135 .IsSuccess());
134 std::memcpy(output.data(), &params, sizeof(params)); 136 std::memcpy(output.data(), &params, sizeof(params));
135 return result; 137 return result;
@@ -251,10 +253,12 @@ NvResult nvmap::IocFree(const std::vector<u8>& input, std::vector<u8>& output) {
251 } 253 }
252 254
253 if (auto freeInfo{file.FreeHandle(params.handle, false)}) { 255 if (auto freeInfo{file.FreeHandle(params.handle, false)}) {
254 ASSERT(system.CurrentProcess() 256 if (freeInfo->can_unlock) {
255 ->PageTable() 257 ASSERT(system.CurrentProcess()
256 .UnlockForDeviceAddressSpace(freeInfo->address, freeInfo->size) 258 ->PageTable()
257 .IsSuccess()); 259 .UnlockForDeviceAddressSpace(freeInfo->address, freeInfo->size)
260 .IsSuccess());
261 }
258 params.address = freeInfo->address; 262 params.address = freeInfo->address;
259 params.size = static_cast<u32>(freeInfo->size); 263 params.size = static_cast<u32>(freeInfo->size);
260 params.flags.raw = 0; 264 params.flags.raw = 0;
diff --git a/src/core/hle/service/nvflinger/buffer_queue_producer.cpp b/src/core/hle/service/nvflinger/buffer_queue_producer.cpp
index 77ddbb6ef..41ba44b21 100644
--- a/src/core/hle/service/nvflinger/buffer_queue_producer.cpp
+++ b/src/core/hle/service/nvflinger/buffer_queue_producer.cpp
@@ -742,6 +742,13 @@ Status BufferQueueProducer::Disconnect(NativeWindowApi api) {
742 return Status::NoError; 742 return Status::NoError;
743 } 743 }
744 744
745 // HACK: We are not Android. Remove handle for items in queue, and clear queue.
746 // Allows synchronous destruction of nvmap handles.
747 for (auto& item : core->queue) {
748 nvmap.FreeHandle(item.graphic_buffer->BufferId(), true);
749 }
750 core->queue.clear();
751
745 switch (api) { 752 switch (api) {
746 case NativeWindowApi::Egl: 753 case NativeWindowApi::Egl:
747 case NativeWindowApi::Cpu: 754 case NativeWindowApi::Cpu:
diff --git a/src/core/hle/service/nvflinger/nvflinger.cpp b/src/core/hle/service/nvflinger/nvflinger.cpp
index dad93b38e..c3af12c90 100644
--- a/src/core/hle/service/nvflinger/nvflinger.cpp
+++ b/src/core/hle/service/nvflinger/nvflinger.cpp
@@ -138,6 +138,19 @@ std::optional<u64> NVFlinger::OpenDisplay(std::string_view name) {
138 return itr->GetID(); 138 return itr->GetID();
139} 139}
140 140
141bool NVFlinger::CloseDisplay(u64 display_id) {
142 const auto lock_guard = Lock();
143 auto* const display = FindDisplay(display_id);
144
145 if (display == nullptr) {
146 return false;
147 }
148
149 display->Reset();
150
151 return true;
152}
153
141std::optional<u64> NVFlinger::CreateLayer(u64 display_id) { 154std::optional<u64> NVFlinger::CreateLayer(u64 display_id) {
142 const auto lock_guard = Lock(); 155 const auto lock_guard = Lock();
143 auto* const display = FindDisplay(display_id); 156 auto* const display = FindDisplay(display_id);
diff --git a/src/core/hle/service/nvflinger/nvflinger.h b/src/core/hle/service/nvflinger/nvflinger.h
index b8191c595..460bef976 100644
--- a/src/core/hle/service/nvflinger/nvflinger.h
+++ b/src/core/hle/service/nvflinger/nvflinger.h
@@ -58,6 +58,11 @@ public:
58 /// If an invalid display name is provided, then an empty optional is returned. 58 /// If an invalid display name is provided, then an empty optional is returned.
59 [[nodiscard]] std::optional<u64> OpenDisplay(std::string_view name); 59 [[nodiscard]] std::optional<u64> OpenDisplay(std::string_view name);
60 60
61 /// Closes the specified display by its ID.
62 ///
63 /// Returns false if an invalid display ID is provided.
64 [[nodiscard]] bool CloseDisplay(u64 display_id);
65
61 /// Creates a layer on the specified display and returns the layer ID. 66 /// Creates a layer on the specified display and returns the layer ID.
62 /// 67 ///
63 /// If an invalid display ID is specified, then an empty optional is returned. 68 /// If an invalid display ID is specified, then an empty optional is returned.
diff --git a/src/core/hle/service/service.cpp b/src/core/hle/service/service.cpp
index 5db6588e4..5ab41c0c4 100644
--- a/src/core/hle/service/service.cpp
+++ b/src/core/hle/service/service.cpp
@@ -99,6 +99,12 @@ ServiceFrameworkBase::ServiceFrameworkBase(Core::System& system_, const char* se
99ServiceFrameworkBase::~ServiceFrameworkBase() { 99ServiceFrameworkBase::~ServiceFrameworkBase() {
100 // Wait for other threads to release access before destroying 100 // Wait for other threads to release access before destroying
101 const auto guard = LockService(); 101 const auto guard = LockService();
102
103 if (named_port != nullptr) {
104 named_port->GetClientPort().Close();
105 named_port->GetServerPort().Close();
106 named_port = nullptr;
107 }
102} 108}
103 109
104void ServiceFrameworkBase::InstallAsService(SM::ServiceManager& service_manager) { 110void ServiceFrameworkBase::InstallAsService(SM::ServiceManager& service_manager) {
@@ -113,15 +119,16 @@ void ServiceFrameworkBase::InstallAsService(SM::ServiceManager& service_manager)
113Kernel::KClientPort& ServiceFrameworkBase::CreatePort() { 119Kernel::KClientPort& ServiceFrameworkBase::CreatePort() {
114 const auto guard = LockService(); 120 const auto guard = LockService();
115 121
116 ASSERT(!service_registered); 122 if (named_port == nullptr) {
123 ASSERT(!service_registered);
117 124
118 auto* port = Kernel::KPort::Create(kernel); 125 named_port = Kernel::KPort::Create(kernel);
119 port->Initialize(max_sessions, false, service_name); 126 named_port->Initialize(max_sessions, false, service_name);
120 port->GetServerPort().SetSessionHandler(shared_from_this());
121 127
122 service_registered = true; 128 service_registered = true;
129 }
123 130
124 return port->GetClientPort(); 131 return named_port->GetClientPort();
125} 132}
126 133
127void ServiceFrameworkBase::RegisterHandlersBase(const FunctionInfoBase* functions, std::size_t n) { 134void ServiceFrameworkBase::RegisterHandlersBase(const FunctionInfoBase* functions, std::size_t n) {
@@ -199,7 +206,6 @@ Result ServiceFrameworkBase::HandleSyncRequest(Kernel::KServerSession& session,
199 switch (ctx.GetCommandType()) { 206 switch (ctx.GetCommandType()) {
200 case IPC::CommandType::Close: 207 case IPC::CommandType::Close:
201 case IPC::CommandType::TIPC_Close: { 208 case IPC::CommandType::TIPC_Close: {
202 session.Close();
203 IPC::ResponseBuilder rb{ctx, 2}; 209 IPC::ResponseBuilder rb{ctx, 2};
204 rb.Push(ResultSuccess); 210 rb.Push(ResultSuccess);
205 result = IPC::ERR_REMOTE_PROCESS_DEAD; 211 result = IPC::ERR_REMOTE_PROCESS_DEAD;
@@ -244,6 +250,7 @@ Services::Services(std::shared_ptr<SM::ServiceManager>& sm, Core::System& system
244 system.GetFileSystemController().CreateFactories(*system.GetFilesystem(), false); 250 system.GetFileSystemController().CreateFactories(*system.GetFilesystem(), false);
245 251
246 system.Kernel().RegisterNamedService("sm:", SM::ServiceManager::InterfaceFactory); 252 system.Kernel().RegisterNamedService("sm:", SM::ServiceManager::InterfaceFactory);
253 system.Kernel().RegisterInterfaceForNamedService("sm:", SM::ServiceManager::SessionHandler);
247 254
248 Account::InstallInterfaces(system); 255 Account::InstallInterfaces(system);
249 AM::InstallInterfaces(*sm, *nv_flinger, system); 256 AM::InstallInterfaces(*sm, *nv_flinger, system);
diff --git a/src/core/hle/service/service.h b/src/core/hle/service/service.h
index ec9deeee4..22e2119d7 100644
--- a/src/core/hle/service/service.h
+++ b/src/core/hle/service/service.h
@@ -20,6 +20,7 @@ class System;
20namespace Kernel { 20namespace Kernel {
21class HLERequestContext; 21class HLERequestContext;
22class KClientPort; 22class KClientPort;
23class KPort;
23class KServerSession; 24class KServerSession;
24class ServiceThread; 25class ServiceThread;
25} // namespace Kernel 26} // namespace Kernel
@@ -98,6 +99,9 @@ protected:
98 /// Identifier string used to connect to the service. 99 /// Identifier string used to connect to the service.
99 std::string service_name; 100 std::string service_name;
100 101
102 /// Port used by ManageNamedPort.
103 Kernel::KPort* named_port{};
104
101private: 105private:
102 template <typename T> 106 template <typename T>
103 friend class ServiceFramework; 107 friend class ServiceFramework;
diff --git a/src/core/hle/service/sm/sm.cpp b/src/core/hle/service/sm/sm.cpp
index 48e70f93c..84720094f 100644
--- a/src/core/hle/service/sm/sm.cpp
+++ b/src/core/hle/service/sm/sm.cpp
@@ -23,7 +23,13 @@ constexpr Result ERR_INVALID_NAME(ErrorModule::SM, 6);
23constexpr Result ERR_SERVICE_NOT_REGISTERED(ErrorModule::SM, 7); 23constexpr Result ERR_SERVICE_NOT_REGISTERED(ErrorModule::SM, 7);
24 24
25ServiceManager::ServiceManager(Kernel::KernelCore& kernel_) : kernel{kernel_} {} 25ServiceManager::ServiceManager(Kernel::KernelCore& kernel_) : kernel{kernel_} {}
26ServiceManager::~ServiceManager() = default; 26
27ServiceManager::~ServiceManager() {
28 for (auto& [name, port] : service_ports) {
29 port->GetClientPort().Close();
30 port->GetServerPort().Close();
31 }
32}
27 33
28void ServiceManager::InvokeControlRequest(Kernel::HLERequestContext& context) { 34void ServiceManager::InvokeControlRequest(Kernel::HLERequestContext& context) {
29 controller_interface->InvokeRequest(context); 35 controller_interface->InvokeRequest(context);
@@ -43,6 +49,10 @@ Kernel::KClientPort& ServiceManager::InterfaceFactory(ServiceManager& self, Core
43 return self.sm_interface->CreatePort(); 49 return self.sm_interface->CreatePort();
44} 50}
45 51
52void ServiceManager::SessionHandler(ServiceManager& self, Kernel::KServerPort* server_port) {
53 self.sm_interface->AcceptSession(server_port);
54}
55
46Result ServiceManager::RegisterService(std::string name, u32 max_sessions, 56Result ServiceManager::RegisterService(std::string name, u32 max_sessions,
47 Kernel::SessionRequestHandlerPtr handler) { 57 Kernel::SessionRequestHandlerPtr handler) {
48 58
@@ -53,7 +63,11 @@ Result ServiceManager::RegisterService(std::string name, u32 max_sessions,
53 return ERR_ALREADY_REGISTERED; 63 return ERR_ALREADY_REGISTERED;
54 } 64 }
55 65
56 registered_services.emplace(std::move(name), handler); 66 auto* port = Kernel::KPort::Create(kernel);
67 port->Initialize(ServerSessionCountMax, false, name);
68
69 service_ports.emplace(name, port);
70 registered_services.emplace(name, handler);
57 71
58 return ResultSuccess; 72 return ResultSuccess;
59} 73}
@@ -68,25 +82,20 @@ Result ServiceManager::UnregisterService(const std::string& name) {
68 } 82 }
69 83
70 registered_services.erase(iter); 84 registered_services.erase(iter);
85 service_ports.erase(name);
86
71 return ResultSuccess; 87 return ResultSuccess;
72} 88}
73 89
74ResultVal<Kernel::KPort*> ServiceManager::GetServicePort(const std::string& name) { 90ResultVal<Kernel::KPort*> ServiceManager::GetServicePort(const std::string& name) {
75 CASCADE_CODE(ValidateServiceName(name)); 91 CASCADE_CODE(ValidateServiceName(name));
76 auto it = registered_services.find(name); 92 auto it = service_ports.find(name);
77 if (it == registered_services.end()) { 93 if (it == service_ports.end()) {
78 LOG_ERROR(Service_SM, "Server is not registered! service={}", name); 94 LOG_ERROR(Service_SM, "Server is not registered! service={}", name);
79 return ERR_SERVICE_NOT_REGISTERED; 95 return ERR_SERVICE_NOT_REGISTERED;
80 } 96 }
81 97
82 auto* port = Kernel::KPort::Create(kernel); 98 return it->second;
83 SCOPE_EXIT({ port->Close(); });
84
85 port->Initialize(ServerSessionCountMax, false, name);
86 auto handler = it->second;
87 port->GetServerPort().SetSessionHandler(std::move(handler));
88
89 return port;
90} 99}
91 100
92/** 101/**
@@ -145,23 +154,20 @@ ResultVal<Kernel::KClientSession*> SM::GetServiceImpl(Kernel::HLERequestContext&
145 154
146 // Find the named port. 155 // Find the named port.
147 auto port_result = service_manager.GetServicePort(name); 156 auto port_result = service_manager.GetServicePort(name);
148 if (port_result.Failed()) { 157 auto service = service_manager.GetService<Kernel::SessionRequestHandler>(name);
158 if (port_result.Failed() || !service) {
149 LOG_ERROR(Service_SM, "called service={} -> error 0x{:08X}", name, port_result.Code().raw); 159 LOG_ERROR(Service_SM, "called service={} -> error 0x{:08X}", name, port_result.Code().raw);
150 return port_result.Code(); 160 return port_result.Code();
151 } 161 }
152 auto& port = port_result.Unwrap(); 162 auto& port = port_result.Unwrap();
153 SCOPE_EXIT({ port->GetClientPort().Close(); });
154
155 kernel.RegisterServerObject(&port->GetServerPort());
156 163
157 // Create a new session. 164 // Create a new session.
158 Kernel::KClientSession* session{}; 165 Kernel::KClientSession* session{};
159 if (const auto result = port->GetClientPort().CreateSession( 166 if (const auto result = port->GetClientPort().CreateSession(&session); result.IsError()) {
160 std::addressof(session), std::make_shared<Kernel::SessionRequestManager>(kernel));
161 result.IsError()) {
162 LOG_ERROR(Service_SM, "called service={} -> error 0x{:08X}", name, result.raw); 167 LOG_ERROR(Service_SM, "called service={} -> error 0x{:08X}", name, result.raw);
163 return result; 168 return result;
164 } 169 }
170 service->AcceptSession(&port->GetServerPort());
165 171
166 LOG_DEBUG(Service_SM, "called service={} -> session={}", name, session->GetId()); 172 LOG_DEBUG(Service_SM, "called service={} -> session={}", name, session->GetId());
167 173
diff --git a/src/core/hle/service/sm/sm.h b/src/core/hle/service/sm/sm.h
index 878decc6f..02a5dde9e 100644
--- a/src/core/hle/service/sm/sm.h
+++ b/src/core/hle/service/sm/sm.h
@@ -51,6 +51,7 @@ private:
51class ServiceManager { 51class ServiceManager {
52public: 52public:
53 static Kernel::KClientPort& InterfaceFactory(ServiceManager& self, Core::System& system); 53 static Kernel::KClientPort& InterfaceFactory(ServiceManager& self, Core::System& system);
54 static void SessionHandler(ServiceManager& self, Kernel::KServerPort* server_port);
54 55
55 explicit ServiceManager(Kernel::KernelCore& kernel_); 56 explicit ServiceManager(Kernel::KernelCore& kernel_);
56 ~ServiceManager(); 57 ~ServiceManager();
@@ -78,6 +79,7 @@ private:
78 79
79 /// Map of registered services, retrieved using GetServicePort. 80 /// Map of registered services, retrieved using GetServicePort.
80 std::unordered_map<std::string, Kernel::SessionRequestHandlerPtr> registered_services; 81 std::unordered_map<std::string, Kernel::SessionRequestHandlerPtr> registered_services;
82 std::unordered_map<std::string, Kernel::KPort*> service_ports;
81 83
82 /// Kernel context 84 /// Kernel context
83 Kernel::KernelCore& kernel; 85 Kernel::KernelCore& kernel;
diff --git a/src/core/hle/service/sm/sm_controller.cpp b/src/core/hle/service/sm/sm_controller.cpp
index 273f79568..1cf9dd1c4 100644
--- a/src/core/hle/service/sm/sm_controller.cpp
+++ b/src/core/hle/service/sm/sm_controller.cpp
@@ -15,10 +15,9 @@
15namespace Service::SM { 15namespace Service::SM {
16 16
17void Controller::ConvertCurrentObjectToDomain(Kernel::HLERequestContext& ctx) { 17void Controller::ConvertCurrentObjectToDomain(Kernel::HLERequestContext& ctx) {
18 ASSERT_MSG(!ctx.Session()->GetSessionRequestManager()->IsDomain(), 18 ASSERT_MSG(!ctx.GetManager()->IsDomain(), "Session is already a domain");
19 "Session is already a domain");
20 LOG_DEBUG(Service, "called, server_session={}", ctx.Session()->GetId()); 19 LOG_DEBUG(Service, "called, server_session={}", ctx.Session()->GetId());
21 ctx.Session()->GetSessionRequestManager()->ConvertToDomainOnRequestEnd(); 20 ctx.GetManager()->ConvertToDomainOnRequestEnd();
22 21
23 IPC::ResponseBuilder rb{ctx, 3}; 22 IPC::ResponseBuilder rb{ctx, 3};
24 rb.Push(ResultSuccess); 23 rb.Push(ResultSuccess);
@@ -28,23 +27,35 @@ void Controller::ConvertCurrentObjectToDomain(Kernel::HLERequestContext& ctx) {
28void Controller::CloneCurrentObject(Kernel::HLERequestContext& ctx) { 27void Controller::CloneCurrentObject(Kernel::HLERequestContext& ctx) {
29 LOG_DEBUG(Service, "called"); 28 LOG_DEBUG(Service, "called");
30 29
31 auto& parent_session = *ctx.Session()->GetParent(); 30 auto& process = *ctx.GetThread().GetOwnerProcess();
32 auto& parent_port = parent_session.GetParent()->GetParent()->GetClientPort(); 31 auto session_manager = ctx.GetManager();
33 auto& session_manager = parent_session.GetServerSession().GetSessionRequestManager();
34 32
35 // Create a session. 33 // FIXME: this is duplicated from the SVC, it should just call it instead
36 Kernel::KClientSession* session{}; 34 // once this is a proper process
37 const Result result = parent_port.CreateSession(std::addressof(session), session_manager); 35
38 if (result.IsError()) { 36 // Reserve a new session from the process resource limit.
39 LOG_CRITICAL(Service, "CreateSession failed with error 0x{:08X}", result.raw); 37 Kernel::KScopedResourceReservation session_reservation(
40 IPC::ResponseBuilder rb{ctx, 2}; 38 &process, Kernel::LimitableResource::SessionCountMax);
41 rb.Push(result); 39 ASSERT(session_reservation.Succeeded());
42 } 40
41 // Create the session.
42 Kernel::KSession* session = Kernel::KSession::Create(system.Kernel());
43 ASSERT(session != nullptr);
44
45 // Initialize the session.
46 session->Initialize(nullptr, "");
47
48 // Commit the session reservation.
49 session_reservation.Commit();
50
51 // Register with manager.
52 session_manager->SessionHandler().RegisterSession(&session->GetServerSession(),
53 session_manager);
43 54
44 // We succeeded. 55 // We succeeded.
45 IPC::ResponseBuilder rb{ctx, 2, 0, 1, IPC::ResponseBuilder::Flags::AlwaysMoveHandles}; 56 IPC::ResponseBuilder rb{ctx, 2, 0, 1, IPC::ResponseBuilder::Flags::AlwaysMoveHandles};
46 rb.Push(ResultSuccess); 57 rb.Push(ResultSuccess);
47 rb.PushMoveObjects(session); 58 rb.PushMoveObjects(session->GetClientSession());
48} 59}
49 60
50void Controller::CloneCurrentObjectEx(Kernel::HLERequestContext& ctx) { 61void Controller::CloneCurrentObjectEx(Kernel::HLERequestContext& ctx) {
diff --git a/src/core/hle/service/vi/display/vi_display.h b/src/core/hle/service/vi/display/vi_display.h
index 33d5f398c..0b65a65da 100644
--- a/src/core/hle/service/vi/display/vi_display.h
+++ b/src/core/hle/service/vi/display/vi_display.h
@@ -106,6 +106,12 @@ public:
106 /// 106 ///
107 void CloseLayer(u64 layer_id); 107 void CloseLayer(u64 layer_id);
108 108
109 /// Resets the display for a new connection.
110 void Reset() {
111 layers.clear();
112 got_vsync_event = false;
113 }
114
109 /// Attempts to find a layer with the given ID. 115 /// Attempts to find a layer with the given ID.
110 /// 116 ///
111 /// @param layer_id The layer ID. 117 /// @param layer_id The layer ID.
diff --git a/src/core/hle/service/vi/vi.cpp b/src/core/hle/service/vi/vi.cpp
index 9c917cacf..bb283e74e 100644
--- a/src/core/hle/service/vi/vi.cpp
+++ b/src/core/hle/service/vi/vi.cpp
@@ -324,10 +324,10 @@ private:
324 IPC::RequestParser rp{ctx}; 324 IPC::RequestParser rp{ctx};
325 const u64 display = rp.Pop<u64>(); 325 const u64 display = rp.Pop<u64>();
326 326
327 LOG_WARNING(Service_VI, "(STUBBED) called. display=0x{:016X}", display); 327 const Result rc = nv_flinger.CloseDisplay(display) ? ResultSuccess : ResultUnknown;
328 328
329 IPC::ResponseBuilder rb{ctx, 2}; 329 IPC::ResponseBuilder rb{ctx, 2};
330 rb.Push(ResultSuccess); 330 rb.Push(rc);
331 } 331 }
332 332
333 void CreateManagedLayer(Kernel::HLERequestContext& ctx) { 333 void CreateManagedLayer(Kernel::HLERequestContext& ctx) {
@@ -508,10 +508,10 @@ private:
508 IPC::RequestParser rp{ctx}; 508 IPC::RequestParser rp{ctx};
509 const u64 display_id = rp.Pop<u64>(); 509 const u64 display_id = rp.Pop<u64>();
510 510
511 LOG_WARNING(Service_VI, "(STUBBED) called. display_id=0x{:016X}", display_id); 511 const Result rc = nv_flinger.CloseDisplay(display_id) ? ResultSuccess : ResultUnknown;
512 512
513 IPC::ResponseBuilder rb{ctx, 2}; 513 IPC::ResponseBuilder rb{ctx, 2};
514 rb.Push(ResultSuccess); 514 rb.Push(rc);
515 } 515 }
516 516
517 // This literally does nothing internally in the actual service itself, 517 // This literally does nothing internally in the actual service itself,
diff --git a/src/core/internal_network/socket_proxy.cpp b/src/core/internal_network/socket_proxy.cpp
index 7d5d37bbc..1e1c42cea 100644
--- a/src/core/internal_network/socket_proxy.cpp
+++ b/src/core/internal_network/socket_proxy.cpp
@@ -11,6 +11,10 @@
11#include "core/internal_network/network_interface.h" 11#include "core/internal_network/network_interface.h"
12#include "core/internal_network/socket_proxy.h" 12#include "core/internal_network/socket_proxy.h"
13 13
14#if YUZU_UNIX
15#include <sys/socket.h>
16#endif
17
14namespace Network { 18namespace Network {
15 19
16ProxySocket::ProxySocket(RoomNetwork& room_network_) noexcept : room_network{room_network_} {} 20ProxySocket::ProxySocket(RoomNetwork& room_network_) noexcept : room_network{room_network_} {}
diff --git a/src/input_common/drivers/gc_adapter.cpp b/src/input_common/drivers/gc_adapter.cpp
index f4dd24e7d..826fa2109 100644
--- a/src/input_common/drivers/gc_adapter.cpp
+++ b/src/input_common/drivers/gc_adapter.cpp
@@ -324,7 +324,7 @@ bool GCAdapter::GetGCEndpoint(libusb_device* device) {
324 return true; 324 return true;
325} 325}
326 326
327Common::Input::VibrationError GCAdapter::SetRumble( 327Common::Input::VibrationError GCAdapter::SetVibration(
328 const PadIdentifier& identifier, const Common::Input::VibrationStatus& vibration) { 328 const PadIdentifier& identifier, const Common::Input::VibrationStatus& vibration) {
329 const auto mean_amplitude = (vibration.low_amplitude + vibration.high_amplitude) * 0.5f; 329 const auto mean_amplitude = (vibration.low_amplitude + vibration.high_amplitude) * 0.5f;
330 const auto processed_amplitude = 330 const auto processed_amplitude =
@@ -338,6 +338,10 @@ Common::Input::VibrationError GCAdapter::SetRumble(
338 return Common::Input::VibrationError::None; 338 return Common::Input::VibrationError::None;
339} 339}
340 340
341bool GCAdapter::IsVibrationEnabled([[maybe_unused]] const PadIdentifier& identifier) {
342 return rumble_enabled;
343}
344
341void GCAdapter::UpdateVibrations() { 345void GCAdapter::UpdateVibrations() {
342 // Use 8 states to keep the switching between on/off fast enough for 346 // Use 8 states to keep the switching between on/off fast enough for
343 // a human to feel different vibration strenght 347 // a human to feel different vibration strenght
diff --git a/src/input_common/drivers/gc_adapter.h b/src/input_common/drivers/gc_adapter.h
index 8682da847..7f81767f7 100644
--- a/src/input_common/drivers/gc_adapter.h
+++ b/src/input_common/drivers/gc_adapter.h
@@ -25,9 +25,11 @@ public:
25 explicit GCAdapter(std::string input_engine_); 25 explicit GCAdapter(std::string input_engine_);
26 ~GCAdapter() override; 26 ~GCAdapter() override;
27 27
28 Common::Input::VibrationError SetRumble( 28 Common::Input::VibrationError SetVibration(
29 const PadIdentifier& identifier, const Common::Input::VibrationStatus& vibration) override; 29 const PadIdentifier& identifier, const Common::Input::VibrationStatus& vibration) override;
30 30
31 bool IsVibrationEnabled(const PadIdentifier& identifier) override;
32
31 /// Used for automapping features 33 /// Used for automapping features
32 std::vector<Common::ParamPackage> GetInputDevices() const override; 34 std::vector<Common::ParamPackage> GetInputDevices() const override;
33 ButtonMapping GetButtonMappingForDevice(const Common::ParamPackage& params) override; 35 ButtonMapping GetButtonMappingForDevice(const Common::ParamPackage& params) override;
diff --git a/src/input_common/drivers/sdl_driver.cpp b/src/input_common/drivers/sdl_driver.cpp
index c175a8853..45ce588f0 100644
--- a/src/input_common/drivers/sdl_driver.cpp
+++ b/src/input_common/drivers/sdl_driver.cpp
@@ -114,6 +114,20 @@ public:
114 } 114 }
115 return false; 115 return false;
116 } 116 }
117
118 void EnableVibration(bool is_enabled) {
119 has_vibration = is_enabled;
120 is_vibration_tested = true;
121 }
122
123 bool HasVibration() const {
124 return has_vibration;
125 }
126
127 bool IsVibrationTested() const {
128 return is_vibration_tested;
129 }
130
117 /** 131 /**
118 * The Pad identifier of the joystick 132 * The Pad identifier of the joystick
119 */ 133 */
@@ -236,6 +250,8 @@ private:
236 u64 last_motion_update{}; 250 u64 last_motion_update{};
237 bool has_gyro{false}; 251 bool has_gyro{false};
238 bool has_accel{false}; 252 bool has_accel{false};
253 bool has_vibration{false};
254 bool is_vibration_tested{false};
239 BasicMotion motion; 255 BasicMotion motion;
240}; 256};
241 257
@@ -517,7 +533,7 @@ std::vector<Common::ParamPackage> SDLDriver::GetInputDevices() const {
517 return devices; 533 return devices;
518} 534}
519 535
520Common::Input::VibrationError SDLDriver::SetRumble( 536Common::Input::VibrationError SDLDriver::SetVibration(
521 const PadIdentifier& identifier, const Common::Input::VibrationStatus& vibration) { 537 const PadIdentifier& identifier, const Common::Input::VibrationStatus& vibration) {
522 const auto joystick = 538 const auto joystick =
523 GetSDLJoystickByGUID(identifier.guid.RawString(), static_cast<int>(identifier.port)); 539 GetSDLJoystickByGUID(identifier.guid.RawString(), static_cast<int>(identifier.port));
@@ -546,13 +562,6 @@ Common::Input::VibrationError SDLDriver::SetRumble(
546 .type = Common::Input::VibrationAmplificationType::Exponential, 562 .type = Common::Input::VibrationAmplificationType::Exponential,
547 }; 563 };
548 564
549 if (vibration.type == Common::Input::VibrationAmplificationType::Test) {
550 if (!joystick->RumblePlay(new_vibration)) {
551 return Common::Input::VibrationError::Unknown;
552 }
553 return Common::Input::VibrationError::None;
554 }
555
556 vibration_queue.Push(VibrationRequest{ 565 vibration_queue.Push(VibrationRequest{
557 .identifier = identifier, 566 .identifier = identifier,
558 .vibration = new_vibration, 567 .vibration = new_vibration,
@@ -561,6 +570,45 @@ Common::Input::VibrationError SDLDriver::SetRumble(
561 return Common::Input::VibrationError::None; 570 return Common::Input::VibrationError::None;
562} 571}
563 572
573bool SDLDriver::IsVibrationEnabled(const PadIdentifier& identifier) {
574 const auto joystick =
575 GetSDLJoystickByGUID(identifier.guid.RawString(), static_cast<int>(identifier.port));
576
577 constexpr Common::Input::VibrationStatus test_vibration{
578 .low_amplitude = 1,
579 .low_frequency = 160.0f,
580 .high_amplitude = 1,
581 .high_frequency = 320.0f,
582 .type = Common::Input::VibrationAmplificationType::Exponential,
583 };
584
585 constexpr Common::Input::VibrationStatus zero_vibration{
586 .low_amplitude = 0,
587 .low_frequency = 160.0f,
588 .high_amplitude = 0,
589 .high_frequency = 320.0f,
590 .type = Common::Input::VibrationAmplificationType::Exponential,
591 };
592
593 if (joystick->IsVibrationTested()) {
594 return joystick->HasVibration();
595 }
596
597 // First vibration might fail
598 joystick->RumblePlay(test_vibration);
599
600 // Wait for about 15ms to ensure the controller is ready for the stop command
601 std::this_thread::sleep_for(std::chrono::milliseconds(15));
602
603 if (!joystick->RumblePlay(zero_vibration)) {
604 joystick->EnableVibration(false);
605 return false;
606 }
607
608 joystick->EnableVibration(true);
609 return true;
610}
611
564void SDLDriver::SendVibrations() { 612void SDLDriver::SendVibrations() {
565 while (!vibration_queue.Empty()) { 613 while (!vibration_queue.Empty()) {
566 VibrationRequest request; 614 VibrationRequest request;
diff --git a/src/input_common/drivers/sdl_driver.h b/src/input_common/drivers/sdl_driver.h
index fc3a44572..d1b4471cf 100644
--- a/src/input_common/drivers/sdl_driver.h
+++ b/src/input_common/drivers/sdl_driver.h
@@ -61,9 +61,11 @@ public:
61 61
62 bool IsStickInverted(const Common::ParamPackage& params) override; 62 bool IsStickInverted(const Common::ParamPackage& params) override;
63 63
64 Common::Input::VibrationError SetRumble( 64 Common::Input::VibrationError SetVibration(
65 const PadIdentifier& identifier, const Common::Input::VibrationStatus& vibration) override; 65 const PadIdentifier& identifier, const Common::Input::VibrationStatus& vibration) override;
66 66
67 bool IsVibrationEnabled(const PadIdentifier& identifier) override;
68
67private: 69private:
68 struct VibrationRequest { 70 struct VibrationRequest {
69 PadIdentifier identifier; 71 PadIdentifier identifier;
diff --git a/src/input_common/input_engine.h b/src/input_common/input_engine.h
index cfbdb26bd..d4c264a8e 100644
--- a/src/input_common/input_engine.h
+++ b/src/input_common/input_engine.h
@@ -108,12 +108,17 @@ public:
108 [[maybe_unused]] const Common::Input::LedStatus& led_status) {} 108 [[maybe_unused]] const Common::Input::LedStatus& led_status) {}
109 109
110 // Sets rumble to a controller 110 // Sets rumble to a controller
111 virtual Common::Input::VibrationError SetRumble( 111 virtual Common::Input::VibrationError SetVibration(
112 [[maybe_unused]] const PadIdentifier& identifier, 112 [[maybe_unused]] const PadIdentifier& identifier,
113 [[maybe_unused]] const Common::Input::VibrationStatus& vibration) { 113 [[maybe_unused]] const Common::Input::VibrationStatus& vibration) {
114 return Common::Input::VibrationError::NotSupported; 114 return Common::Input::VibrationError::NotSupported;
115 } 115 }
116 116
117 // Returns true if device supports vibrations
118 virtual bool IsVibrationEnabled([[maybe_unused]] const PadIdentifier& identifier) {
119 return false;
120 }
121
117 // Sets polling mode to a controller 122 // Sets polling mode to a controller
118 virtual Common::Input::PollingError SetPollingMode( 123 virtual Common::Input::PollingError SetPollingMode(
119 [[maybe_unused]] const PadIdentifier& identifier, 124 [[maybe_unused]] const PadIdentifier& identifier,
diff --git a/src/input_common/input_poller.cpp b/src/input_common/input_poller.cpp
index ccc3076ca..4ac182147 100644
--- a/src/input_common/input_poller.cpp
+++ b/src/input_common/input_poller.cpp
@@ -763,7 +763,11 @@ public:
763 763
764 Common::Input::VibrationError SetVibration( 764 Common::Input::VibrationError SetVibration(
765 const Common::Input::VibrationStatus& vibration_status) override { 765 const Common::Input::VibrationStatus& vibration_status) override {
766 return input_engine->SetRumble(identifier, vibration_status); 766 return input_engine->SetVibration(identifier, vibration_status);
767 }
768
769 bool IsVibrationEnabled() override {
770 return input_engine->IsVibrationEnabled(identifier);
767 } 771 }
768 772
769 Common::Input::PollingError SetPollingMode(Common::Input::PollingMode polling_mode) override { 773 Common::Input::PollingError SetPollingMode(Common::Input::PollingMode polling_mode) override {
diff --git a/src/shader_recompiler/CMakeLists.txt b/src/shader_recompiler/CMakeLists.txt
index bcdd60db9..545d69c7e 100644
--- a/src/shader_recompiler/CMakeLists.txt
+++ b/src/shader_recompiler/CMakeLists.txt
@@ -224,6 +224,7 @@ add_library(shader_recompiler STATIC
224 ir_opt/lower_fp16_to_fp32.cpp 224 ir_opt/lower_fp16_to_fp32.cpp
225 ir_opt/lower_int64_to_int32.cpp 225 ir_opt/lower_int64_to_int32.cpp
226 ir_opt/passes.h 226 ir_opt/passes.h
227 ir_opt/position_pass.cpp
227 ir_opt/rescaling_pass.cpp 228 ir_opt/rescaling_pass.cpp
228 ir_opt/ssa_rewrite_pass.cpp 229 ir_opt/ssa_rewrite_pass.cpp
229 ir_opt/texture_pass.cpp 230 ir_opt/texture_pass.cpp
diff --git a/src/shader_recompiler/backend/glasm/emit_glasm.cpp b/src/shader_recompiler/backend/glasm/emit_glasm.cpp
index 01f9abc71..3b0176bf6 100644
--- a/src/shader_recompiler/backend/glasm/emit_glasm.cpp
+++ b/src/shader_recompiler/backend/glasm/emit_glasm.cpp
@@ -450,6 +450,9 @@ std::string EmitGLASM(const Profile& profile, const RuntimeInfo& runtime_info, I
450 if (program.info.uses_rescaling_uniform) { 450 if (program.info.uses_rescaling_uniform) {
451 header += "PARAM scaling[1]={program.local[0..0]};"; 451 header += "PARAM scaling[1]={program.local[0..0]};";
452 } 452 }
453 if (program.info.uses_render_area) {
454 header += "PARAM render_area[1]={program.local[1..1]};";
455 }
453 header += "TEMP "; 456 header += "TEMP ";
454 for (size_t index = 0; index < ctx.reg_alloc.NumUsedRegisters(); ++index) { 457 for (size_t index = 0; index < ctx.reg_alloc.NumUsedRegisters(); ++index) {
455 header += fmt::format("R{},", index); 458 header += fmt::format("R{},", index);
diff --git a/src/shader_recompiler/backend/glasm/emit_glasm_bitwise_conversion.cpp b/src/shader_recompiler/backend/glasm/emit_glasm_bitwise_conversion.cpp
index 2fc2a0ac6..5bfdecc09 100644
--- a/src/shader_recompiler/backend/glasm/emit_glasm_bitwise_conversion.cpp
+++ b/src/shader_recompiler/backend/glasm/emit_glasm_bitwise_conversion.cpp
@@ -43,6 +43,10 @@ void EmitBitCastU64F64(EmitContext&, IR::Inst& inst, const IR::Value& value) {
43 Alias(inst, value); 43 Alias(inst, value);
44} 44}
45 45
46void EmitBitCastS32F32(EmitContext&, IR::Inst& inst, const IR::Value& value) {
47 Alias(inst, value);
48}
49
46void EmitBitCastF16U16(EmitContext&, IR::Inst& inst, const IR::Value& value) { 50void EmitBitCastF16U16(EmitContext&, IR::Inst& inst, const IR::Value& value) {
47 Alias(inst, value); 51 Alias(inst, value);
48} 52}
diff --git a/src/shader_recompiler/backend/glasm/emit_glasm_context_get_set.cpp b/src/shader_recompiler/backend/glasm/emit_glasm_context_get_set.cpp
index 7e8f37563..d6562c842 100644
--- a/src/shader_recompiler/backend/glasm/emit_glasm_context_get_set.cpp
+++ b/src/shader_recompiler/backend/glasm/emit_glasm_context_get_set.cpp
@@ -379,6 +379,18 @@ void EmitInvocationId(EmitContext& ctx, IR::Inst& inst) {
379 ctx.Add("MOV.S {}.x,primitive_invocation.x;", inst); 379 ctx.Add("MOV.S {}.x,primitive_invocation.x;", inst);
380} 380}
381 381
382void EmitInvocationInfo(EmitContext& ctx, IR::Inst& inst) {
383 switch (ctx.stage) {
384 case Stage::TessellationControl:
385 case Stage::TessellationEval:
386 ctx.Add("SHL.U {}.x,primitive.vertexcount,16;", inst);
387 break;
388 default:
389 LOG_WARNING(Shader, "(STUBBED) called");
390 ctx.Add("MOV.S {}.x,0x00ff0000;", inst);
391 }
392}
393
382void EmitSampleId(EmitContext& ctx, IR::Inst& inst) { 394void EmitSampleId(EmitContext& ctx, IR::Inst& inst) {
383 ctx.Add("MOV.S {}.x,fragment.sampleid.x;", inst); 395 ctx.Add("MOV.S {}.x,fragment.sampleid.x;", inst);
384} 396}
@@ -396,6 +408,10 @@ void EmitResolutionDownFactor(EmitContext& ctx, IR::Inst& inst) {
396 ctx.Add("MOV.F {}.x,scaling[0].z;", inst); 408 ctx.Add("MOV.F {}.x,scaling[0].z;", inst);
397} 409}
398 410
411void EmitRenderArea(EmitContext& ctx, IR::Inst& inst) {
412 ctx.Add("MOV.F {},render_area[0];", inst);
413}
414
399void EmitLoadLocal(EmitContext& ctx, IR::Inst& inst, ScalarU32 word_offset) { 415void EmitLoadLocal(EmitContext& ctx, IR::Inst& inst, ScalarU32 word_offset) {
400 ctx.Add("MOV.U {},lmem[{}].x;", inst, word_offset); 416 ctx.Add("MOV.U {},lmem[{}].x;", inst, word_offset);
401} 417}
diff --git a/src/shader_recompiler/backend/glasm/emit_glasm_instructions.h b/src/shader_recompiler/backend/glasm/emit_glasm_instructions.h
index 8b0ac3031..eaaf9ba39 100644
--- a/src/shader_recompiler/backend/glasm/emit_glasm_instructions.h
+++ b/src/shader_recompiler/backend/glasm/emit_glasm_instructions.h
@@ -69,10 +69,12 @@ void EmitSetOFlag(EmitContext& ctx);
69void EmitWorkgroupId(EmitContext& ctx, IR::Inst& inst); 69void EmitWorkgroupId(EmitContext& ctx, IR::Inst& inst);
70void EmitLocalInvocationId(EmitContext& ctx, IR::Inst& inst); 70void EmitLocalInvocationId(EmitContext& ctx, IR::Inst& inst);
71void EmitInvocationId(EmitContext& ctx, IR::Inst& inst); 71void EmitInvocationId(EmitContext& ctx, IR::Inst& inst);
72void EmitInvocationInfo(EmitContext& ctx, IR::Inst& inst);
72void EmitSampleId(EmitContext& ctx, IR::Inst& inst); 73void EmitSampleId(EmitContext& ctx, IR::Inst& inst);
73void EmitIsHelperInvocation(EmitContext& ctx, IR::Inst& inst); 74void EmitIsHelperInvocation(EmitContext& ctx, IR::Inst& inst);
74void EmitYDirection(EmitContext& ctx, IR::Inst& inst); 75void EmitYDirection(EmitContext& ctx, IR::Inst& inst);
75void EmitResolutionDownFactor(EmitContext& ctx, IR::Inst& inst); 76void EmitResolutionDownFactor(EmitContext& ctx, IR::Inst& inst);
77void EmitRenderArea(EmitContext& ctx, IR::Inst& inst);
76void EmitLoadLocal(EmitContext& ctx, IR::Inst& inst, ScalarU32 word_offset); 78void EmitLoadLocal(EmitContext& ctx, IR::Inst& inst, ScalarU32 word_offset);
77void EmitWriteLocal(EmitContext& ctx, ScalarU32 word_offset, ScalarU32 value); 79void EmitWriteLocal(EmitContext& ctx, ScalarU32 word_offset, ScalarU32 value);
78void EmitUndefU1(EmitContext& ctx, IR::Inst& inst); 80void EmitUndefU1(EmitContext& ctx, IR::Inst& inst);
@@ -195,6 +197,7 @@ void EmitSelectF64(EmitContext& ctx, ScalarS32 cond, Register true_value, Regist
195void EmitBitCastU16F16(EmitContext& ctx, IR::Inst& inst, const IR::Value& value); 197void EmitBitCastU16F16(EmitContext& ctx, IR::Inst& inst, const IR::Value& value);
196void EmitBitCastU32F32(EmitContext& ctx, IR::Inst& inst, const IR::Value& value); 198void EmitBitCastU32F32(EmitContext& ctx, IR::Inst& inst, const IR::Value& value);
197void EmitBitCastU64F64(EmitContext& ctx, IR::Inst& inst, const IR::Value& value); 199void EmitBitCastU64F64(EmitContext& ctx, IR::Inst& inst, const IR::Value& value);
200void EmitBitCastS32F32(EmitContext& ctx, IR::Inst& inst, const IR::Value& value);
198void EmitBitCastF16U16(EmitContext& ctx, IR::Inst& inst, const IR::Value& value); 201void EmitBitCastF16U16(EmitContext& ctx, IR::Inst& inst, const IR::Value& value);
199void EmitBitCastF32U32(EmitContext& ctx, IR::Inst& inst, const IR::Value& value); 202void EmitBitCastF32U32(EmitContext& ctx, IR::Inst& inst, const IR::Value& value);
200void EmitBitCastF64U64(EmitContext& ctx, IR::Inst& inst, const IR::Value& value); 203void EmitBitCastF64U64(EmitContext& ctx, IR::Inst& inst, const IR::Value& value);
diff --git a/src/shader_recompiler/backend/glasm/glasm_emit_context.cpp b/src/shader_recompiler/backend/glasm/glasm_emit_context.cpp
index 89603c1c4..333a91cc5 100644
--- a/src/shader_recompiler/backend/glasm/glasm_emit_context.cpp
+++ b/src/shader_recompiler/backend/glasm/glasm_emit_context.cpp
@@ -95,6 +95,10 @@ EmitContext::EmitContext(IR::Program& program, Bindings& bindings, const Profile
95 if (info.uses_invocation_id) { 95 if (info.uses_invocation_id) {
96 Add("ATTRIB primitive_invocation=primitive.invocation;"); 96 Add("ATTRIB primitive_invocation=primitive.invocation;");
97 } 97 }
98 if (info.uses_invocation_info &&
99 (stage == Stage::TessellationControl || stage == Stage::TessellationEval)) {
100 Add("ATTRIB primitive_vertexcount = primitive.vertexcount;");
101 }
98 if (info.stores_tess_level_outer) { 102 if (info.stores_tess_level_outer) {
99 Add("OUTPUT result_patch_tessouter[]={{result.patch.tessouter[0..3]}};"); 103 Add("OUTPUT result_patch_tessouter[]={{result.patch.tessouter[0..3]}};");
100 } 104 }
diff --git a/src/shader_recompiler/backend/glsl/emit_glsl_bitwise_conversion.cpp b/src/shader_recompiler/backend/glsl/emit_glsl_bitwise_conversion.cpp
index 1be4a0f59..8e5e6cf1f 100644
--- a/src/shader_recompiler/backend/glsl/emit_glsl_bitwise_conversion.cpp
+++ b/src/shader_recompiler/backend/glsl/emit_glsl_bitwise_conversion.cpp
@@ -48,6 +48,10 @@ void EmitBitCastU64F64(EmitContext& ctx, IR::Inst& inst, std::string_view value)
48 ctx.AddU64("{}=doubleBitsToUint64({});", inst, value); 48 ctx.AddU64("{}=doubleBitsToUint64({});", inst, value);
49} 49}
50 50
51void EmitBitCastS32F32(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
52 ctx.AddF32("{}=ftoi({});", inst, value);
53}
54
51void EmitBitCastF16U16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst) { 55void EmitBitCastF16U16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst) {
52 NotImplemented(); 56 NotImplemented();
53} 57}
diff --git a/src/shader_recompiler/backend/glsl/emit_glsl_context_get_set.cpp b/src/shader_recompiler/backend/glsl/emit_glsl_context_get_set.cpp
index fad8d1e30..c1671c37b 100644
--- a/src/shader_recompiler/backend/glsl/emit_glsl_context_get_set.cpp
+++ b/src/shader_recompiler/backend/glsl/emit_glsl_context_get_set.cpp
@@ -399,6 +399,18 @@ void EmitInvocationId(EmitContext& ctx, IR::Inst& inst) {
399 ctx.AddU32("{}=uint(gl_InvocationID);", inst); 399 ctx.AddU32("{}=uint(gl_InvocationID);", inst);
400} 400}
401 401
402void EmitInvocationInfo(EmitContext& ctx, IR::Inst& inst) {
403 switch (ctx.stage) {
404 case Stage::TessellationControl:
405 case Stage::TessellationEval:
406 ctx.AddU32("{}=uint(gl_PatchVerticesIn)<<16;", inst);
407 break;
408 default:
409 LOG_WARNING(Shader, "(STUBBED) called");
410 ctx.AddU32("{}=uint(0x00ff0000);", inst);
411 }
412}
413
402void EmitSampleId(EmitContext& ctx, IR::Inst& inst) { 414void EmitSampleId(EmitContext& ctx, IR::Inst& inst) {
403 ctx.AddU32("{}=uint(gl_SampleID);", inst); 415 ctx.AddU32("{}=uint(gl_SampleID);", inst);
404} 416}
@@ -416,6 +428,10 @@ void EmitResolutionDownFactor(EmitContext& ctx, IR::Inst& inst) {
416 ctx.AddF32("{}=scaling.z;", inst); 428 ctx.AddF32("{}=scaling.z;", inst);
417} 429}
418 430
431void EmitRenderArea(EmitContext& ctx, IR::Inst& inst) {
432 ctx.AddF32x4("{}=render_area;", inst);
433}
434
419void EmitLoadLocal(EmitContext& ctx, IR::Inst& inst, std::string_view word_offset) { 435void EmitLoadLocal(EmitContext& ctx, IR::Inst& inst, std::string_view word_offset) {
420 ctx.AddU32("{}=lmem[{}];", inst, word_offset); 436 ctx.AddU32("{}=lmem[{}];", inst, word_offset);
421} 437}
diff --git a/src/shader_recompiler/backend/glsl/emit_glsl_instructions.h b/src/shader_recompiler/backend/glsl/emit_glsl_instructions.h
index 639691ba6..4151c89de 100644
--- a/src/shader_recompiler/backend/glsl/emit_glsl_instructions.h
+++ b/src/shader_recompiler/backend/glsl/emit_glsl_instructions.h
@@ -83,10 +83,12 @@ void EmitSetOFlag(EmitContext& ctx);
83void EmitWorkgroupId(EmitContext& ctx, IR::Inst& inst); 83void EmitWorkgroupId(EmitContext& ctx, IR::Inst& inst);
84void EmitLocalInvocationId(EmitContext& ctx, IR::Inst& inst); 84void EmitLocalInvocationId(EmitContext& ctx, IR::Inst& inst);
85void EmitInvocationId(EmitContext& ctx, IR::Inst& inst); 85void EmitInvocationId(EmitContext& ctx, IR::Inst& inst);
86void EmitInvocationInfo(EmitContext& ctx, IR::Inst& inst);
86void EmitSampleId(EmitContext& ctx, IR::Inst& inst); 87void EmitSampleId(EmitContext& ctx, IR::Inst& inst);
87void EmitIsHelperInvocation(EmitContext& ctx, IR::Inst& inst); 88void EmitIsHelperInvocation(EmitContext& ctx, IR::Inst& inst);
88void EmitYDirection(EmitContext& ctx, IR::Inst& inst); 89void EmitYDirection(EmitContext& ctx, IR::Inst& inst);
89void EmitResolutionDownFactor(EmitContext& ctx, IR::Inst& inst); 90void EmitResolutionDownFactor(EmitContext& ctx, IR::Inst& inst);
91void EmitRenderArea(EmitContext& ctx, IR::Inst& inst);
90void EmitLoadLocal(EmitContext& ctx, IR::Inst& inst, std::string_view word_offset); 92void EmitLoadLocal(EmitContext& ctx, IR::Inst& inst, std::string_view word_offset);
91void EmitWriteLocal(EmitContext& ctx, std::string_view word_offset, std::string_view value); 93void EmitWriteLocal(EmitContext& ctx, std::string_view word_offset, std::string_view value);
92void EmitUndefU1(EmitContext& ctx, IR::Inst& inst); 94void EmitUndefU1(EmitContext& ctx, IR::Inst& inst);
@@ -229,6 +231,7 @@ void EmitSelectF64(EmitContext& ctx, IR::Inst& inst, std::string_view cond,
229void EmitBitCastU16F16(EmitContext& ctx, IR::Inst& inst); 231void EmitBitCastU16F16(EmitContext& ctx, IR::Inst& inst);
230void EmitBitCastU32F32(EmitContext& ctx, IR::Inst& inst, std::string_view value); 232void EmitBitCastU32F32(EmitContext& ctx, IR::Inst& inst, std::string_view value);
231void EmitBitCastU64F64(EmitContext& ctx, IR::Inst& inst, std::string_view value); 233void EmitBitCastU64F64(EmitContext& ctx, IR::Inst& inst, std::string_view value);
234void EmitBitCastS32F32(EmitContext& ctx, IR::Inst& inst, std::string_view value);
232void EmitBitCastF16U16(EmitContext& ctx, IR::Inst& inst); 235void EmitBitCastF16U16(EmitContext& ctx, IR::Inst& inst);
233void EmitBitCastF32U32(EmitContext& ctx, IR::Inst& inst, std::string_view value); 236void EmitBitCastF32U32(EmitContext& ctx, IR::Inst& inst, std::string_view value);
234void EmitBitCastF64U64(EmitContext& ctx, IR::Inst& inst, std::string_view value); 237void EmitBitCastF64U64(EmitContext& ctx, IR::Inst& inst, std::string_view value);
diff --git a/src/shader_recompiler/backend/glsl/glsl_emit_context.cpp b/src/shader_recompiler/backend/glsl/glsl_emit_context.cpp
index c767a9dc3..5d01ec0cd 100644
--- a/src/shader_recompiler/backend/glsl/glsl_emit_context.cpp
+++ b/src/shader_recompiler/backend/glsl/glsl_emit_context.cpp
@@ -358,6 +358,9 @@ EmitContext::EmitContext(IR::Program& program, Bindings& bindings, const Profile
358 if (info.uses_rescaling_uniform) { 358 if (info.uses_rescaling_uniform) {
359 header += "layout(location=0) uniform vec4 scaling;"; 359 header += "layout(location=0) uniform vec4 scaling;";
360 } 360 }
361 if (info.uses_render_area) {
362 header += "layout(location=1) uniform vec4 render_area;";
363 }
361 DefineConstantBuffers(bindings); 364 DefineConstantBuffers(bindings);
362 DefineConstantBufferIndirect(); 365 DefineConstantBufferIndirect();
363 DefineStorageBuffers(bindings); 366 DefineStorageBuffers(bindings);
diff --git a/src/shader_recompiler/backend/spirv/emit_spirv.h b/src/shader_recompiler/backend/spirv/emit_spirv.h
index 7567b6fc9..937881484 100644
--- a/src/shader_recompiler/backend/spirv/emit_spirv.h
+++ b/src/shader_recompiler/backend/spirv/emit_spirv.h
@@ -23,8 +23,12 @@ struct RescalingLayout {
23 alignas(16) std::array<u32, NUM_IMAGE_SCALING_WORDS> rescaling_images; 23 alignas(16) std::array<u32, NUM_IMAGE_SCALING_WORDS> rescaling_images;
24 u32 down_factor; 24 u32 down_factor;
25}; 25};
26struct RenderAreaLayout {
27 std::array<f32, 4> render_area;
28};
26constexpr u32 RESCALING_LAYOUT_WORDS_OFFSET = offsetof(RescalingLayout, rescaling_textures); 29constexpr u32 RESCALING_LAYOUT_WORDS_OFFSET = offsetof(RescalingLayout, rescaling_textures);
27constexpr u32 RESCALING_LAYOUT_DOWN_FACTOR_OFFSET = offsetof(RescalingLayout, down_factor); 30constexpr u32 RESCALING_LAYOUT_DOWN_FACTOR_OFFSET = offsetof(RescalingLayout, down_factor);
31constexpr u32 RENDERAREA_LAYOUT_OFFSET = offsetof(RenderAreaLayout, render_area);
28 32
29[[nodiscard]] std::vector<u32> EmitSPIRV(const Profile& profile, const RuntimeInfo& runtime_info, 33[[nodiscard]] std::vector<u32> EmitSPIRV(const Profile& profile, const RuntimeInfo& runtime_info,
30 IR::Program& program, Bindings& bindings); 34 IR::Program& program, Bindings& bindings);
diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_bitwise_conversion.cpp b/src/shader_recompiler/backend/spirv/emit_spirv_bitwise_conversion.cpp
index c4ca28d11..50daacd95 100644
--- a/src/shader_recompiler/backend/spirv/emit_spirv_bitwise_conversion.cpp
+++ b/src/shader_recompiler/backend/spirv/emit_spirv_bitwise_conversion.cpp
@@ -18,6 +18,10 @@ void EmitBitCastU64F64(EmitContext&) {
18 throw NotImplementedException("SPIR-V Instruction"); 18 throw NotImplementedException("SPIR-V Instruction");
19} 19}
20 20
21void EmitBitCastS32F32(EmitContext&) {
22 throw NotImplementedException("SPIR-V Instruction");
23}
24
21void EmitBitCastF16U16(EmitContext&) { 25void EmitBitCastF16U16(EmitContext&) {
22 throw NotImplementedException("SPIR-V Instruction"); 26 throw NotImplementedException("SPIR-V Instruction");
23} 27}
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 2c68aba39..5b3b5d1f3 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
@@ -353,7 +353,6 @@ Id EmitGetAttribute(EmitContext& ctx, IR::Attribute attr, Id vertex) {
353 case IR::Attribute::TessellationEvaluationPointV: 353 case IR::Attribute::TessellationEvaluationPointV:
354 return ctx.OpLoad(ctx.F32[1], 354 return ctx.OpLoad(ctx.F32[1],
355 ctx.OpAccessChain(ctx.input_f32, ctx.tess_coord, ctx.Const(1U))); 355 ctx.OpAccessChain(ctx.input_f32, ctx.tess_coord, ctx.Const(1U)));
356
357 default: 356 default:
358 throw NotImplementedException("Read attribute {}", attr); 357 throw NotImplementedException("Read attribute {}", attr);
359 } 358 }
@@ -513,6 +512,18 @@ Id EmitInvocationId(EmitContext& ctx) {
513 return ctx.OpLoad(ctx.U32[1], ctx.invocation_id); 512 return ctx.OpLoad(ctx.U32[1], ctx.invocation_id);
514} 513}
515 514
515Id EmitInvocationInfo(EmitContext& ctx) {
516 switch (ctx.stage) {
517 case Stage::TessellationControl:
518 case Stage::TessellationEval:
519 return ctx.OpShiftLeftLogical(ctx.U32[1], ctx.OpLoad(ctx.U32[1], ctx.patch_vertices_in),
520 ctx.Const(16u));
521 default:
522 LOG_WARNING(Shader, "(STUBBED) called");
523 return ctx.Const(0x00ff0000u);
524 }
525}
526
516Id EmitSampleId(EmitContext& ctx) { 527Id EmitSampleId(EmitContext& ctx) {
517 return ctx.OpLoad(ctx.U32[1], ctx.sample_id); 528 return ctx.OpLoad(ctx.U32[1], ctx.sample_id);
518} 529}
@@ -537,6 +548,17 @@ Id EmitResolutionDownFactor(EmitContext& ctx) {
537 } 548 }
538} 549}
539 550
551Id EmitRenderArea(EmitContext& ctx) {
552 if (ctx.profile.unified_descriptor_binding) {
553 const Id pointer_type{ctx.TypePointer(spv::StorageClass::PushConstant, ctx.F32[4])};
554 const Id index{ctx.Const(ctx.render_are_member_index)};
555 const Id pointer{ctx.OpAccessChain(pointer_type, ctx.render_area_push_constant, index)};
556 return ctx.OpLoad(ctx.F32[4], pointer);
557 } else {
558 throw NotImplementedException("SPIR-V Instruction");
559 }
560}
561
540Id EmitLoadLocal(EmitContext& ctx, Id word_offset) { 562Id EmitLoadLocal(EmitContext& ctx, Id word_offset) {
541 const Id pointer{ctx.OpAccessChain(ctx.private_u32, ctx.local_memory, word_offset)}; 563 const Id pointer{ctx.OpAccessChain(ctx.private_u32, ctx.local_memory, word_offset)};
542 return ctx.OpLoad(ctx.U32[1], pointer); 564 return ctx.OpLoad(ctx.U32[1], pointer);
diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_instructions.h b/src/shader_recompiler/backend/spirv/emit_spirv_instructions.h
index 984d072b4..e31cdc5e8 100644
--- a/src/shader_recompiler/backend/spirv/emit_spirv_instructions.h
+++ b/src/shader_recompiler/backend/spirv/emit_spirv_instructions.h
@@ -72,10 +72,12 @@ void EmitSetOFlag(EmitContext& ctx);
72Id EmitWorkgroupId(EmitContext& ctx); 72Id EmitWorkgroupId(EmitContext& ctx);
73Id EmitLocalInvocationId(EmitContext& ctx); 73Id EmitLocalInvocationId(EmitContext& ctx);
74Id EmitInvocationId(EmitContext& ctx); 74Id EmitInvocationId(EmitContext& ctx);
75Id EmitInvocationInfo(EmitContext& ctx);
75Id EmitSampleId(EmitContext& ctx); 76Id EmitSampleId(EmitContext& ctx);
76Id EmitIsHelperInvocation(EmitContext& ctx); 77Id EmitIsHelperInvocation(EmitContext& ctx);
77Id EmitYDirection(EmitContext& ctx); 78Id EmitYDirection(EmitContext& ctx);
78Id EmitResolutionDownFactor(EmitContext& ctx); 79Id EmitResolutionDownFactor(EmitContext& ctx);
80Id EmitRenderArea(EmitContext& ctx);
79Id EmitLoadLocal(EmitContext& ctx, Id word_offset); 81Id EmitLoadLocal(EmitContext& ctx, Id word_offset);
80void EmitWriteLocal(EmitContext& ctx, Id word_offset, Id value); 82void EmitWriteLocal(EmitContext& ctx, Id word_offset, Id value);
81Id EmitUndefU1(EmitContext& ctx); 83Id EmitUndefU1(EmitContext& ctx);
@@ -177,7 +179,8 @@ Id EmitSelectF64(EmitContext& ctx, Id cond, Id true_value, Id false_value);
177void EmitBitCastU16F16(EmitContext& ctx); 179void EmitBitCastU16F16(EmitContext& ctx);
178Id EmitBitCastU32F32(EmitContext& ctx, Id value); 180Id EmitBitCastU32F32(EmitContext& ctx, Id value);
179void EmitBitCastU64F64(EmitContext& ctx); 181void EmitBitCastU64F64(EmitContext& ctx);
180void EmitBitCastF16U16(EmitContext& ctx); 182void EmitBitCastS32F32(EmitContext& ctx);
183void EmitBitCastF16U16(EmitContext&);
181Id EmitBitCastF32U32(EmitContext& ctx, Id value); 184Id EmitBitCastF32U32(EmitContext& ctx, Id value);
182void EmitBitCastF64U64(EmitContext& ctx); 185void EmitBitCastF64U64(EmitContext& ctx);
183Id EmitPackUint2x32(EmitContext& ctx, Id value); 186Id EmitPackUint2x32(EmitContext& ctx, Id value);
diff --git a/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp b/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp
index aecc4c612..0bfc2dd89 100644
--- a/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp
+++ b/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp
@@ -473,6 +473,7 @@ EmitContext::EmitContext(const Profile& profile_, const RuntimeInfo& runtime_inf
473 DefineAttributeMemAccess(program.info); 473 DefineAttributeMemAccess(program.info);
474 DefineGlobalMemoryFunctions(program.info); 474 DefineGlobalMemoryFunctions(program.info);
475 DefineRescalingInput(program.info); 475 DefineRescalingInput(program.info);
476 DefineRenderArea(program.info);
476} 477}
477 478
478EmitContext::~EmitContext() = default; 479EmitContext::~EmitContext() = default;
@@ -982,6 +983,36 @@ void EmitContext::DefineRescalingInputUniformConstant() {
982 } 983 }
983} 984}
984 985
986void EmitContext::DefineRenderArea(const Info& info) {
987 if (!info.uses_render_area) {
988 return;
989 }
990
991 if (profile.unified_descriptor_binding) {
992 boost::container::static_vector<Id, 1> members{};
993 u32 member_index{0};
994
995 members.push_back(F32[4]);
996 render_are_member_index = member_index++;
997
998 const Id push_constant_struct{TypeStruct(std::span(members.data(), members.size()))};
999 Decorate(push_constant_struct, spv::Decoration::Block);
1000 Name(push_constant_struct, "RenderAreaInfo");
1001
1002 MemberDecorate(push_constant_struct, render_are_member_index, spv::Decoration::Offset, 0);
1003 MemberName(push_constant_struct, render_are_member_index, "render_area");
1004
1005 const Id pointer_type{TypePointer(spv::StorageClass::PushConstant, push_constant_struct)};
1006 render_area_push_constant =
1007 AddGlobalVariable(pointer_type, spv::StorageClass::PushConstant);
1008 Name(render_area_push_constant, "render_area_push_constants");
1009
1010 if (profile.supported_spirv >= 0x00010400) {
1011 interfaces.push_back(render_area_push_constant);
1012 }
1013 }
1014}
1015
985void EmitContext::DefineConstantBuffers(const Info& info, u32& binding) { 1016void EmitContext::DefineConstantBuffers(const Info& info, u32& binding) {
986 if (info.constant_buffer_descriptors.empty()) { 1017 if (info.constant_buffer_descriptors.empty()) {
987 return; 1018 return;
@@ -1294,6 +1325,10 @@ void EmitContext::DefineInputs(const IR::Program& program) {
1294 if (info.uses_invocation_id) { 1325 if (info.uses_invocation_id) {
1295 invocation_id = DefineInput(*this, U32[1], false, spv::BuiltIn::InvocationId); 1326 invocation_id = DefineInput(*this, U32[1], false, spv::BuiltIn::InvocationId);
1296 } 1327 }
1328 if (info.uses_invocation_info &&
1329 (stage == Shader::Stage::TessellationControl || stage == Shader::Stage::TessellationEval)) {
1330 patch_vertices_in = DefineInput(*this, U32[1], false, spv::BuiltIn::PatchVertices);
1331 }
1297 if (info.uses_sample_id) { 1332 if (info.uses_sample_id) {
1298 sample_id = DefineInput(*this, U32[1], false, spv::BuiltIn::SampleId); 1333 sample_id = DefineInput(*this, U32[1], false, spv::BuiltIn::SampleId);
1299 } 1334 }
diff --git a/src/shader_recompiler/backend/spirv/spirv_emit_context.h b/src/shader_recompiler/backend/spirv/spirv_emit_context.h
index bc25b8b84..dde45b4bc 100644
--- a/src/shader_recompiler/backend/spirv/spirv_emit_context.h
+++ b/src/shader_recompiler/backend/spirv/spirv_emit_context.h
@@ -204,6 +204,7 @@ public:
204 Id workgroup_id{}; 204 Id workgroup_id{};
205 Id local_invocation_id{}; 205 Id local_invocation_id{};
206 Id invocation_id{}; 206 Id invocation_id{};
207 Id patch_vertices_in{};
207 Id sample_id{}; 208 Id sample_id{};
208 Id is_helper_invocation{}; 209 Id is_helper_invocation{};
209 Id subgroup_local_invocation_id{}; 210 Id subgroup_local_invocation_id{};
@@ -243,6 +244,9 @@ public:
243 u32 texture_rescaling_index{}; 244 u32 texture_rescaling_index{};
244 u32 image_rescaling_index{}; 245 u32 image_rescaling_index{};
245 246
247 Id render_area_push_constant{};
248 u32 render_are_member_index{};
249
246 Id local_memory{}; 250 Id local_memory{};
247 251
248 Id shared_memory_u8{}; 252 Id shared_memory_u8{};
@@ -318,6 +322,7 @@ private:
318 void DefineRescalingInput(const Info& info); 322 void DefineRescalingInput(const Info& info);
319 void DefineRescalingInputPushConstant(); 323 void DefineRescalingInputPushConstant();
320 void DefineRescalingInputUniformConstant(); 324 void DefineRescalingInputUniformConstant();
325 void DefineRenderArea(const Info& info);
321 326
322 void DefineInputs(const IR::Program& program); 327 void DefineInputs(const IR::Program& program);
323 void DefineOutputs(const IR::Program& program); 328 void DefineOutputs(const IR::Program& program);
diff --git a/src/shader_recompiler/environment.h b/src/shader_recompiler/environment.h
index 9729d48c6..402f2664f 100644
--- a/src/shader_recompiler/environment.h
+++ b/src/shader_recompiler/environment.h
@@ -22,6 +22,10 @@ public:
22 22
23 [[nodiscard]] virtual TextureType ReadTextureType(u32 raw_handle) = 0; 23 [[nodiscard]] virtual TextureType ReadTextureType(u32 raw_handle) = 0;
24 24
25 [[nodiscard]] virtual TexturePixelFormat ReadTexturePixelFormat(u32 raw_handle) = 0;
26
27 [[nodiscard]] virtual u32 ReadViewportTransformState() = 0;
28
25 [[nodiscard]] virtual u32 TextureBoundBuffer() const = 0; 29 [[nodiscard]] virtual u32 TextureBoundBuffer() const = 0;
26 30
27 [[nodiscard]] virtual u32 LocalMemorySize() const = 0; 31 [[nodiscard]] virtual u32 LocalMemorySize() const = 0;
diff --git a/src/shader_recompiler/frontend/ir/ir_emitter.cpp b/src/shader_recompiler/frontend/ir/ir_emitter.cpp
index 11086ed8c..0cdac0eff 100644
--- a/src/shader_recompiler/frontend/ir/ir_emitter.cpp
+++ b/src/shader_recompiler/frontend/ir/ir_emitter.cpp
@@ -362,6 +362,10 @@ U32 IREmitter::InvocationId() {
362 return Inst<U32>(Opcode::InvocationId); 362 return Inst<U32>(Opcode::InvocationId);
363} 363}
364 364
365U32 IREmitter::InvocationInfo() {
366 return Inst<U32>(Opcode::InvocationInfo);
367}
368
365U32 IREmitter::SampleId() { 369U32 IREmitter::SampleId() {
366 return Inst<U32>(Opcode::SampleId); 370 return Inst<U32>(Opcode::SampleId);
367} 371}
@@ -378,6 +382,14 @@ F32 IREmitter::ResolutionDownFactor() {
378 return Inst<F32>(Opcode::ResolutionDownFactor); 382 return Inst<F32>(Opcode::ResolutionDownFactor);
379} 383}
380 384
385F32 IREmitter::RenderAreaWidth() {
386 return F32(CompositeExtract(Inst<Value>(Opcode::RenderArea), 0));
387}
388
389F32 IREmitter::RenderAreaHeight() {
390 return F32(CompositeExtract(Inst<Value>(Opcode::RenderArea), 1));
391}
392
381U32 IREmitter::LaneId() { 393U32 IREmitter::LaneId() {
382 return Inst<U32>(Opcode::LaneId); 394 return Inst<U32>(Opcode::LaneId);
383} 395}
@@ -684,6 +696,11 @@ IR::U32 IREmitter::BitCast<IR::U32, IR::F32>(const IR::F32& value) {
684} 696}
685 697
686template <> 698template <>
699IR::S32 IREmitter::BitCast<IR::S32, IR::F32>(const IR::F32& value) {
700 return Inst<IR::S32>(Opcode::BitCastS32F32, value);
701}
702
703template <>
687IR::F32 IREmitter::BitCast<IR::F32, IR::U32>(const IR::U32& value) { 704IR::F32 IREmitter::BitCast<IR::F32, IR::U32>(const IR::U32& value) {
688 return Inst<IR::F32>(Opcode::BitCastF32U32, value); 705 return Inst<IR::F32>(Opcode::BitCastF32U32, value);
689} 706}
diff --git a/src/shader_recompiler/frontend/ir/ir_emitter.h b/src/shader_recompiler/frontend/ir/ir_emitter.h
index 25839a371..2df992feb 100644
--- a/src/shader_recompiler/frontend/ir/ir_emitter.h
+++ b/src/shader_recompiler/frontend/ir/ir_emitter.h
@@ -97,12 +97,16 @@ public:
97 [[nodiscard]] U32 LocalInvocationIdZ(); 97 [[nodiscard]] U32 LocalInvocationIdZ();
98 98
99 [[nodiscard]] U32 InvocationId(); 99 [[nodiscard]] U32 InvocationId();
100 [[nodiscard]] U32 InvocationInfo();
100 [[nodiscard]] U32 SampleId(); 101 [[nodiscard]] U32 SampleId();
101 [[nodiscard]] U1 IsHelperInvocation(); 102 [[nodiscard]] U1 IsHelperInvocation();
102 [[nodiscard]] F32 YDirection(); 103 [[nodiscard]] F32 YDirection();
103 104
104 [[nodiscard]] F32 ResolutionDownFactor(); 105 [[nodiscard]] F32 ResolutionDownFactor();
105 106
107 [[nodiscard]] F32 RenderAreaWidth();
108 [[nodiscard]] F32 RenderAreaHeight();
109
106 [[nodiscard]] U32 LaneId(); 110 [[nodiscard]] U32 LaneId();
107 111
108 [[nodiscard]] U32 LoadGlobalU8(const U64& address); 112 [[nodiscard]] U32 LoadGlobalU8(const U64& address);
diff --git a/src/shader_recompiler/frontend/ir/microinstruction.cpp b/src/shader_recompiler/frontend/ir/microinstruction.cpp
index 468782eb1..84417980b 100644
--- a/src/shader_recompiler/frontend/ir/microinstruction.cpp
+++ b/src/shader_recompiler/frontend/ir/microinstruction.cpp
@@ -325,11 +325,6 @@ void Inst::AddPhiOperand(Block* predecessor, const Value& value) {
325 phi_args.emplace_back(predecessor, value); 325 phi_args.emplace_back(predecessor, value);
326} 326}
327 327
328void Inst::ErasePhiOperand(size_t index) {
329 const auto operand_it{phi_args.begin() + static_cast<ptrdiff_t>(index)};
330 phi_args.erase(operand_it);
331}
332
333void Inst::OrderPhiArgs() { 328void Inst::OrderPhiArgs() {
334 if (op != Opcode::Phi) { 329 if (op != Opcode::Phi) {
335 throw LogicError("{} is not a Phi instruction", op); 330 throw LogicError("{} is not a Phi instruction", op);
diff --git a/src/shader_recompiler/frontend/ir/opcodes.h b/src/shader_recompiler/frontend/ir/opcodes.h
index 752879a18..e70d7745c 100644
--- a/src/shader_recompiler/frontend/ir/opcodes.h
+++ b/src/shader_recompiler/frontend/ir/opcodes.h
@@ -37,6 +37,7 @@ constexpr Type U8{Type::U8};
37constexpr Type U16{Type::U16}; 37constexpr Type U16{Type::U16};
38constexpr Type U32{Type::U32}; 38constexpr Type U32{Type::U32};
39constexpr Type U64{Type::U64}; 39constexpr Type U64{Type::U64};
40constexpr Type S32{Type::S32};
40constexpr Type F16{Type::F16}; 41constexpr Type F16{Type::F16};
41constexpr Type F32{Type::F32}; 42constexpr Type F32{Type::F32};
42constexpr Type F64{Type::F64}; 43constexpr Type F64{Type::F64};
diff --git a/src/shader_recompiler/frontend/ir/opcodes.inc b/src/shader_recompiler/frontend/ir/opcodes.inc
index 86410ddfc..1fe3749cc 100644
--- a/src/shader_recompiler/frontend/ir/opcodes.inc
+++ b/src/shader_recompiler/frontend/ir/opcodes.inc
@@ -59,10 +59,12 @@ OPCODE(SetOFlag, Void, U1,
59OPCODE(WorkgroupId, U32x3, ) 59OPCODE(WorkgroupId, U32x3, )
60OPCODE(LocalInvocationId, U32x3, ) 60OPCODE(LocalInvocationId, U32x3, )
61OPCODE(InvocationId, U32, ) 61OPCODE(InvocationId, U32, )
62OPCODE(InvocationInfo, U32, )
62OPCODE(SampleId, U32, ) 63OPCODE(SampleId, U32, )
63OPCODE(IsHelperInvocation, U1, ) 64OPCODE(IsHelperInvocation, U1, )
64OPCODE(YDirection, F32, ) 65OPCODE(YDirection, F32, )
65OPCODE(ResolutionDownFactor, F32, ) 66OPCODE(ResolutionDownFactor, F32, )
67OPCODE(RenderArea, F32x4, )
66 68
67// Undefined 69// Undefined
68OPCODE(UndefU1, U1, ) 70OPCODE(UndefU1, U1, )
@@ -173,6 +175,7 @@ OPCODE(SelectF64, F64, U1,
173OPCODE(BitCastU16F16, U16, F16, ) 175OPCODE(BitCastU16F16, U16, F16, )
174OPCODE(BitCastU32F32, U32, F32, ) 176OPCODE(BitCastU32F32, U32, F32, )
175OPCODE(BitCastU64F64, U64, F64, ) 177OPCODE(BitCastU64F64, U64, F64, )
178OPCODE(BitCastS32F32, S32, F32, )
176OPCODE(BitCastF16U16, F16, U16, ) 179OPCODE(BitCastF16U16, F16, U16, )
177OPCODE(BitCastF32U32, F32, U32, ) 180OPCODE(BitCastF32U32, F32, U32, )
178OPCODE(BitCastF64U64, F64, U64, ) 181OPCODE(BitCastF64U64, F64, U64, )
diff --git a/src/shader_recompiler/frontend/ir/patch.h b/src/shader_recompiler/frontend/ir/patch.h
index 1e37c8eb6..5077e56c2 100644
--- a/src/shader_recompiler/frontend/ir/patch.h
+++ b/src/shader_recompiler/frontend/ir/patch.h
@@ -14,8 +14,6 @@ enum class Patch : u64 {
14 TessellationLodBottom, 14 TessellationLodBottom,
15 TessellationLodInteriorU, 15 TessellationLodInteriorU,
16 TessellationLodInteriorV, 16 TessellationLodInteriorV,
17 ComponentPadding0,
18 ComponentPadding1,
19 Component0, 17 Component0,
20 Component1, 18 Component1,
21 Component2, 19 Component2,
@@ -137,7 +135,7 @@ enum class Patch : u64 {
137 Component118, 135 Component118,
138 Component119, 136 Component119,
139}; 137};
140static_assert(static_cast<u64>(Patch::Component119) == 127); 138static_assert(static_cast<u64>(Patch::Component119) == 125);
141 139
142[[nodiscard]] bool IsGeneric(Patch patch) noexcept; 140[[nodiscard]] bool IsGeneric(Patch patch) noexcept;
143 141
diff --git a/src/shader_recompiler/frontend/ir/type.h b/src/shader_recompiler/frontend/ir/type.h
index 04c8c4ddb..5a7c706ad 100644
--- a/src/shader_recompiler/frontend/ir/type.h
+++ b/src/shader_recompiler/frontend/ir/type.h
@@ -24,21 +24,22 @@ enum class Type {
24 U16 = 1 << 7, 24 U16 = 1 << 7,
25 U32 = 1 << 8, 25 U32 = 1 << 8,
26 U64 = 1 << 9, 26 U64 = 1 << 9,
27 F16 = 1 << 10, 27 S32 = 1 << 10,
28 F32 = 1 << 11, 28 F16 = 1 << 11,
29 F64 = 1 << 12, 29 F32 = 1 << 12,
30 U32x2 = 1 << 13, 30 F64 = 1 << 13,
31 U32x3 = 1 << 14, 31 U32x2 = 1 << 14,
32 U32x4 = 1 << 15, 32 U32x3 = 1 << 15,
33 F16x2 = 1 << 16, 33 U32x4 = 1 << 16,
34 F16x3 = 1 << 17, 34 F16x2 = 1 << 17,
35 F16x4 = 1 << 18, 35 F16x3 = 1 << 18,
36 F32x2 = 1 << 19, 36 F16x4 = 1 << 19,
37 F32x3 = 1 << 20, 37 F32x2 = 1 << 20,
38 F32x4 = 1 << 21, 38 F32x3 = 1 << 21,
39 F64x2 = 1 << 22, 39 F32x4 = 1 << 22,
40 F64x3 = 1 << 23, 40 F64x2 = 1 << 23,
41 F64x4 = 1 << 24, 41 F64x3 = 1 << 24,
42 F64x4 = 1 << 25,
42}; 43};
43DECLARE_ENUM_FLAG_OPERATORS(Type) 44DECLARE_ENUM_FLAG_OPERATORS(Type)
44 45
diff --git a/src/shader_recompiler/frontend/ir/value.cpp b/src/shader_recompiler/frontend/ir/value.cpp
index 346169328..30ba12316 100644
--- a/src/shader_recompiler/frontend/ir/value.cpp
+++ b/src/shader_recompiler/frontend/ir/value.cpp
@@ -23,6 +23,8 @@ Value::Value(u16 value) noexcept : type{Type::U16}, imm_u16{value} {}
23 23
24Value::Value(u32 value) noexcept : type{Type::U32}, imm_u32{value} {} 24Value::Value(u32 value) noexcept : type{Type::U32}, imm_u32{value} {}
25 25
26Value::Value(s32 value) noexcept : type{Type::S32}, imm_s32{value} {}
27
26Value::Value(f32 value) noexcept : type{Type::F32}, imm_f32{value} {} 28Value::Value(f32 value) noexcept : type{Type::F32}, imm_f32{value} {}
27 29
28Value::Value(u64 value) noexcept : type{Type::U64}, imm_u64{value} {} 30Value::Value(u64 value) noexcept : type{Type::U64}, imm_u64{value} {}
@@ -69,6 +71,7 @@ bool Value::operator==(const Value& other) const {
69 return imm_u16 == other.imm_u16; 71 return imm_u16 == other.imm_u16;
70 case Type::U32: 72 case Type::U32:
71 case Type::F32: 73 case Type::F32:
74 case Type::S32:
72 return imm_u32 == other.imm_u32; 75 return imm_u32 == other.imm_u32;
73 case Type::U64: 76 case Type::U64:
74 case Type::F64: 77 case Type::F64:
diff --git a/src/shader_recompiler/frontend/ir/value.h b/src/shader_recompiler/frontend/ir/value.h
index 1a2e4ccb6..e8bbb93a5 100644
--- a/src/shader_recompiler/frontend/ir/value.h
+++ b/src/shader_recompiler/frontend/ir/value.h
@@ -44,6 +44,7 @@ public:
44 explicit Value(u8 value) noexcept; 44 explicit Value(u8 value) noexcept;
45 explicit Value(u16 value) noexcept; 45 explicit Value(u16 value) noexcept;
46 explicit Value(u32 value) noexcept; 46 explicit Value(u32 value) noexcept;
47 explicit Value(s32 value) noexcept;
47 explicit Value(f32 value) noexcept; 48 explicit Value(f32 value) noexcept;
48 explicit Value(u64 value) noexcept; 49 explicit Value(u64 value) noexcept;
49 explicit Value(f64 value) noexcept; 50 explicit Value(f64 value) noexcept;
@@ -66,6 +67,7 @@ public:
66 [[nodiscard]] u8 U8() const; 67 [[nodiscard]] u8 U8() const;
67 [[nodiscard]] u16 U16() const; 68 [[nodiscard]] u16 U16() const;
68 [[nodiscard]] u32 U32() const; 69 [[nodiscard]] u32 U32() const;
70 [[nodiscard]] s32 S32() const;
69 [[nodiscard]] f32 F32() const; 71 [[nodiscard]] f32 F32() const;
70 [[nodiscard]] u64 U64() const; 72 [[nodiscard]] u64 U64() const;
71 [[nodiscard]] f64 F64() const; 73 [[nodiscard]] f64 F64() const;
@@ -85,6 +87,7 @@ private:
85 u8 imm_u8; 87 u8 imm_u8;
86 u16 imm_u16; 88 u16 imm_u16;
87 u32 imm_u32; 89 u32 imm_u32;
90 s32 imm_s32;
88 f32 imm_f32; 91 f32 imm_f32;
89 u64 imm_u64; 92 u64 imm_u64;
90 f64 imm_f64; 93 f64 imm_f64;
@@ -178,13 +181,9 @@ public:
178 181
179 /// Get a pointer to the block of a phi argument. 182 /// Get a pointer to the block of a phi argument.
180 [[nodiscard]] Block* PhiBlock(size_t index) const; 183 [[nodiscard]] Block* PhiBlock(size_t index) const;
181
182 /// Add phi operand to a phi instruction. 184 /// Add phi operand to a phi instruction.
183 void AddPhiOperand(Block* predecessor, const Value& value); 185 void AddPhiOperand(Block* predecessor, const Value& value);
184 186
185 // Erase the phi operand at the given index.
186 void ErasePhiOperand(size_t index);
187
188 /// Orders the Phi arguments from farthest away to nearest. 187 /// Orders the Phi arguments from farthest away to nearest.
189 void OrderPhiArgs(); 188 void OrderPhiArgs();
190 189
@@ -270,6 +269,7 @@ using U8 = TypedValue<Type::U8>;
270using U16 = TypedValue<Type::U16>; 269using U16 = TypedValue<Type::U16>;
271using U32 = TypedValue<Type::U32>; 270using U32 = TypedValue<Type::U32>;
272using U64 = TypedValue<Type::U64>; 271using U64 = TypedValue<Type::U64>;
272using S32 = TypedValue<Type::S32>;
273using F16 = TypedValue<Type::F16>; 273using F16 = TypedValue<Type::F16>;
274using F32 = TypedValue<Type::F32>; 274using F32 = TypedValue<Type::F32>;
275using F64 = TypedValue<Type::F64>; 275using F64 = TypedValue<Type::F64>;
@@ -381,6 +381,14 @@ inline u32 Value::U32() const {
381 return imm_u32; 381 return imm_u32;
382} 382}
383 383
384inline s32 Value::S32() const {
385 if (IsIdentity()) {
386 return inst->Arg(0).S32();
387 }
388 DEBUG_ASSERT(type == Type::S32);
389 return imm_s32;
390}
391
384inline f32 Value::F32() const { 392inline f32 Value::F32() const {
385 if (IsIdentity()) { 393 if (IsIdentity()) {
386 return inst->Arg(0).F32(); 394 return inst->Arg(0).F32();
diff --git a/src/shader_recompiler/frontend/maxwell/translate/impl/move_special_register.cpp b/src/shader_recompiler/frontend/maxwell/translate/impl/move_special_register.cpp
index 52be12f9c..753c62098 100644
--- a/src/shader_recompiler/frontend/maxwell/translate/impl/move_special_register.cpp
+++ b/src/shader_recompiler/frontend/maxwell/translate/impl/move_special_register.cpp
@@ -117,8 +117,7 @@ enum class SpecialRegister : u64 {
117 case SpecialRegister::SR_THREAD_KILL: 117 case SpecialRegister::SR_THREAD_KILL:
118 return IR::U32{ir.Select(ir.IsHelperInvocation(), ir.Imm32(-1), ir.Imm32(0))}; 118 return IR::U32{ir.Select(ir.IsHelperInvocation(), ir.Imm32(-1), ir.Imm32(0))};
119 case SpecialRegister::SR_INVOCATION_INFO: 119 case SpecialRegister::SR_INVOCATION_INFO:
120 LOG_WARNING(Shader, "(STUBBED) SR_INVOCATION_INFO"); 120 return ir.InvocationInfo();
121 return ir.Imm32(0x00ff'0000);
122 case SpecialRegister::SR_TID: { 121 case SpecialRegister::SR_TID: {
123 const IR::Value tid{ir.LocalInvocationId()}; 122 const IR::Value tid{ir.LocalInvocationId()};
124 return ir.BitFieldInsert(ir.BitFieldInsert(IR::U32{ir.CompositeExtract(tid, 0)}, 123 return ir.BitFieldInsert(ir.BitFieldInsert(IR::U32{ir.CompositeExtract(tid, 0)},
diff --git a/src/shader_recompiler/frontend/maxwell/translate_program.cpp b/src/shader_recompiler/frontend/maxwell/translate_program.cpp
index b58741d4d..376aae0ea 100644
--- a/src/shader_recompiler/frontend/maxwell/translate_program.cpp
+++ b/src/shader_recompiler/frontend/maxwell/translate_program.cpp
@@ -220,8 +220,10 @@ IR::Program TranslateProgram(ObjectPool<IR::Inst>& inst_pool, ObjectPool<IR::Blo
220 220
221 Optimization::ConstantPropagationPass(program); 221 Optimization::ConstantPropagationPass(program);
222 222
223 Optimization::PositionPass(env, program);
224
223 Optimization::GlobalMemoryToStorageBufferPass(program); 225 Optimization::GlobalMemoryToStorageBufferPass(program);
224 Optimization::TexturePass(env, program); 226 Optimization::TexturePass(env, program, host_info);
225 227
226 if (Settings::values.resolution_info.active) { 228 if (Settings::values.resolution_info.active) {
227 Optimization::RescalingPass(program); 229 Optimization::RescalingPass(program);
diff --git a/src/shader_recompiler/host_translate_info.h b/src/shader_recompiler/host_translate_info.h
index 881874310..cc1500690 100644
--- a/src/shader_recompiler/host_translate_info.h
+++ b/src/shader_recompiler/host_translate_info.h
@@ -13,6 +13,7 @@ struct HostTranslateInfo {
13 bool support_float16{}; ///< True when the device supports 16-bit floats 13 bool support_float16{}; ///< True when the device supports 16-bit floats
14 bool support_int64{}; ///< True when the device supports 64-bit integers 14 bool support_int64{}; ///< True when the device supports 64-bit integers
15 bool needs_demote_reorder{}; ///< True when the device needs DemoteToHelperInvocation reordered 15 bool needs_demote_reorder{}; ///< True when the device needs DemoteToHelperInvocation reordered
16 bool support_snorm_render_buffer{}; ///< True when the device supports SNORM render buffers
16}; 17};
17 18
18} // namespace Shader 19} // namespace Shader
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 7cff8ecdc..5a4195217 100644
--- a/src/shader_recompiler/ir_opt/collect_shader_info_pass.cpp
+++ b/src/shader_recompiler/ir_opt/collect_shader_info_pass.cpp
@@ -468,6 +468,9 @@ void VisitUsages(Info& info, IR::Inst& inst) {
468 case IR::Opcode::InvocationId: 468 case IR::Opcode::InvocationId:
469 info.uses_invocation_id = true; 469 info.uses_invocation_id = true;
470 break; 470 break;
471 case IR::Opcode::InvocationInfo:
472 info.uses_invocation_info = true;
473 break;
471 case IR::Opcode::SampleId: 474 case IR::Opcode::SampleId:
472 info.uses_sample_id = true; 475 info.uses_sample_id = true;
473 break; 476 break;
diff --git a/src/shader_recompiler/ir_opt/dead_code_elimination_pass.cpp b/src/shader_recompiler/ir_opt/dead_code_elimination_pass.cpp
index 9a7d47344..1bd8afd6f 100644
--- a/src/shader_recompiler/ir_opt/dead_code_elimination_pass.cpp
+++ b/src/shader_recompiler/ir_opt/dead_code_elimination_pass.cpp
@@ -1,104 +1,24 @@
1// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project 1// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later 2// SPDX-License-Identifier: GPL-2.0-or-later
3 3
4#include <algorithm>
5
6#include <boost/container/small_vector.hpp>
7
8#include "shader_recompiler/frontend/ir/basic_block.h" 4#include "shader_recompiler/frontend/ir/basic_block.h"
9#include "shader_recompiler/frontend/ir/value.h" 5#include "shader_recompiler/frontend/ir/value.h"
10#include "shader_recompiler/ir_opt/passes.h" 6#include "shader_recompiler/ir_opt/passes.h"
11 7
12namespace Shader::Optimization { 8namespace Shader::Optimization {
13namespace { 9
14template <bool TEST_USES> 10void DeadCodeEliminationPass(IR::Program& program) {
15void DeadInstElimination(IR::Block* const block) {
16 // We iterate over the instructions in reverse order. 11 // We iterate over the instructions in reverse order.
17 // This is because removing an instruction reduces the number of uses for earlier instructions. 12 // This is because removing an instruction reduces the number of uses for earlier instructions.
18 auto it{block->end()}; 13 for (IR::Block* const block : program.post_order_blocks) {
19 while (it != block->begin()) { 14 auto it{block->end()};
20 --it; 15 while (it != block->begin()) {
21 if constexpr (TEST_USES) { 16 --it;
22 if (it->HasUses() || it->MayHaveSideEffects()) { 17 if (!it->HasUses() && !it->MayHaveSideEffects()) {
23 continue; 18 it->Invalidate();
24 } 19 it = block->Instructions().erase(it);
25 }
26 it->Invalidate();
27 it = block->Instructions().erase(it);
28 }
29}
30
31void DeletedPhiArgElimination(IR::Program& program, std::span<const IR::Block*> dead_blocks) {
32 for (IR::Block* const block : program.blocks) {
33 for (IR::Inst& phi : *block) {
34 if (!IR::IsPhi(phi)) {
35 continue;
36 }
37 for (size_t i = 0; i < phi.NumArgs(); ++i) {
38 if (std::ranges::find(dead_blocks, phi.PhiBlock(i)) == dead_blocks.end()) {
39 continue;
40 }
41 // Phi operand at this index is an unreachable block
42 phi.ErasePhiOperand(i);
43 --i;
44 }
45 }
46 }
47}
48
49void DeadBranchElimination(IR::Program& program) {
50 boost::container::small_vector<const IR::Block*, 3> dead_blocks;
51 const auto begin_it{program.syntax_list.begin()};
52 for (auto node_it = begin_it; node_it != program.syntax_list.end(); ++node_it) {
53 if (node_it->type != IR::AbstractSyntaxNode::Type::If) {
54 continue;
55 }
56 IR::Inst* const cond_ref{node_it->data.if_node.cond.Inst()};
57 const IR::U1 cond{cond_ref->Arg(0)};
58 if (!cond.IsImmediate()) {
59 continue;
60 }
61 if (cond.U1()) {
62 continue;
63 }
64 // False immediate condition. Remove condition ref, erase the entire branch.
65 cond_ref->Invalidate();
66 // Account for nested if-statements within the if(false) branch
67 u32 nested_ifs{1u};
68 while (node_it->type != IR::AbstractSyntaxNode::Type::EndIf || nested_ifs > 0) {
69 node_it = program.syntax_list.erase(node_it);
70 switch (node_it->type) {
71 case IR::AbstractSyntaxNode::Type::If:
72 ++nested_ifs;
73 break;
74 case IR::AbstractSyntaxNode::Type::EndIf:
75 --nested_ifs;
76 break;
77 case IR::AbstractSyntaxNode::Type::Block: {
78 IR::Block* const block{node_it->data.block};
79 DeadInstElimination<false>(block);
80 dead_blocks.push_back(block);
81 break;
82 }
83 default:
84 break;
85 } 20 }
86 } 21 }
87 // Erase EndIf node of the if(false) branch
88 node_it = program.syntax_list.erase(node_it);
89 // Account for loop increment
90 --node_it;
91 }
92 if (!dead_blocks.empty()) {
93 DeletedPhiArgElimination(program, std::span(dead_blocks.data(), dead_blocks.size()));
94 }
95}
96} // namespace
97
98void DeadCodeEliminationPass(IR::Program& program) {
99 DeadBranchElimination(program);
100 for (IR::Block* const block : program.post_order_blocks) {
101 DeadInstElimination<true>(block);
102 } 22 }
103} 23}
104 24
diff --git a/src/shader_recompiler/ir_opt/passes.h b/src/shader_recompiler/ir_opt/passes.h
index 6ff8e4266..586a0668f 100644
--- a/src/shader_recompiler/ir_opt/passes.h
+++ b/src/shader_recompiler/ir_opt/passes.h
@@ -6,6 +6,10 @@
6#include "shader_recompiler/environment.h" 6#include "shader_recompiler/environment.h"
7#include "shader_recompiler/frontend/ir/program.h" 7#include "shader_recompiler/frontend/ir/program.h"
8 8
9namespace Shader {
10struct HostTranslateInfo;
11}
12
9namespace Shader::Optimization { 13namespace Shader::Optimization {
10 14
11void CollectShaderInfoPass(Environment& env, IR::Program& program); 15void CollectShaderInfoPass(Environment& env, IR::Program& program);
@@ -17,7 +21,8 @@ void LowerFp16ToFp32(IR::Program& program);
17void LowerInt64ToInt32(IR::Program& program); 21void LowerInt64ToInt32(IR::Program& program);
18void RescalingPass(IR::Program& program); 22void RescalingPass(IR::Program& program);
19void SsaRewritePass(IR::Program& program); 23void SsaRewritePass(IR::Program& program);
20void TexturePass(Environment& env, IR::Program& program); 24void PositionPass(Environment& env, IR::Program& program);
25void TexturePass(Environment& env, IR::Program& program, const HostTranslateInfo& host_info);
21void VerificationPass(const IR::Program& program); 26void VerificationPass(const IR::Program& program);
22 27
23// Dual Vertex 28// Dual Vertex
diff --git a/src/shader_recompiler/ir_opt/position_pass.cpp b/src/shader_recompiler/ir_opt/position_pass.cpp
new file mode 100644
index 000000000..3c20b7189
--- /dev/null
+++ b/src/shader_recompiler/ir_opt/position_pass.cpp
@@ -0,0 +1,77 @@
1// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include <boost/container/small_vector.hpp>
5
6#include "shader_recompiler/frontend/ir/basic_block.h"
7#include "shader_recompiler/frontend/ir/ir_emitter.h"
8#include "shader_recompiler/frontend/ir/value.h"
9#include "shader_recompiler/ir_opt/passes.h"
10
11namespace Shader::Optimization {
12
13namespace {
14struct PositionInst {
15 IR::Inst* inst;
16 IR::Block* block;
17 IR::Attribute attr;
18};
19using PositionInstVector = boost::container::small_vector<PositionInst, 24>;
20} // Anonymous namespace
21
22void PositionPass(Environment& env, IR::Program& program) {
23 if (env.ShaderStage() != Stage::VertexB || env.ReadViewportTransformState()) {
24 return;
25 }
26
27 Info& info{program.info};
28 info.uses_render_area = true;
29
30 PositionInstVector to_replace;
31 for (IR::Block* const block : program.post_order_blocks) {
32 for (IR::Inst& inst : block->Instructions()) {
33 switch (inst.GetOpcode()) {
34 case IR::Opcode::SetAttribute: {
35 const IR::Attribute attr{inst.Arg(0).Attribute()};
36 switch (attr) {
37 case IR::Attribute::PositionX:
38 case IR::Attribute::PositionY: {
39 to_replace.push_back(PositionInst{.inst = &inst, .block = block, .attr = attr});
40 break;
41 }
42 default:
43 break;
44 }
45 break;
46 }
47 default:
48 break;
49 }
50 }
51 }
52
53 for (PositionInst& position_inst : to_replace) {
54 IR::IREmitter ir{*position_inst.block,
55 IR::Block::InstructionList::s_iterator_to(*position_inst.inst)};
56 const IR::F32 value(position_inst.inst->Arg(1));
57 const IR::F32F64 scale(ir.Imm32(2.f));
58 const IR::F32 negative_one{ir.Imm32(-1.f)};
59 switch (position_inst.attr) {
60 case IR::Attribute::PositionX: {
61 position_inst.inst->SetArg(
62 1,
63 ir.FPFma(value, ir.FPMul(ir.FPRecip(ir.RenderAreaWidth()), scale), negative_one));
64 break;
65 }
66 case IR::Attribute::PositionY: {
67 position_inst.inst->SetArg(
68 1,
69 ir.FPFma(value, ir.FPMul(ir.FPRecip(ir.RenderAreaHeight()), scale), negative_one));
70 break;
71 }
72 default:
73 break;
74 }
75 }
76}
77} // namespace Shader::Optimization
diff --git a/src/shader_recompiler/ir_opt/texture_pass.cpp b/src/shader_recompiler/ir_opt/texture_pass.cpp
index e8be58357..f5c86fcb1 100644
--- a/src/shader_recompiler/ir_opt/texture_pass.cpp
+++ b/src/shader_recompiler/ir_opt/texture_pass.cpp
@@ -11,6 +11,7 @@
11#include "shader_recompiler/frontend/ir/basic_block.h" 11#include "shader_recompiler/frontend/ir/basic_block.h"
12#include "shader_recompiler/frontend/ir/breadth_first_search.h" 12#include "shader_recompiler/frontend/ir/breadth_first_search.h"
13#include "shader_recompiler/frontend/ir/ir_emitter.h" 13#include "shader_recompiler/frontend/ir/ir_emitter.h"
14#include "shader_recompiler/host_translate_info.h"
14#include "shader_recompiler/ir_opt/passes.h" 15#include "shader_recompiler/ir_opt/passes.h"
15#include "shader_recompiler/shader_info.h" 16#include "shader_recompiler/shader_info.h"
16 17
@@ -363,6 +364,14 @@ TextureType ReadTextureType(Environment& env, const ConstBufferAddr& cbuf) {
363 return env.ReadTextureType(lhs_raw | rhs_raw); 364 return env.ReadTextureType(lhs_raw | rhs_raw);
364} 365}
365 366
367TexturePixelFormat ReadTexturePixelFormat(Environment& env, const ConstBufferAddr& cbuf) {
368 const u32 secondary_index{cbuf.has_secondary ? cbuf.secondary_index : cbuf.index};
369 const u32 secondary_offset{cbuf.has_secondary ? cbuf.secondary_offset : cbuf.offset};
370 const u32 lhs_raw{env.ReadCbufValue(cbuf.index, cbuf.offset)};
371 const u32 rhs_raw{env.ReadCbufValue(secondary_index, secondary_offset)};
372 return env.ReadTexturePixelFormat(lhs_raw | rhs_raw);
373}
374
366class Descriptors { 375class Descriptors {
367public: 376public:
368 explicit Descriptors(TextureBufferDescriptors& texture_buffer_descriptors_, 377 explicit Descriptors(TextureBufferDescriptors& texture_buffer_descriptors_,
@@ -451,9 +460,41 @@ void PatchImageSampleImplicitLod(IR::Block& block, IR::Inst& inst) {
451 ir.FPMul(IR::F32(ir.CompositeExtract(coord, 1)), 460 ir.FPMul(IR::F32(ir.CompositeExtract(coord, 1)),
452 ir.FPRecip(ir.ConvertUToF(32, 32, ir.CompositeExtract(texture_size, 1)))))); 461 ir.FPRecip(ir.ConvertUToF(32, 32, ir.CompositeExtract(texture_size, 1))))));
453} 462}
463
464void PatchTexelFetch(IR::Block& block, IR::Inst& inst, TexturePixelFormat pixel_format) {
465 const auto it{IR::Block::InstructionList::s_iterator_to(inst)};
466 IR::IREmitter ir{block, IR::Block::InstructionList::s_iterator_to(inst)};
467 auto get_max_value = [pixel_format]() -> float {
468 switch (pixel_format) {
469 case TexturePixelFormat::A8B8G8R8_SNORM:
470 case TexturePixelFormat::R8G8_SNORM:
471 case TexturePixelFormat::R8_SNORM:
472 return 1.f / std::numeric_limits<char>::max();
473 case TexturePixelFormat::R16G16B16A16_SNORM:
474 case TexturePixelFormat::R16G16_SNORM:
475 case TexturePixelFormat::R16_SNORM:
476 return 1.f / std::numeric_limits<short>::max();
477 default:
478 throw InvalidArgument("Invalid texture pixel format");
479 }
480 };
481
482 const IR::Value new_inst{&*block.PrependNewInst(it, inst)};
483 const IR::F32 x(ir.CompositeExtract(new_inst, 0));
484 const IR::F32 y(ir.CompositeExtract(new_inst, 1));
485 const IR::F32 z(ir.CompositeExtract(new_inst, 2));
486 const IR::F32 w(ir.CompositeExtract(new_inst, 3));
487 const IR::F16F32F64 max_value(ir.Imm32(get_max_value()));
488 const IR::Value converted =
489 ir.CompositeConstruct(ir.FPMul(ir.ConvertSToF(32, 32, ir.BitCast<IR::S32>(x)), max_value),
490 ir.FPMul(ir.ConvertSToF(32, 32, ir.BitCast<IR::S32>(y)), max_value),
491 ir.FPMul(ir.ConvertSToF(32, 32, ir.BitCast<IR::S32>(z)), max_value),
492 ir.FPMul(ir.ConvertSToF(32, 32, ir.BitCast<IR::S32>(w)), max_value));
493 inst.ReplaceUsesWith(converted);
494}
454} // Anonymous namespace 495} // Anonymous namespace
455 496
456void TexturePass(Environment& env, IR::Program& program) { 497void TexturePass(Environment& env, IR::Program& program, const HostTranslateInfo& host_info) {
457 TextureInstVector to_replace; 498 TextureInstVector to_replace;
458 for (IR::Block* const block : program.post_order_blocks) { 499 for (IR::Block* const block : program.post_order_blocks) {
459 for (IR::Inst& inst : block->Instructions()) { 500 for (IR::Inst& inst : block->Instructions()) {
@@ -597,6 +638,14 @@ void TexturePass(Environment& env, IR::Program& program) {
597 } else { 638 } else {
598 inst->SetArg(0, IR::Value{}); 639 inst->SetArg(0, IR::Value{});
599 } 640 }
641
642 if (!host_info.support_snorm_render_buffer && inst->GetOpcode() == IR::Opcode::ImageFetch &&
643 flags.type == TextureType::Buffer) {
644 const auto pixel_format = ReadTexturePixelFormat(env, cbuf);
645 if (pixel_format != TexturePixelFormat::OTHER) {
646 PatchTexelFetch(*texture_inst.block, *texture_inst.inst, pixel_format);
647 }
648 }
600 } 649 }
601} 650}
602 651
diff --git a/src/shader_recompiler/shader_info.h b/src/shader_recompiler/shader_info.h
index 81097bf1a..ee6252bb5 100644
--- a/src/shader_recompiler/shader_info.h
+++ b/src/shader_recompiler/shader_info.h
@@ -29,6 +29,16 @@ enum class TextureType : u32 {
29}; 29};
30constexpr u32 NUM_TEXTURE_TYPES = 9; 30constexpr u32 NUM_TEXTURE_TYPES = 9;
31 31
32enum class TexturePixelFormat : u32 {
33 A8B8G8R8_SNORM,
34 R8_SNORM,
35 R8G8_SNORM,
36 R16G16B16A16_SNORM,
37 R16G16_SNORM,
38 R16_SNORM,
39 OTHER
40};
41
32enum class ImageFormat : u32 { 42enum class ImageFormat : u32 {
33 Typeless, 43 Typeless,
34 R8_UINT, 44 R8_UINT,
@@ -117,6 +127,7 @@ struct Info {
117 bool uses_workgroup_id{}; 127 bool uses_workgroup_id{};
118 bool uses_local_invocation_id{}; 128 bool uses_local_invocation_id{};
119 bool uses_invocation_id{}; 129 bool uses_invocation_id{};
130 bool uses_invocation_info{};
120 bool uses_sample_id{}; 131 bool uses_sample_id{};
121 bool uses_is_helper_invocation{}; 132 bool uses_is_helper_invocation{};
122 bool uses_subgroup_invocation_id{}; 133 bool uses_subgroup_invocation_id{};
@@ -182,6 +193,7 @@ struct Info {
182 bool uses_shadow_lod{}; 193 bool uses_shadow_lod{};
183 bool uses_rescaling_uniform{}; 194 bool uses_rescaling_uniform{};
184 bool uses_cbuf_indirect{}; 195 bool uses_cbuf_indirect{};
196 bool uses_render_area{};
185 197
186 IR::Type used_constant_buffer_types{}; 198 IR::Type used_constant_buffer_types{};
187 IR::Type used_storage_buffer_types{}; 199 IR::Type used_storage_buffer_types{};
diff --git a/src/tests/video_core/buffer_base.cpp b/src/tests/video_core/buffer_base.cpp
index 71121e42a..f7236afab 100644
--- a/src/tests/video_core/buffer_base.cpp
+++ b/src/tests/video_core/buffer_base.cpp
@@ -44,7 +44,7 @@ public:
44 44
45 [[nodiscard]] unsigned Count() const noexcept { 45 [[nodiscard]] unsigned Count() const noexcept {
46 unsigned count = 0; 46 unsigned count = 0;
47 for (const auto [index, value] : page_table) { 47 for (const auto& [index, value] : page_table) {
48 count += value; 48 count += value;
49 } 49 }
50 return count; 50 return count;
diff --git a/src/video_core/CMakeLists.txt b/src/video_core/CMakeLists.txt
index 106991969..d7f7d336c 100644
--- a/src/video_core/CMakeLists.txt
+++ b/src/video_core/CMakeLists.txt
@@ -73,8 +73,6 @@ add_library(video_core STATIC
73 macro/macro_hle.h 73 macro/macro_hle.h
74 macro/macro_interpreter.cpp 74 macro/macro_interpreter.cpp
75 macro/macro_interpreter.h 75 macro/macro_interpreter.h
76 macro/macro_jit_x64.cpp
77 macro/macro_jit_x64.h
78 fence_manager.h 76 fence_manager.h
79 gpu.cpp 77 gpu.cpp
80 gpu.h 78 gpu.h
@@ -245,7 +243,7 @@ add_library(video_core STATIC
245create_target_directory_groups(video_core) 243create_target_directory_groups(video_core)
246 244
247target_link_libraries(video_core PUBLIC common core) 245target_link_libraries(video_core PUBLIC common core)
248target_link_libraries(video_core PUBLIC glad shader_recompiler xbyak) 246target_link_libraries(video_core PUBLIC glad shader_recompiler)
249 247
250if (YUZU_USE_BUNDLED_FFMPEG AND NOT WIN32) 248if (YUZU_USE_BUNDLED_FFMPEG AND NOT WIN32)
251 add_dependencies(video_core ffmpeg-build) 249 add_dependencies(video_core ffmpeg-build)
@@ -282,8 +280,19 @@ else()
282 280
283 -Wno-sign-conversion 281 -Wno-sign-conversion
284 ) 282 )
283
284 # xbyak
285 set_source_files_properties(macro/macro_jit_x64.cpp PROPERTIES COMPILE_OPTIONS "-Wno-conversion;-Wno-shadow")
285endif() 286endif()
286 287
287if (ARCHITECTURE_x86_64) 288if (ARCHITECTURE_x86_64)
289 target_sources(video_core PRIVATE
290 macro/macro_jit_x64.cpp
291 macro/macro_jit_x64.h
292 )
293 target_link_libraries(video_core PUBLIC xbyak)
294endif()
295
296if (ARCHITECTURE_x86_64 OR ARCHITECTURE_arm64)
288 target_link_libraries(video_core PRIVATE dynarmic) 297 target_link_libraries(video_core PRIVATE dynarmic)
289endif() 298endif()
diff --git a/src/video_core/engines/maxwell_3d.cpp b/src/video_core/engines/maxwell_3d.cpp
index b1a22b76c..4a2f2c1fd 100644
--- a/src/video_core/engines/maxwell_3d.cpp
+++ b/src/video_core/engines/maxwell_3d.cpp
@@ -117,10 +117,15 @@ void Maxwell3D::InitializeRegisterDefaults() {
117 117
118 shadow_state = regs; 118 shadow_state = regs;
119 119
120 mme_inline[MAXWELL3D_REG_INDEX(draw.end)] = true; 120 draw_command[MAXWELL3D_REG_INDEX(draw.end)] = true;
121 mme_inline[MAXWELL3D_REG_INDEX(draw.begin)] = true; 121 draw_command[MAXWELL3D_REG_INDEX(draw.begin)] = true;
122 mme_inline[MAXWELL3D_REG_INDEX(vertex_buffer.count)] = true; 122 draw_command[MAXWELL3D_REG_INDEX(vertex_buffer.first)] = true;
123 mme_inline[MAXWELL3D_REG_INDEX(index_buffer.count)] = true; 123 draw_command[MAXWELL3D_REG_INDEX(vertex_buffer.count)] = true;
124 draw_command[MAXWELL3D_REG_INDEX(index_buffer.first)] = true;
125 draw_command[MAXWELL3D_REG_INDEX(index_buffer.count)] = true;
126 draw_command[MAXWELL3D_REG_INDEX(draw_inline_index)] = true;
127 draw_command[MAXWELL3D_REG_INDEX(inline_index_2x16.even)] = true;
128 draw_command[MAXWELL3D_REG_INDEX(inline_index_4x8.index0)] = true;
124} 129}
125 130
126void Maxwell3D::ProcessMacro(u32 method, const u32* base_start, u32 amount, bool is_last_call) { 131void Maxwell3D::ProcessMacro(u32 method, const u32* base_start, u32 amount, bool is_last_call) {
@@ -208,25 +213,21 @@ void Maxwell3D::ProcessMethodCall(u32 method, u32 argument, u32 nonshadow_argume
208 return ProcessCBBind(3); 213 return ProcessCBBind(3);
209 case MAXWELL3D_REG_INDEX(bind_groups[4].raw_config): 214 case MAXWELL3D_REG_INDEX(bind_groups[4].raw_config):
210 return ProcessCBBind(4); 215 return ProcessCBBind(4);
211 case MAXWELL3D_REG_INDEX(draw.end):
212 return DrawArrays();
213 case MAXWELL3D_REG_INDEX(index_buffer32_first): 216 case MAXWELL3D_REG_INDEX(index_buffer32_first):
214 regs.index_buffer.count = regs.index_buffer32_first.count; 217 regs.index_buffer.count = regs.index_buffer32_first.count;
215 regs.index_buffer.first = regs.index_buffer32_first.first; 218 regs.index_buffer.first = regs.index_buffer32_first.first;
216 dirty.flags[VideoCommon::Dirty::IndexBuffer] = true; 219 dirty.flags[VideoCommon::Dirty::IndexBuffer] = true;
217 return DrawArrays(); 220 return ProcessDraw();
218 case MAXWELL3D_REG_INDEX(index_buffer16_first): 221 case MAXWELL3D_REG_INDEX(index_buffer16_first):
219 regs.index_buffer.count = regs.index_buffer16_first.count; 222 regs.index_buffer.count = regs.index_buffer16_first.count;
220 regs.index_buffer.first = regs.index_buffer16_first.first; 223 regs.index_buffer.first = regs.index_buffer16_first.first;
221 dirty.flags[VideoCommon::Dirty::IndexBuffer] = true; 224 dirty.flags[VideoCommon::Dirty::IndexBuffer] = true;
222 return DrawArrays(); 225 return ProcessDraw();
223 case MAXWELL3D_REG_INDEX(index_buffer8_first): 226 case MAXWELL3D_REG_INDEX(index_buffer8_first):
224 regs.index_buffer.count = regs.index_buffer8_first.count; 227 regs.index_buffer.count = regs.index_buffer8_first.count;
225 regs.index_buffer.first = regs.index_buffer8_first.first; 228 regs.index_buffer.first = regs.index_buffer8_first.first;
226 dirty.flags[VideoCommon::Dirty::IndexBuffer] = true; 229 dirty.flags[VideoCommon::Dirty::IndexBuffer] = true;
227 // a macro calls this one over and over, should it increase instancing? 230 return ProcessDraw();
228 // Used by Hades and likely other Vulkan games.
229 return DrawArrays();
230 case MAXWELL3D_REG_INDEX(topology_override): 231 case MAXWELL3D_REG_INDEX(topology_override):
231 use_topology_override = true; 232 use_topology_override = true;
232 return; 233 return;
@@ -261,14 +262,13 @@ void Maxwell3D::CallMacroMethod(u32 method, const std::vector<u32>& parameters)
261 262
262 // Execute the current macro. 263 // Execute the current macro.
263 macro_engine->Execute(macro_positions[entry], parameters); 264 macro_engine->Execute(macro_positions[entry], parameters);
264 if (mme_draw.current_mode != MMEDrawMode::Undefined) { 265
265 FlushMMEInlineDraw(); 266 ProcessDeferredDraw();
266 }
267} 267}
268 268
269void Maxwell3D::CallMethod(u32 method, u32 method_argument, bool is_last_call) { 269void Maxwell3D::CallMethod(u32 method, u32 method_argument, bool is_last_call) {
270 // It is an error to write to a register other than the current macro's ARG register before it 270 // It is an error to write to a register other than the current macro's ARG register before
271 // has finished execution. 271 // it has finished execution.
272 if (executing_macro != 0) { 272 if (executing_macro != 0) {
273 ASSERT(method == executing_macro + 1); 273 ASSERT(method == executing_macro + 1);
274 } 274 }
@@ -283,9 +283,33 @@ void Maxwell3D::CallMethod(u32 method, u32 method_argument, bool is_last_call) {
283 ASSERT_MSG(method < Regs::NUM_REGS, 283 ASSERT_MSG(method < Regs::NUM_REGS,
284 "Invalid Maxwell3D register, increase the size of the Regs structure"); 284 "Invalid Maxwell3D register, increase the size of the Regs structure");
285 285
286 const u32 argument = ProcessShadowRam(method, method_argument); 286 if (draw_command[method]) {
287 ProcessDirtyRegisters(method, argument); 287 regs.reg_array[method] = method_argument;
288 ProcessMethodCall(method, argument, method_argument, is_last_call); 288 deferred_draw_method.push_back(method);
289 auto u32_to_u8 = [&](const u32 argument) {
290 inline_index_draw_indexes.push_back(static_cast<u8>(argument & 0x000000ff));
291 inline_index_draw_indexes.push_back(static_cast<u8>((argument & 0x0000ff00) >> 8));
292 inline_index_draw_indexes.push_back(static_cast<u8>((argument & 0x00ff0000) >> 16));
293 inline_index_draw_indexes.push_back(static_cast<u8>((argument & 0xff000000) >> 24));
294 };
295 if (MAXWELL3D_REG_INDEX(draw_inline_index) == method) {
296 u32_to_u8(method_argument);
297 } else if (MAXWELL3D_REG_INDEX(inline_index_2x16.even) == method) {
298 u32_to_u8(regs.inline_index_2x16.even);
299 u32_to_u8(regs.inline_index_2x16.odd);
300 } else if (MAXWELL3D_REG_INDEX(inline_index_4x8.index0) == method) {
301 u32_to_u8(regs.inline_index_4x8.index0);
302 u32_to_u8(regs.inline_index_4x8.index1);
303 u32_to_u8(regs.inline_index_4x8.index2);
304 u32_to_u8(regs.inline_index_4x8.index3);
305 }
306 } else {
307 ProcessDeferredDraw();
308
309 const u32 argument = ProcessShadowRam(method, method_argument);
310 ProcessDirtyRegisters(method, argument);
311 ProcessMethodCall(method, argument, method_argument, is_last_call);
312 }
289} 313}
290 314
291void Maxwell3D::CallMultiMethod(u32 method, const u32* base_start, u32 amount, 315void Maxwell3D::CallMultiMethod(u32 method, const u32* base_start, u32 amount,
@@ -326,55 +350,6 @@ void Maxwell3D::CallMultiMethod(u32 method, const u32* base_start, u32 amount,
326 } 350 }
327} 351}
328 352
329void Maxwell3D::StepInstance(const MMEDrawMode expected_mode, const u32 count) {
330 if (mme_draw.current_mode == MMEDrawMode::Undefined) {
331 if (mme_draw.gl_begin_consume) {
332 mme_draw.current_mode = expected_mode;
333 mme_draw.current_count = count;
334 mme_draw.instance_count = 1;
335 mme_draw.gl_begin_consume = false;
336 mme_draw.gl_end_count = 0;
337 }
338 return;
339 } else {
340 if (mme_draw.current_mode == expected_mode && count == mme_draw.current_count &&
341 mme_draw.instance_mode && mme_draw.gl_begin_consume) {
342 mme_draw.instance_count++;
343 mme_draw.gl_begin_consume = false;
344 return;
345 } else {
346 FlushMMEInlineDraw();
347 }
348 }
349 // Tail call in case it needs to retry.
350 StepInstance(expected_mode, count);
351}
352
353void Maxwell3D::CallMethodFromMME(u32 method, u32 method_argument) {
354 if (mme_inline[method]) {
355 regs.reg_array[method] = method_argument;
356 if (method == MAXWELL3D_REG_INDEX(vertex_buffer.count) ||
357 method == MAXWELL3D_REG_INDEX(index_buffer.count)) {
358 const MMEDrawMode expected_mode = method == MAXWELL3D_REG_INDEX(vertex_buffer.count)
359 ? MMEDrawMode::Array
360 : MMEDrawMode::Indexed;
361 StepInstance(expected_mode, method_argument);
362 } else if (method == MAXWELL3D_REG_INDEX(draw.begin)) {
363 mme_draw.instance_mode =
364 (regs.draw.instance_id == Maxwell3D::Regs::Draw::InstanceId::Subsequent) ||
365 (regs.draw.instance_id == Maxwell3D::Regs::Draw::InstanceId::Unchanged);
366 mme_draw.gl_begin_consume = true;
367 } else {
368 mme_draw.gl_end_count++;
369 }
370 } else {
371 if (mme_draw.current_mode != MMEDrawMode::Undefined) {
372 FlushMMEInlineDraw();
373 }
374 CallMethod(method, method_argument, true);
375 }
376}
377
378void Maxwell3D::ProcessTopologyOverride() { 353void Maxwell3D::ProcessTopologyOverride() {
379 using PrimitiveTopology = Maxwell3D::Regs::PrimitiveTopology; 354 using PrimitiveTopology = Maxwell3D::Regs::PrimitiveTopology;
380 using PrimitiveTopologyOverride = Maxwell3D::Regs::PrimitiveTopologyOverride; 355 using PrimitiveTopologyOverride = Maxwell3D::Regs::PrimitiveTopologyOverride;
@@ -404,41 +379,6 @@ void Maxwell3D::ProcessTopologyOverride() {
404 } 379 }
405} 380}
406 381
407void Maxwell3D::FlushMMEInlineDraw() {
408 LOG_TRACE(HW_GPU, "called, topology={}, count={}", regs.draw.topology.Value(),
409 regs.vertex_buffer.count);
410 ASSERT_MSG(!(regs.index_buffer.count && regs.vertex_buffer.count), "Both indexed and direct?");
411 ASSERT(mme_draw.instance_count == mme_draw.gl_end_count);
412
413 // Both instance configuration registers can not be set at the same time.
414 ASSERT_MSG(regs.draw.instance_id == Maxwell3D::Regs::Draw::InstanceId::First ||
415 regs.draw.instance_id != Maxwell3D::Regs::Draw::InstanceId::Unchanged,
416 "Illegal combination of instancing parameters");
417
418 ProcessTopologyOverride();
419
420 const bool is_indexed = mme_draw.current_mode == MMEDrawMode::Indexed;
421 if (ShouldExecute()) {
422 rasterizer->Draw(is_indexed, true);
423 }
424
425 // TODO(bunnei): Below, we reset vertex count so that we can use these registers to determine if
426 // the game is trying to draw indexed or direct mode. This needs to be verified on HW still -
427 // it's possible that it is incorrect and that there is some other register used to specify the
428 // drawing mode.
429 if (is_indexed) {
430 regs.index_buffer.count = 0;
431 } else {
432 regs.vertex_buffer.count = 0;
433 }
434 mme_draw.current_mode = MMEDrawMode::Undefined;
435 mme_draw.current_count = 0;
436 mme_draw.instance_count = 0;
437 mme_draw.instance_mode = false;
438 mme_draw.gl_begin_consume = false;
439 mme_draw.gl_end_count = 0;
440}
441
442void Maxwell3D::ProcessMacroUpload(u32 data) { 382void Maxwell3D::ProcessMacroUpload(u32 data) {
443 macro_engine->AddCode(regs.load_mme.instruction_ptr++, data); 383 macro_engine->AddCode(regs.load_mme.instruction_ptr++, data);
444} 384}
@@ -573,42 +513,6 @@ void Maxwell3D::ProcessSyncPoint() {
573 rasterizer->SignalSyncPoint(sync_point); 513 rasterizer->SignalSyncPoint(sync_point);
574} 514}
575 515
576void Maxwell3D::DrawArrays() {
577 LOG_TRACE(HW_GPU, "called, topology={}, count={}", regs.draw.topology.Value(),
578 regs.vertex_buffer.count);
579 ASSERT_MSG(!(regs.index_buffer.count && regs.vertex_buffer.count), "Both indexed and direct?");
580
581 // Both instance configuration registers can not be set at the same time.
582 ASSERT_MSG(regs.draw.instance_id == Maxwell3D::Regs::Draw::InstanceId::First ||
583 regs.draw.instance_id != Maxwell3D::Regs::Draw::InstanceId::Unchanged,
584 "Illegal combination of instancing parameters");
585
586 ProcessTopologyOverride();
587
588 if (regs.draw.instance_id == Maxwell3D::Regs::Draw::InstanceId::Subsequent) {
589 // Increment the current instance *before* drawing.
590 state.current_instance++;
591 } else if (regs.draw.instance_id != Maxwell3D::Regs::Draw::InstanceId::Unchanged) {
592 // Reset the current instance to 0.
593 state.current_instance = 0;
594 }
595
596 const bool is_indexed{regs.index_buffer.count && !regs.vertex_buffer.count};
597 if (ShouldExecute()) {
598 rasterizer->Draw(is_indexed, false);
599 }
600
601 // TODO(bunnei): Below, we reset vertex count so that we can use these registers to determine if
602 // the game is trying to draw indexed or direct mode. This needs to be verified on HW still -
603 // it's possible that it is incorrect and that there is some other register used to specify the
604 // drawing mode.
605 if (is_indexed) {
606 regs.index_buffer.count = 0;
607 } else {
608 regs.vertex_buffer.count = 0;
609 }
610}
611
612std::optional<u64> Maxwell3D::GetQueryResult() { 516std::optional<u64> Maxwell3D::GetQueryResult() {
613 switch (regs.report_semaphore.query.report) { 517 switch (regs.report_semaphore.query.report) {
614 case Regs::ReportSemaphore::Report::Payload: 518 case Regs::ReportSemaphore::Report::Payload:
@@ -691,4 +595,83 @@ void Maxwell3D::ProcessClearBuffers() {
691 rasterizer->Clear(); 595 rasterizer->Clear();
692} 596}
693 597
598void Maxwell3D::ProcessDraw(u32 instance_count) {
599 LOG_TRACE(HW_GPU, "called, topology={}, count={}", regs.draw.topology.Value(),
600 regs.vertex_buffer.count);
601
602 ASSERT_MSG(!(regs.index_buffer.count && regs.vertex_buffer.count), "Both indexed and direct?");
603
604 // Both instance configuration registers can not be set at the same time.
605 ASSERT_MSG(regs.draw.instance_id == Maxwell3D::Regs::Draw::InstanceId::First ||
606 regs.draw.instance_id != Maxwell3D::Regs::Draw::InstanceId::Unchanged,
607 "Illegal combination of instancing parameters");
608
609 ProcessTopologyOverride();
610
611 const bool is_indexed = regs.index_buffer.count && !regs.vertex_buffer.count;
612 if (ShouldExecute()) {
613 rasterizer->Draw(is_indexed, instance_count);
614 }
615
616 if (is_indexed) {
617 regs.index_buffer.count = 0;
618 } else {
619 regs.vertex_buffer.count = 0;
620 }
621}
622
623void Maxwell3D::ProcessDeferredDraw() {
624 if (deferred_draw_method.empty()) {
625 return;
626 }
627
628 enum class DrawMode {
629 Undefined,
630 General,
631 Instance,
632 };
633 DrawMode draw_mode{DrawMode::Undefined};
634 u32 method_count = static_cast<u32>(deferred_draw_method.size());
635 u32 method = deferred_draw_method[method_count - 1];
636 if (MAXWELL3D_REG_INDEX(draw.end) != method) {
637 return;
638 }
639 draw_mode = (regs.draw.instance_id == Maxwell3D::Regs::Draw::InstanceId::Subsequent) ||
640 (regs.draw.instance_id == Maxwell3D::Regs::Draw::InstanceId::Unchanged)
641 ? DrawMode::Instance
642 : DrawMode::General;
643 u32 instance_count = 0;
644 if (draw_mode == DrawMode::Instance) {
645 u32 vertex_buffer_count = 0;
646 u32 index_buffer_count = 0;
647 for (u32 index = 0; index < method_count; ++index) {
648 method = deferred_draw_method[index];
649 if (method == MAXWELL3D_REG_INDEX(vertex_buffer.count)) {
650 instance_count = ++vertex_buffer_count;
651 } else if (method == MAXWELL3D_REG_INDEX(index_buffer.count)) {
652 instance_count = ++index_buffer_count;
653 }
654 }
655 ASSERT_MSG(!(vertex_buffer_count && index_buffer_count),
656 "Instance both indexed and direct?");
657 } else {
658 instance_count = 1;
659 for (u32 index = 0; index < method_count; ++index) {
660 method = deferred_draw_method[index];
661 if (MAXWELL3D_REG_INDEX(draw_inline_index) == method ||
662 MAXWELL3D_REG_INDEX(inline_index_2x16.even) == method ||
663 MAXWELL3D_REG_INDEX(inline_index_4x8.index0) == method) {
664 regs.index_buffer.count = static_cast<u32>(inline_index_draw_indexes.size() / 4);
665 regs.index_buffer.format = Regs::IndexFormat::UnsignedInt;
666 break;
667 }
668 }
669 }
670
671 ProcessDraw(instance_count);
672
673 deferred_draw_method.clear();
674 inline_index_draw_indexes.clear();
675}
676
694} // namespace Tegra::Engines 677} // namespace Tegra::Engines
diff --git a/src/video_core/engines/maxwell_3d.h b/src/video_core/engines/maxwell_3d.h
index 75e3b868d..910ab213a 100644
--- a/src/video_core/engines/maxwell_3d.h
+++ b/src/video_core/engines/maxwell_3d.h
@@ -1739,14 +1739,11 @@ public:
1739 Footprint_1x1_Virtual = 2, 1739 Footprint_1x1_Virtual = 2,
1740 }; 1740 };
1741 1741
1742 struct InlineIndex4x8Align { 1742 struct InlineIndex4x8 {
1743 union { 1743 union {
1744 BitField<0, 30, u32> count; 1744 BitField<0, 30, u32> count;
1745 BitField<30, 2, u32> start; 1745 BitField<30, 2, u32> start;
1746 }; 1746 };
1747 };
1748
1749 struct InlineIndex4x8Index {
1750 union { 1747 union {
1751 BitField<0, 8, u32> index0; 1748 BitField<0, 8, u32> index0;
1752 BitField<8, 8, u32> index1; 1749 BitField<8, 8, u32> index1;
@@ -2836,8 +2833,7 @@ public:
2836 u32 depth_write_enabled; ///< 0x12E8 2833 u32 depth_write_enabled; ///< 0x12E8
2837 u32 alpha_test_enabled; ///< 0x12EC 2834 u32 alpha_test_enabled; ///< 0x12EC
2838 INSERT_PADDING_BYTES_NOINIT(0x10); 2835 INSERT_PADDING_BYTES_NOINIT(0x10);
2839 InlineIndex4x8Align inline_index_4x8_align; ///< 0x1300 2836 InlineIndex4x8 inline_index_4x8; ///< 0x1300
2840 InlineIndex4x8Index inline_index_4x8_index; ///< 0x1304
2841 D3DCullMode d3d_cull_mode; ///< 0x1308 2837 D3DCullMode d3d_cull_mode; ///< 0x1308
2842 ComparisonOp depth_test_func; ///< 0x130C 2838 ComparisonOp depth_test_func; ///< 0x130C
2843 f32 alpha_test_ref; ///< 0x1310 2839 f32 alpha_test_ref; ///< 0x1310
@@ -2974,7 +2970,7 @@ public:
2974 CullFace gl_cull_face; ///< 0x1920 2970 CullFace gl_cull_face; ///< 0x1920
2975 Viewport::PixelCenter viewport_pixel_center; ///< 0x1924 2971 Viewport::PixelCenter viewport_pixel_center; ///< 0x1924
2976 INSERT_PADDING_BYTES_NOINIT(0x4); 2972 INSERT_PADDING_BYTES_NOINIT(0x4);
2977 u32 viewport_scale_offset_enbled; ///< 0x192C 2973 u32 viewport_scale_offset_enabled; ///< 0x192C
2978 INSERT_PADDING_BYTES_NOINIT(0xC); 2974 INSERT_PADDING_BYTES_NOINIT(0xC);
2979 ViewportClipControl viewport_clip_control; ///< 0x193C 2975 ViewportClipControl viewport_clip_control; ///< 0x193C
2980 UserClip::Op user_clip_op; ///< 0x1940 2976 UserClip::Op user_clip_op; ///< 0x1940
@@ -3048,8 +3044,6 @@ public:
3048 }; 3044 };
3049 3045
3050 std::array<ShaderStageInfo, Regs::MaxShaderStage> shader_stages; 3046 std::array<ShaderStageInfo, Regs::MaxShaderStage> shader_stages;
3051
3052 u32 current_instance = 0; ///< Current instance to be used to simulate instanced rendering.
3053 }; 3047 };
3054 3048
3055 State state{}; 3049 State state{};
@@ -3064,11 +3058,6 @@ public:
3064 void CallMultiMethod(u32 method, const u32* base_start, u32 amount, 3058 void CallMultiMethod(u32 method, const u32* base_start, u32 amount,
3065 u32 methods_pending) override; 3059 u32 methods_pending) override;
3066 3060
3067 /// Write the value to the register identified by method.
3068 void CallMethodFromMME(u32 method, u32 method_argument);
3069
3070 void FlushMMEInlineDraw();
3071
3072 bool ShouldExecute() const { 3061 bool ShouldExecute() const {
3073 return execute_on; 3062 return execute_on;
3074 } 3063 }
@@ -3081,21 +3070,6 @@ public:
3081 return *rasterizer; 3070 return *rasterizer;
3082 } 3071 }
3083 3072
3084 enum class MMEDrawMode : u32 {
3085 Undefined,
3086 Array,
3087 Indexed,
3088 };
3089
3090 struct MMEDrawState {
3091 MMEDrawMode current_mode{MMEDrawMode::Undefined};
3092 u32 current_count{};
3093 u32 instance_count{};
3094 bool instance_mode{};
3095 bool gl_begin_consume{};
3096 u32 gl_end_count{};
3097 } mme_draw;
3098
3099 struct DirtyState { 3073 struct DirtyState {
3100 using Flags = std::bitset<std::numeric_limits<u8>::max()>; 3074 using Flags = std::bitset<std::numeric_limits<u8>::max()>;
3101 using Table = std::array<u8, Regs::NUM_REGS>; 3075 using Table = std::array<u8, Regs::NUM_REGS>;
@@ -3105,6 +3079,8 @@ public:
3105 Tables tables{}; 3079 Tables tables{};
3106 } dirty; 3080 } dirty;
3107 3081
3082 std::vector<u8> inline_index_draw_indexes;
3083
3108private: 3084private:
3109 void InitializeRegisterDefaults(); 3085 void InitializeRegisterDefaults();
3110 3086
@@ -3164,14 +3140,12 @@ private:
3164 /// Handles a write to the CB_BIND register. 3140 /// Handles a write to the CB_BIND register.
3165 void ProcessCBBind(size_t stage_index); 3141 void ProcessCBBind(size_t stage_index);
3166 3142
3167 /// Handles a write to the VERTEX_END_GL register, triggering a draw.
3168 void DrawArrays();
3169
3170 /// Handles use of topology overrides (e.g., to avoid using a topology assigned from a macro) 3143 /// Handles use of topology overrides (e.g., to avoid using a topology assigned from a macro)
3171 void ProcessTopologyOverride(); 3144 void ProcessTopologyOverride();
3172 3145
3173 // Handles a instance drawcall from MME 3146 void ProcessDraw(u32 instance_count = 1);
3174 void StepInstance(MMEDrawMode expected_mode, u32 count); 3147
3148 void ProcessDeferredDraw();
3175 3149
3176 /// Returns a query's value or an empty object if the value will be deferred through a cache. 3150 /// Returns a query's value or an empty object if the value will be deferred through a cache.
3177 std::optional<u64> GetQueryResult(); 3151 std::optional<u64> GetQueryResult();
@@ -3184,8 +3158,6 @@ private:
3184 /// Start offsets of each macro in macro_memory 3158 /// Start offsets of each macro in macro_memory
3185 std::array<u32, 0x80> macro_positions{}; 3159 std::array<u32, 0x80> macro_positions{};
3186 3160
3187 std::array<bool, Regs::NUM_REGS> mme_inline{};
3188
3189 /// Macro method that is currently being executed / being fed parameters. 3161 /// Macro method that is currently being executed / being fed parameters.
3190 u32 executing_macro = 0; 3162 u32 executing_macro = 0;
3191 /// Parameters that have been submitted to the macro call so far. 3163 /// Parameters that have been submitted to the macro call so far.
@@ -3198,6 +3170,9 @@ private:
3198 3170
3199 bool execute_on{true}; 3171 bool execute_on{true};
3200 bool use_topology_override{false}; 3172 bool use_topology_override{false};
3173
3174 std::array<bool, Regs::NUM_REGS> draw_command{};
3175 std::vector<u32> deferred_draw_method;
3201}; 3176};
3202 3177
3203#define ASSERT_REG_POSITION(field_name, position) \ 3178#define ASSERT_REG_POSITION(field_name, position) \
@@ -3402,8 +3377,7 @@ ASSERT_REG_POSITION(alpha_to_coverage_dither, 0x12E0);
3402ASSERT_REG_POSITION(blend_per_target_enabled, 0x12E4); 3377ASSERT_REG_POSITION(blend_per_target_enabled, 0x12E4);
3403ASSERT_REG_POSITION(depth_write_enabled, 0x12E8); 3378ASSERT_REG_POSITION(depth_write_enabled, 0x12E8);
3404ASSERT_REG_POSITION(alpha_test_enabled, 0x12EC); 3379ASSERT_REG_POSITION(alpha_test_enabled, 0x12EC);
3405ASSERT_REG_POSITION(inline_index_4x8_align, 0x1300); 3380ASSERT_REG_POSITION(inline_index_4x8, 0x1300);
3406ASSERT_REG_POSITION(inline_index_4x8_index, 0x1304);
3407ASSERT_REG_POSITION(d3d_cull_mode, 0x1308); 3381ASSERT_REG_POSITION(d3d_cull_mode, 0x1308);
3408ASSERT_REG_POSITION(depth_test_func, 0x130C); 3382ASSERT_REG_POSITION(depth_test_func, 0x130C);
3409ASSERT_REG_POSITION(alpha_test_ref, 0x1310); 3383ASSERT_REG_POSITION(alpha_test_ref, 0x1310);
@@ -3508,7 +3482,7 @@ ASSERT_REG_POSITION(gl_cull_test_enabled, 0x1918);
3508ASSERT_REG_POSITION(gl_front_face, 0x191C); 3482ASSERT_REG_POSITION(gl_front_face, 0x191C);
3509ASSERT_REG_POSITION(gl_cull_face, 0x1920); 3483ASSERT_REG_POSITION(gl_cull_face, 0x1920);
3510ASSERT_REG_POSITION(viewport_pixel_center, 0x1924); 3484ASSERT_REG_POSITION(viewport_pixel_center, 0x1924);
3511ASSERT_REG_POSITION(viewport_scale_offset_enbled, 0x192C); 3485ASSERT_REG_POSITION(viewport_scale_offset_enabled, 0x192C);
3512ASSERT_REG_POSITION(viewport_clip_control, 0x193C); 3486ASSERT_REG_POSITION(viewport_clip_control, 0x193C);
3513ASSERT_REG_POSITION(user_clip_op, 0x1940); 3487ASSERT_REG_POSITION(user_clip_op, 0x1940);
3514ASSERT_REG_POSITION(render_enable_override, 0x1944); 3488ASSERT_REG_POSITION(render_enable_override, 0x1944);
diff --git a/src/video_core/macro/macro.cpp b/src/video_core/macro/macro.cpp
index f61d5998e..505d81c1e 100644
--- a/src/video_core/macro/macro.cpp
+++ b/src/video_core/macro/macro.cpp
@@ -16,7 +16,10 @@
16#include "video_core/macro/macro.h" 16#include "video_core/macro/macro.h"
17#include "video_core/macro/macro_hle.h" 17#include "video_core/macro/macro_hle.h"
18#include "video_core/macro/macro_interpreter.h" 18#include "video_core/macro/macro_interpreter.h"
19
20#ifdef ARCHITECTURE_x86_64
19#include "video_core/macro/macro_jit_x64.h" 21#include "video_core/macro/macro_jit_x64.h"
22#endif
20 23
21namespace Tegra { 24namespace Tegra {
22 25
diff --git a/src/video_core/macro/macro_hle.cpp b/src/video_core/macro/macro_hle.cpp
index 8a8adbb42..f896591bf 100644
--- a/src/video_core/macro/macro_hle.cpp
+++ b/src/video_core/macro/macro_hle.cpp
@@ -22,35 +22,29 @@ void HLE_771BB18C62444DA0(Engines::Maxwell3D& maxwell3d, const std::vector<u32>&
22 maxwell3d.regs.draw.topology.Assign( 22 maxwell3d.regs.draw.topology.Assign(
23 static_cast<Tegra::Engines::Maxwell3D::Regs::PrimitiveTopology>(parameters[0] & 0x3ffffff)); 23 static_cast<Tegra::Engines::Maxwell3D::Regs::PrimitiveTopology>(parameters[0] & 0x3ffffff));
24 maxwell3d.regs.global_base_instance_index = parameters[5]; 24 maxwell3d.regs.global_base_instance_index = parameters[5];
25 maxwell3d.mme_draw.instance_count = instance_count;
26 maxwell3d.regs.global_base_vertex_index = parameters[3]; 25 maxwell3d.regs.global_base_vertex_index = parameters[3];
27 maxwell3d.regs.index_buffer.count = parameters[1]; 26 maxwell3d.regs.index_buffer.count = parameters[1];
28 maxwell3d.regs.index_buffer.first = parameters[4]; 27 maxwell3d.regs.index_buffer.first = parameters[4];
29 28
30 if (maxwell3d.ShouldExecute()) { 29 if (maxwell3d.ShouldExecute()) {
31 maxwell3d.Rasterizer().Draw(true, true); 30 maxwell3d.Rasterizer().Draw(true, instance_count);
32 } 31 }
33 maxwell3d.regs.index_buffer.count = 0; 32 maxwell3d.regs.index_buffer.count = 0;
34 maxwell3d.mme_draw.instance_count = 0;
35 maxwell3d.mme_draw.current_mode = Engines::Maxwell3D::MMEDrawMode::Undefined;
36} 33}
37 34
38void HLE_0D61FC9FAAC9FCAD(Engines::Maxwell3D& maxwell3d, const std::vector<u32>& parameters) { 35void HLE_0D61FC9FAAC9FCAD(Engines::Maxwell3D& maxwell3d, const std::vector<u32>& parameters) {
39 const u32 count = (maxwell3d.GetRegisterValue(0xD1B) & parameters[2]); 36 const u32 instance_count = (maxwell3d.GetRegisterValue(0xD1B) & parameters[2]);
40 37
41 maxwell3d.regs.vertex_buffer.first = parameters[3]; 38 maxwell3d.regs.vertex_buffer.first = parameters[3];
42 maxwell3d.regs.vertex_buffer.count = parameters[1]; 39 maxwell3d.regs.vertex_buffer.count = parameters[1];
43 maxwell3d.regs.global_base_instance_index = parameters[4]; 40 maxwell3d.regs.global_base_instance_index = parameters[4];
44 maxwell3d.regs.draw.topology.Assign( 41 maxwell3d.regs.draw.topology.Assign(
45 static_cast<Tegra::Engines::Maxwell3D::Regs::PrimitiveTopology>(parameters[0])); 42 static_cast<Tegra::Engines::Maxwell3D::Regs::PrimitiveTopology>(parameters[0]));
46 maxwell3d.mme_draw.instance_count = count;
47 43
48 if (maxwell3d.ShouldExecute()) { 44 if (maxwell3d.ShouldExecute()) {
49 maxwell3d.Rasterizer().Draw(false, true); 45 maxwell3d.Rasterizer().Draw(false, instance_count);
50 } 46 }
51 maxwell3d.regs.vertex_buffer.count = 0; 47 maxwell3d.regs.vertex_buffer.count = 0;
52 maxwell3d.mme_draw.instance_count = 0;
53 maxwell3d.mme_draw.current_mode = Engines::Maxwell3D::MMEDrawMode::Undefined;
54} 48}
55 49
56void HLE_0217920100488FF7(Engines::Maxwell3D& maxwell3d, const std::vector<u32>& parameters) { 50void HLE_0217920100488FF7(Engines::Maxwell3D& maxwell3d, const std::vector<u32>& parameters) {
@@ -63,24 +57,21 @@ void HLE_0217920100488FF7(Engines::Maxwell3D& maxwell3d, const std::vector<u32>&
63 maxwell3d.dirty.flags[VideoCommon::Dirty::IndexBuffer] = true; 57 maxwell3d.dirty.flags[VideoCommon::Dirty::IndexBuffer] = true;
64 maxwell3d.regs.global_base_vertex_index = element_base; 58 maxwell3d.regs.global_base_vertex_index = element_base;
65 maxwell3d.regs.global_base_instance_index = base_instance; 59 maxwell3d.regs.global_base_instance_index = base_instance;
66 maxwell3d.mme_draw.instance_count = instance_count; 60 maxwell3d.CallMethod(0x8e3, 0x640, true);
67 maxwell3d.CallMethodFromMME(0x8e3, 0x640); 61 maxwell3d.CallMethod(0x8e4, element_base, true);
68 maxwell3d.CallMethodFromMME(0x8e4, element_base); 62 maxwell3d.CallMethod(0x8e5, base_instance, true);
69 maxwell3d.CallMethodFromMME(0x8e5, base_instance);
70 maxwell3d.regs.draw.topology.Assign( 63 maxwell3d.regs.draw.topology.Assign(
71 static_cast<Tegra::Engines::Maxwell3D::Regs::PrimitiveTopology>(parameters[0])); 64 static_cast<Tegra::Engines::Maxwell3D::Regs::PrimitiveTopology>(parameters[0]));
72 if (maxwell3d.ShouldExecute()) { 65 if (maxwell3d.ShouldExecute()) {
73 maxwell3d.Rasterizer().Draw(true, true); 66 maxwell3d.Rasterizer().Draw(true, instance_count);
74 } 67 }
75 maxwell3d.regs.vertex_id_base = 0x0; 68 maxwell3d.regs.vertex_id_base = 0x0;
76 maxwell3d.regs.index_buffer.count = 0; 69 maxwell3d.regs.index_buffer.count = 0;
77 maxwell3d.regs.global_base_vertex_index = 0x0; 70 maxwell3d.regs.global_base_vertex_index = 0x0;
78 maxwell3d.regs.global_base_instance_index = 0x0; 71 maxwell3d.regs.global_base_instance_index = 0x0;
79 maxwell3d.mme_draw.instance_count = 0; 72 maxwell3d.CallMethod(0x8e3, 0x640, true);
80 maxwell3d.CallMethodFromMME(0x8e3, 0x640); 73 maxwell3d.CallMethod(0x8e4, 0x0, true);
81 maxwell3d.CallMethodFromMME(0x8e4, 0x0); 74 maxwell3d.CallMethod(0x8e5, 0x0, true);
82 maxwell3d.CallMethodFromMME(0x8e5, 0x0);
83 maxwell3d.mme_draw.current_mode = Engines::Maxwell3D::MMEDrawMode::Undefined;
84} 75}
85 76
86// Multidraw Indirect 77// Multidraw Indirect
@@ -91,11 +82,9 @@ void HLE_3F5E74B9C9A50164(Engines::Maxwell3D& maxwell3d, const std::vector<u32>&
91 maxwell3d.regs.index_buffer.count = 0; 82 maxwell3d.regs.index_buffer.count = 0;
92 maxwell3d.regs.global_base_vertex_index = 0x0; 83 maxwell3d.regs.global_base_vertex_index = 0x0;
93 maxwell3d.regs.global_base_instance_index = 0x0; 84 maxwell3d.regs.global_base_instance_index = 0x0;
94 maxwell3d.mme_draw.instance_count = 0; 85 maxwell3d.CallMethod(0x8e3, 0x640, true);
95 maxwell3d.CallMethodFromMME(0x8e3, 0x640); 86 maxwell3d.CallMethod(0x8e4, 0x0, true);
96 maxwell3d.CallMethodFromMME(0x8e4, 0x0); 87 maxwell3d.CallMethod(0x8e5, 0x0, true);
97 maxwell3d.CallMethodFromMME(0x8e5, 0x0);
98 maxwell3d.mme_draw.current_mode = Engines::Maxwell3D::MMEDrawMode::Undefined;
99 maxwell3d.dirty.flags[VideoCommon::Dirty::IndexBuffer] = true; 88 maxwell3d.dirty.flags[VideoCommon::Dirty::IndexBuffer] = true;
100 }); 89 });
101 const u32 start_indirect = parameters[0]; 90 const u32 start_indirect = parameters[0];
@@ -127,15 +116,13 @@ void HLE_3F5E74B9C9A50164(Engines::Maxwell3D& maxwell3d, const std::vector<u32>&
127 maxwell3d.regs.index_buffer.count = num_vertices; 116 maxwell3d.regs.index_buffer.count = num_vertices;
128 maxwell3d.regs.global_base_vertex_index = base_vertex; 117 maxwell3d.regs.global_base_vertex_index = base_vertex;
129 maxwell3d.regs.global_base_instance_index = base_instance; 118 maxwell3d.regs.global_base_instance_index = base_instance;
130 maxwell3d.mme_draw.instance_count = instance_count; 119 maxwell3d.CallMethod(0x8e3, 0x640, true);
131 maxwell3d.CallMethodFromMME(0x8e3, 0x640); 120 maxwell3d.CallMethod(0x8e4, base_vertex, true);
132 maxwell3d.CallMethodFromMME(0x8e4, base_vertex); 121 maxwell3d.CallMethod(0x8e5, base_instance, true);
133 maxwell3d.CallMethodFromMME(0x8e5, base_instance);
134 maxwell3d.dirty.flags[VideoCommon::Dirty::IndexBuffer] = true; 122 maxwell3d.dirty.flags[VideoCommon::Dirty::IndexBuffer] = true;
135 if (maxwell3d.ShouldExecute()) { 123 if (maxwell3d.ShouldExecute()) {
136 maxwell3d.Rasterizer().Draw(true, true); 124 maxwell3d.Rasterizer().Draw(true, instance_count);
137 } 125 }
138 maxwell3d.mme_draw.current_mode = Engines::Maxwell3D::MMEDrawMode::Undefined;
139 } 126 }
140} 127}
141 128
diff --git a/src/video_core/macro/macro_interpreter.cpp b/src/video_core/macro/macro_interpreter.cpp
index f670b1bca..c0d32c112 100644
--- a/src/video_core/macro/macro_interpreter.cpp
+++ b/src/video_core/macro/macro_interpreter.cpp
@@ -335,7 +335,7 @@ void MacroInterpreterImpl::SetMethodAddress(u32 address) {
335} 335}
336 336
337void MacroInterpreterImpl::Send(u32 value) { 337void MacroInterpreterImpl::Send(u32 value) {
338 maxwell3d.CallMethodFromMME(method_address.address, value); 338 maxwell3d.CallMethod(method_address.address, value, true);
339 // Increment the method address by the method increment. 339 // Increment the method address by the method increment.
340 method_address.address.Assign(method_address.address.Value() + 340 method_address.address.Assign(method_address.address.Value() +
341 method_address.increment.Value()); 341 method_address.increment.Value());
diff --git a/src/video_core/macro/macro_jit_x64.cpp b/src/video_core/macro/macro_jit_x64.cpp
index a302a9603..25c1ce798 100644
--- a/src/video_core/macro/macro_jit_x64.cpp
+++ b/src/video_core/macro/macro_jit_x64.cpp
@@ -346,7 +346,7 @@ void MacroJITx64Impl::Compile_Read(Macro::Opcode opcode) {
346} 346}
347 347
348void Send(Engines::Maxwell3D* maxwell3d, Macro::MethodAddress method_address, u32 value) { 348void Send(Engines::Maxwell3D* maxwell3d, Macro::MethodAddress method_address, u32 value) {
349 maxwell3d->CallMethodFromMME(method_address.address, value); 349 maxwell3d->CallMethod(method_address.address, value, true);
350} 350}
351 351
352void MacroJITx64Impl::Compile_Send(Xbyak::Reg32 value) { 352void MacroJITx64Impl::Compile_Send(Xbyak::Reg32 value) {
diff --git a/src/video_core/memory_manager.cpp b/src/video_core/memory_manager.cpp
index 384350dbd..8c8dfcca6 100644
--- a/src/video_core/memory_manager.cpp
+++ b/src/video_core/memory_manager.cpp
@@ -45,7 +45,7 @@ MemoryManager::MemoryManager(Core::System& system_, u64 address_space_bits_, u64
45 kind_valus.fill(PTEKind::INVALID); 45 kind_valus.fill(PTEKind::INVALID);
46 big_kinds.resize(big_page_table_size / 32, kind_valus); 46 big_kinds.resize(big_page_table_size / 32, kind_valus);
47 entries.resize(page_table_size / 32, 0); 47 entries.resize(page_table_size / 32, 0);
48 kinds.resize(big_page_table_size / 32, kind_valus); 48 kinds.resize(page_table_size / 32, kind_valus);
49} 49}
50 50
51MemoryManager::~MemoryManager() = default; 51MemoryManager::~MemoryManager() = default;
diff --git a/src/video_core/rasterizer_interface.h b/src/video_core/rasterizer_interface.h
index d2d40884c..1cbfef090 100644
--- a/src/video_core/rasterizer_interface.h
+++ b/src/video_core/rasterizer_interface.h
@@ -40,7 +40,7 @@ public:
40 virtual ~RasterizerInterface() = default; 40 virtual ~RasterizerInterface() = default;
41 41
42 /// Dispatches a draw invocation 42 /// Dispatches a draw invocation
43 virtual void Draw(bool is_indexed, bool is_instanced) = 0; 43 virtual void Draw(bool is_indexed, u32 instance_count) = 0;
44 44
45 /// Clear the current framebuffer 45 /// Clear the current framebuffer
46 virtual void Clear() = 0; 46 virtual void Clear() = 0;
diff --git a/src/video_core/renderer_opengl/gl_buffer_cache.cpp b/src/video_core/renderer_opengl/gl_buffer_cache.cpp
index 08f4d69ab..6af4ae793 100644
--- a/src/video_core/renderer_opengl/gl_buffer_cache.cpp
+++ b/src/video_core/renderer_opengl/gl_buffer_cache.cpp
@@ -29,17 +29,17 @@ constexpr std::array PROGRAM_LUT{
29[[nodiscard]] GLenum GetTextureBufferFormat(GLenum gl_format) { 29[[nodiscard]] GLenum GetTextureBufferFormat(GLenum gl_format) {
30 switch (gl_format) { 30 switch (gl_format) {
31 case GL_RGBA8_SNORM: 31 case GL_RGBA8_SNORM:
32 return GL_RGBA8; 32 return GL_RGBA8I;
33 case GL_R8_SNORM: 33 case GL_R8_SNORM:
34 return GL_R8; 34 return GL_R8I;
35 case GL_RGBA16_SNORM: 35 case GL_RGBA16_SNORM:
36 return GL_RGBA16; 36 return GL_RGBA16I;
37 case GL_R16_SNORM: 37 case GL_R16_SNORM:
38 return GL_R16; 38 return GL_R16I;
39 case GL_RG16_SNORM: 39 case GL_RG16_SNORM:
40 return GL_RG16; 40 return GL_RG16I;
41 case GL_RG8_SNORM: 41 case GL_RG8_SNORM:
42 return GL_RG8; 42 return GL_RG8I;
43 default: 43 default:
44 return gl_format; 44 return gl_format;
45 } 45 }
@@ -96,9 +96,6 @@ GLuint Buffer::View(u32 offset, u32 size, PixelFormat format) {
96 texture.Create(GL_TEXTURE_BUFFER); 96 texture.Create(GL_TEXTURE_BUFFER);
97 const GLenum gl_format{MaxwellToGL::GetFormatTuple(format).internal_format}; 97 const GLenum gl_format{MaxwellToGL::GetFormatTuple(format).internal_format};
98 const GLenum texture_format{GetTextureBufferFormat(gl_format)}; 98 const GLenum texture_format{GetTextureBufferFormat(gl_format)};
99 if (texture_format != gl_format) {
100 LOG_WARNING(Render_OpenGL, "Emulating SNORM texture buffer with UNORM.");
101 }
102 glTextureBufferRange(texture.handle, texture_format, buffer.handle, offset, size); 99 glTextureBufferRange(texture.handle, texture_format, buffer.handle, offset, size);
103 views.push_back({ 100 views.push_back({
104 .offset = offset, 101 .offset = offset,
diff --git a/src/video_core/renderer_opengl/gl_graphics_pipeline.cpp b/src/video_core/renderer_opengl/gl_graphics_pipeline.cpp
index 1d20a79ec..c115dabe1 100644
--- a/src/video_core/renderer_opengl/gl_graphics_pipeline.cpp
+++ b/src/video_core/renderer_opengl/gl_graphics_pipeline.cpp
@@ -503,6 +503,17 @@ void GraphicsPipeline::ConfigureImpl(bool is_indexed) {
503 float_image_scaling_mask, down_factor, 0.0f); 503 float_image_scaling_mask, down_factor, 0.0f);
504 } 504 }
505 } 505 }
506 if (info.uses_render_area) {
507 const auto render_area_width(static_cast<GLfloat>(regs.surface_clip.width));
508 const auto render_area_height(static_cast<GLfloat>(regs.surface_clip.height));
509 if (use_assembly) {
510 glProgramLocalParameter4fARB(AssemblyStage(stage), 1, render_area_width,
511 render_area_height, 0.0f, 0.0f);
512 } else {
513 glProgramUniform4f(source_programs[stage].handle, 1, render_area_width,
514 render_area_height, 0.0f, 0.0f);
515 }
516 }
506 }}; 517 }};
507 if constexpr (Spec::enabled_stages[0]) { 518 if constexpr (Spec::enabled_stages[0]) {
508 prepare_stage(0); 519 prepare_stage(0);
diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp
index e5c09a969..8a8b5ce54 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer.cpp
+++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp
@@ -205,7 +205,7 @@ void RasterizerOpenGL::Clear() {
205 ++num_queued_commands; 205 ++num_queued_commands;
206} 206}
207 207
208void RasterizerOpenGL::Draw(bool is_indexed, bool is_instanced) { 208void RasterizerOpenGL::Draw(bool is_indexed, u32 instance_count) {
209 MICROPROFILE_SCOPE(OpenGL_Drawing); 209 MICROPROFILE_SCOPE(OpenGL_Drawing);
210 210
211 SCOPE_EXIT({ gpu.TickWork(); }); 211 SCOPE_EXIT({ gpu.TickWork(); });
@@ -222,14 +222,15 @@ void RasterizerOpenGL::Draw(bool is_indexed, bool is_instanced) {
222 pipeline->SetEngine(maxwell3d, gpu_memory); 222 pipeline->SetEngine(maxwell3d, gpu_memory);
223 pipeline->Configure(is_indexed); 223 pipeline->Configure(is_indexed);
224 224
225 BindInlineIndexBuffer();
226
225 SyncState(); 227 SyncState();
226 228
227 const GLenum primitive_mode = MaxwellToGL::PrimitiveTopology(maxwell3d->regs.draw.topology); 229 const GLenum primitive_mode = MaxwellToGL::PrimitiveTopology(maxwell3d->regs.draw.topology);
228 BeginTransformFeedback(pipeline, primitive_mode); 230 BeginTransformFeedback(pipeline, primitive_mode);
229 231
230 const GLuint base_instance = static_cast<GLuint>(maxwell3d->regs.global_base_instance_index); 232 const GLuint base_instance = static_cast<GLuint>(maxwell3d->regs.global_base_instance_index);
231 const GLsizei num_instances = 233 const GLsizei num_instances = static_cast<GLsizei>(instance_count);
232 static_cast<GLsizei>(is_instanced ? maxwell3d->mme_draw.instance_count : 1);
233 if (is_indexed) { 234 if (is_indexed) {
234 const GLint base_vertex = static_cast<GLint>(maxwell3d->regs.global_base_vertex_index); 235 const GLint base_vertex = static_cast<GLint>(maxwell3d->regs.global_base_vertex_index);
235 const GLsizei num_vertices = static_cast<GLsizei>(maxwell3d->regs.index_buffer.count); 236 const GLsizei num_vertices = static_cast<GLsizei>(maxwell3d->regs.index_buffer.count);
@@ -617,6 +618,16 @@ void RasterizerOpenGL::SyncViewport() {
617 } 618 }
618 flags[Dirty::Viewport0 + index] = false; 619 flags[Dirty::Viewport0 + index] = false;
619 620
621 if (!regs.viewport_scale_offset_enabled) {
622 const auto x = static_cast<GLfloat>(regs.surface_clip.x);
623 const auto y = static_cast<GLfloat>(regs.surface_clip.y);
624 const auto width = static_cast<GLfloat>(regs.surface_clip.width);
625 const auto height = static_cast<GLfloat>(regs.surface_clip.height);
626 glViewportIndexedf(static_cast<GLuint>(index), x, y, width != 0.0f ? width : 1.0f,
627 height != 0.0f ? height : 1.0f);
628 continue;
629 }
630
620 const auto& src = regs.viewport_transform[index]; 631 const auto& src = regs.viewport_transform[index];
621 GLfloat x = conv(src.translate_x - src.scale_x); 632 GLfloat x = conv(src.translate_x - src.scale_x);
622 GLfloat y = conv(src.translate_y - src.scale_y); 633 GLfloat y = conv(src.translate_y - src.scale_y);
@@ -1129,6 +1140,16 @@ void RasterizerOpenGL::ReleaseChannel(s32 channel_id) {
1129 query_cache.EraseChannel(channel_id); 1140 query_cache.EraseChannel(channel_id);
1130} 1141}
1131 1142
1143void RasterizerOpenGL::BindInlineIndexBuffer() {
1144 if (maxwell3d->inline_index_draw_indexes.empty()) {
1145 return;
1146 }
1147 const auto data_count = static_cast<u32>(maxwell3d->inline_index_draw_indexes.size());
1148 auto buffer = Buffer(buffer_cache_runtime, *this, 0, data_count);
1149 buffer.ImmediateUpload(0, maxwell3d->inline_index_draw_indexes);
1150 buffer_cache_runtime.BindIndexBuffer(buffer, 0, data_count);
1151}
1152
1132AccelerateDMA::AccelerateDMA(BufferCache& buffer_cache_) : buffer_cache{buffer_cache_} {} 1153AccelerateDMA::AccelerateDMA(BufferCache& buffer_cache_) : buffer_cache{buffer_cache_} {}
1133 1154
1134bool AccelerateDMA::BufferCopy(GPUVAddr src_address, GPUVAddr dest_address, u64 amount) { 1155bool AccelerateDMA::BufferCopy(GPUVAddr src_address, GPUVAddr dest_address, u64 amount) {
diff --git a/src/video_core/renderer_opengl/gl_rasterizer.h b/src/video_core/renderer_opengl/gl_rasterizer.h
index 45131b785..793e0d608 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer.h
+++ b/src/video_core/renderer_opengl/gl_rasterizer.h
@@ -68,7 +68,7 @@ public:
68 StateTracker& state_tracker_); 68 StateTracker& state_tracker_);
69 ~RasterizerOpenGL() override; 69 ~RasterizerOpenGL() override;
70 70
71 void Draw(bool is_indexed, bool is_instanced) override; 71 void Draw(bool is_indexed, u32 instance_count) override;
72 void Clear() override; 72 void Clear() override;
73 void DispatchCompute() override; 73 void DispatchCompute() override;
74 void ResetCounter(VideoCore::QueryType type) override; 74 void ResetCounter(VideoCore::QueryType type) override;
@@ -199,6 +199,8 @@ private:
199 /// End a transform feedback 199 /// End a transform feedback
200 void EndTransformFeedback(); 200 void EndTransformFeedback();
201 201
202 void BindInlineIndexBuffer();
203
202 Tegra::GPU& gpu; 204 Tegra::GPU& gpu;
203 205
204 const Device& device; 206 const Device& device;
diff --git a/src/video_core/renderer_opengl/gl_shader_cache.cpp b/src/video_core/renderer_opengl/gl_shader_cache.cpp
index e94cfdb1a..3fe04a115 100644
--- a/src/video_core/renderer_opengl/gl_shader_cache.cpp
+++ b/src/video_core/renderer_opengl/gl_shader_cache.cpp
@@ -49,7 +49,7 @@ using VideoCommon::LoadPipelines;
49using VideoCommon::SerializePipeline; 49using VideoCommon::SerializePipeline;
50using Context = ShaderContext::Context; 50using Context = ShaderContext::Context;
51 51
52constexpr u32 CACHE_VERSION = 6; 52constexpr u32 CACHE_VERSION = 7;
53 53
54template <typename Container> 54template <typename Container>
55auto MakeSpan(Container& container) { 55auto MakeSpan(Container& container) {
@@ -76,7 +76,8 @@ Shader::RuntimeInfo MakeRuntimeInfo(const GraphicsPipelineKey& key,
76 } 76 }
77 break; 77 break;
78 case Shader::Stage::TessellationEval: 78 case Shader::Stage::TessellationEval:
79 info.tess_clockwise = key.tessellation_clockwise != 0; 79 // Flip the face, as OpenGL's drawing is flipped.
80 info.tess_clockwise = key.tessellation_clockwise == 0;
80 info.tess_primitive = [&key] { 81 info.tess_primitive = [&key] {
81 switch (key.tessellation_primitive) { 82 switch (key.tessellation_primitive) {
82 case Maxwell::Tessellation::DomainType::Isolines: 83 case Maxwell::Tessellation::DomainType::Isolines:
@@ -218,6 +219,7 @@ ShaderCache::ShaderCache(RasterizerOpenGL& rasterizer_, Core::Frontend::EmuWindo
218 .support_float16 = false, 219 .support_float16 = false,
219 .support_int64 = device.HasShaderInt64(), 220 .support_int64 = device.HasShaderInt64(),
220 .needs_demote_reorder = device.IsAmd(), 221 .needs_demote_reorder = device.IsAmd(),
222 .support_snorm_render_buffer = false,
221 } { 223 } {
222 if (use_asynchronous_shaders) { 224 if (use_asynchronous_shaders) {
223 workers = CreateWorkers(); 225 workers = CreateWorkers();
diff --git a/src/video_core/renderer_opengl/gl_state_tracker.cpp b/src/video_core/renderer_opengl/gl_state_tracker.cpp
index a359f96f1..d53b422ca 100644
--- a/src/video_core/renderer_opengl/gl_state_tracker.cpp
+++ b/src/video_core/renderer_opengl/gl_state_tracker.cpp
@@ -70,8 +70,8 @@ void SetupDirtyViewports(Tables& tables) {
70 FillBlock(tables[1], OFF(viewport_transform), NUM(viewport_transform), Viewports); 70 FillBlock(tables[1], OFF(viewport_transform), NUM(viewport_transform), Viewports);
71 FillBlock(tables[1], OFF(viewports), NUM(viewports), Viewports); 71 FillBlock(tables[1], OFF(viewports), NUM(viewports), Viewports);
72 72
73 tables[0][OFF(viewport_scale_offset_enbled)] = ViewportTransform; 73 tables[0][OFF(viewport_scale_offset_enabled)] = ViewportTransform;
74 tables[1][OFF(viewport_scale_offset_enbled)] = Viewports; 74 tables[1][OFF(viewport_scale_offset_enabled)] = Viewports;
75} 75}
76 76
77void SetupDirtyScissors(Tables& tables) { 77void SetupDirtyScissors(Tables& tables) {
diff --git a/src/video_core/renderer_vulkan/pipeline_helper.h b/src/video_core/renderer_vulkan/pipeline_helper.h
index b24f3424a..b7843e995 100644
--- a/src/video_core/renderer_vulkan/pipeline_helper.h
+++ b/src/video_core/renderer_vulkan/pipeline_helper.h
@@ -68,13 +68,15 @@ public:
68 } 68 }
69 69
70 vk::PipelineLayout CreatePipelineLayout(VkDescriptorSetLayout descriptor_set_layout) const { 70 vk::PipelineLayout CreatePipelineLayout(VkDescriptorSetLayout descriptor_set_layout) const {
71 using Shader::Backend::SPIRV::RenderAreaLayout;
71 using Shader::Backend::SPIRV::RescalingLayout; 72 using Shader::Backend::SPIRV::RescalingLayout;
72 const u32 size_offset = is_compute ? sizeof(RescalingLayout::down_factor) : 0u; 73 const u32 size_offset = is_compute ? sizeof(RescalingLayout::down_factor) : 0u;
73 const VkPushConstantRange range{ 74 const VkPushConstantRange range{
74 .stageFlags = static_cast<VkShaderStageFlags>( 75 .stageFlags = static_cast<VkShaderStageFlags>(
75 is_compute ? VK_SHADER_STAGE_COMPUTE_BIT : VK_SHADER_STAGE_ALL_GRAPHICS), 76 is_compute ? VK_SHADER_STAGE_COMPUTE_BIT : VK_SHADER_STAGE_ALL_GRAPHICS),
76 .offset = 0, 77 .offset = 0,
77 .size = static_cast<u32>(sizeof(RescalingLayout)) - size_offset, 78 .size = static_cast<u32>(sizeof(RescalingLayout)) - size_offset +
79 static_cast<u32>(sizeof(RenderAreaLayout)),
78 }; 80 };
79 return device->GetLogical().CreatePipelineLayout({ 81 return device->GetLogical().CreatePipelineLayout({
80 .sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO, 82 .sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO,
@@ -167,6 +169,12 @@ private:
167 u32 image_bit{1u}; 169 u32 image_bit{1u};
168}; 170};
169 171
172class RenderAreaPushConstant {
173public:
174 bool uses_render_area{};
175 std::array<f32, 4> words{};
176};
177
170inline void PushImageDescriptors(TextureCache& texture_cache, 178inline void PushImageDescriptors(TextureCache& texture_cache,
171 UpdateDescriptorQueue& update_descriptor_queue, 179 UpdateDescriptorQueue& update_descriptor_queue,
172 const Shader::Info& info, RescalingPushConstant& rescaling, 180 const Shader::Info& info, RescalingPushConstant& rescaling,
diff --git a/src/video_core/renderer_vulkan/vk_blit_screen.cpp b/src/video_core/renderer_vulkan/vk_blit_screen.cpp
index cb7fa2078..89426121f 100644
--- a/src/video_core/renderer_vulkan/vk_blit_screen.cpp
+++ b/src/video_core/renderer_vulkan/vk_blit_screen.cpp
@@ -480,11 +480,15 @@ void BlitScreen::RefreshResources(const Tegra::FramebufferConfig& framebuffer) {
480 fsr.reset(); 480 fsr.reset();
481 } 481 }
482 482
483 if (framebuffer.width == raw_width && framebuffer.height == raw_height && !raw_images.empty()) { 483 if (framebuffer.width == raw_width && framebuffer.height == raw_height &&
484 framebuffer.pixel_format == pixel_format && !raw_images.empty()) {
484 return; 485 return;
485 } 486 }
487
486 raw_width = framebuffer.width; 488 raw_width = framebuffer.width;
487 raw_height = framebuffer.height; 489 raw_height = framebuffer.height;
490 pixel_format = framebuffer.pixel_format;
491
488 ReleaseRawImages(); 492 ReleaseRawImages();
489 493
490 CreateStagingBuffer(framebuffer); 494 CreateStagingBuffer(framebuffer);
diff --git a/src/video_core/renderer_vulkan/vk_blit_screen.h b/src/video_core/renderer_vulkan/vk_blit_screen.h
index 29e2ea925..a2b73ec54 100644
--- a/src/video_core/renderer_vulkan/vk_blit_screen.h
+++ b/src/video_core/renderer_vulkan/vk_blit_screen.h
@@ -28,6 +28,10 @@ namespace VideoCore {
28class RasterizerInterface; 28class RasterizerInterface;
29} 29}
30 30
31namespace Service::android {
32enum class PixelFormat : u32;
33}
34
31namespace Vulkan { 35namespace Vulkan {
32 36
33struct ScreenInfo; 37struct ScreenInfo;
@@ -156,6 +160,7 @@ private:
156 160
157 u32 raw_width = 0; 161 u32 raw_width = 0;
158 u32 raw_height = 0; 162 u32 raw_height = 0;
163 Service::android::PixelFormat pixel_format{};
159 164
160 std::unique_ptr<FSR> fsr; 165 std::unique_ptr<FSR> fsr;
161}; 166};
diff --git a/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp b/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp
index c3f66c8a3..1aa116cea 100644
--- a/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp
+++ b/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp
@@ -31,6 +31,7 @@ namespace {
31using boost::container::small_vector; 31using boost::container::small_vector;
32using boost::container::static_vector; 32using boost::container::static_vector;
33using Shader::ImageBufferDescriptor; 33using Shader::ImageBufferDescriptor;
34using Shader::Backend::SPIRV::RENDERAREA_LAYOUT_OFFSET;
34using Shader::Backend::SPIRV::RESCALING_LAYOUT_DOWN_FACTOR_OFFSET; 35using Shader::Backend::SPIRV::RESCALING_LAYOUT_DOWN_FACTOR_OFFSET;
35using Shader::Backend::SPIRV::RESCALING_LAYOUT_WORDS_OFFSET; 36using Shader::Backend::SPIRV::RESCALING_LAYOUT_WORDS_OFFSET;
36using Tegra::Texture::TexturePair; 37using Tegra::Texture::TexturePair;
@@ -433,12 +434,19 @@ void GraphicsPipeline::ConfigureImpl(bool is_indexed) {
433 update_descriptor_queue.Acquire(); 434 update_descriptor_queue.Acquire();
434 435
435 RescalingPushConstant rescaling; 436 RescalingPushConstant rescaling;
437 RenderAreaPushConstant render_area;
436 const VkSampler* samplers_it{samplers.data()}; 438 const VkSampler* samplers_it{samplers.data()};
437 const VideoCommon::ImageViewInOut* views_it{views.data()}; 439 const VideoCommon::ImageViewInOut* views_it{views.data()};
438 const auto prepare_stage{[&](size_t stage) LAMBDA_FORCEINLINE { 440 const auto prepare_stage{[&](size_t stage) LAMBDA_FORCEINLINE {
439 buffer_cache.BindHostStageBuffers(stage); 441 buffer_cache.BindHostStageBuffers(stage);
440 PushImageDescriptors(texture_cache, update_descriptor_queue, stage_infos[stage], rescaling, 442 PushImageDescriptors(texture_cache, update_descriptor_queue, stage_infos[stage], rescaling,
441 samplers_it, views_it); 443 samplers_it, views_it);
444 const auto& info{stage_infos[0]};
445 if (info.uses_render_area) {
446 render_area.uses_render_area = true;
447 render_area.words = {static_cast<float>(regs.surface_clip.width),
448 static_cast<float>(regs.surface_clip.height)};
449 }
442 }}; 450 }};
443 if constexpr (Spec::enabled_stages[0]) { 451 if constexpr (Spec::enabled_stages[0]) {
444 prepare_stage(0); 452 prepare_stage(0);
@@ -455,10 +463,11 @@ void GraphicsPipeline::ConfigureImpl(bool is_indexed) {
455 if constexpr (Spec::enabled_stages[4]) { 463 if constexpr (Spec::enabled_stages[4]) {
456 prepare_stage(4); 464 prepare_stage(4);
457 } 465 }
458 ConfigureDraw(rescaling); 466 ConfigureDraw(rescaling, render_area);
459} 467}
460 468
461void GraphicsPipeline::ConfigureDraw(const RescalingPushConstant& rescaling) { 469void GraphicsPipeline::ConfigureDraw(const RescalingPushConstant& rescaling,
470 const RenderAreaPushConstant& render_area) {
462 texture_cache.UpdateRenderTargets(false); 471 texture_cache.UpdateRenderTargets(false);
463 scheduler.RequestRenderpass(texture_cache.GetFramebuffer()); 472 scheduler.RequestRenderpass(texture_cache.GetFramebuffer());
464 473
@@ -474,7 +483,9 @@ void GraphicsPipeline::ConfigureDraw(const RescalingPushConstant& rescaling) {
474 const bool bind_pipeline{scheduler.UpdateGraphicsPipeline(this)}; 483 const bool bind_pipeline{scheduler.UpdateGraphicsPipeline(this)};
475 const void* const descriptor_data{update_descriptor_queue.UpdateData()}; 484 const void* const descriptor_data{update_descriptor_queue.UpdateData()};
476 scheduler.Record([this, descriptor_data, bind_pipeline, rescaling_data = rescaling.Data(), 485 scheduler.Record([this, descriptor_data, bind_pipeline, rescaling_data = rescaling.Data(),
477 is_rescaling, update_rescaling](vk::CommandBuffer cmdbuf) { 486 is_rescaling, update_rescaling,
487 uses_render_area = render_area.uses_render_area,
488 render_area_data = render_area.words](vk::CommandBuffer cmdbuf) {
478 if (bind_pipeline) { 489 if (bind_pipeline) {
479 cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, *pipeline); 490 cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, *pipeline);
480 } 491 }
@@ -488,6 +499,11 @@ void GraphicsPipeline::ConfigureDraw(const RescalingPushConstant& rescaling) {
488 RESCALING_LAYOUT_DOWN_FACTOR_OFFSET, sizeof(scale_down_factor), 499 RESCALING_LAYOUT_DOWN_FACTOR_OFFSET, sizeof(scale_down_factor),
489 &scale_down_factor); 500 &scale_down_factor);
490 } 501 }
502 if (uses_render_area) {
503 cmdbuf.PushConstants(*pipeline_layout, VK_SHADER_STAGE_ALL_GRAPHICS,
504 RENDERAREA_LAYOUT_OFFSET, sizeof(render_area_data),
505 &render_area_data);
506 }
491 if (!descriptor_set_layout) { 507 if (!descriptor_set_layout) {
492 return; 508 return;
493 } 509 }
diff --git a/src/video_core/renderer_vulkan/vk_graphics_pipeline.h b/src/video_core/renderer_vulkan/vk_graphics_pipeline.h
index 85602592b..6bf577d25 100644
--- a/src/video_core/renderer_vulkan/vk_graphics_pipeline.h
+++ b/src/video_core/renderer_vulkan/vk_graphics_pipeline.h
@@ -62,6 +62,7 @@ class Device;
62class PipelineStatistics; 62class PipelineStatistics;
63class RenderPassCache; 63class RenderPassCache;
64class RescalingPushConstant; 64class RescalingPushConstant;
65class RenderAreaPushConstant;
65class Scheduler; 66class Scheduler;
66class UpdateDescriptorQueue; 67class UpdateDescriptorQueue;
67 68
@@ -119,7 +120,8 @@ private:
119 template <typename Spec> 120 template <typename Spec>
120 void ConfigureImpl(bool is_indexed); 121 void ConfigureImpl(bool is_indexed);
121 122
122 void ConfigureDraw(const RescalingPushConstant& rescaling); 123 void ConfigureDraw(const RescalingPushConstant& rescaling,
124 const RenderAreaPushConstant& render_are);
123 125
124 void MakePipeline(VkRenderPass render_pass); 126 void MakePipeline(VkRenderPass render_pass);
125 127
diff --git a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp
index 13d5a1f67..d4b0a542a 100644
--- a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp
+++ b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp
@@ -53,7 +53,7 @@ using VideoCommon::FileEnvironment;
53using VideoCommon::GenericEnvironment; 53using VideoCommon::GenericEnvironment;
54using VideoCommon::GraphicsEnvironment; 54using VideoCommon::GraphicsEnvironment;
55 55
56constexpr u32 CACHE_VERSION = 6; 56constexpr u32 CACHE_VERSION = 7;
57 57
58template <typename Container> 58template <typename Container>
59auto MakeSpan(Container& container) { 59auto MakeSpan(Container& container) {
@@ -166,6 +166,7 @@ Shader::RuntimeInfo MakeRuntimeInfo(std::span<const Shader::IR::Program> program
166 } 166 }
167 break; 167 break;
168 case Shader::Stage::TessellationEval: 168 case Shader::Stage::TessellationEval:
169 info.tess_clockwise = key.state.tessellation_clockwise != 0;
169 info.tess_primitive = [&key] { 170 info.tess_primitive = [&key] {
170 const u32 raw{key.state.tessellation_primitive.Value()}; 171 const u32 raw{key.state.tessellation_primitive.Value()};
171 switch (static_cast<Maxwell::Tessellation::DomainType>(raw)) { 172 switch (static_cast<Maxwell::Tessellation::DomainType>(raw)) {
@@ -325,6 +326,7 @@ PipelineCache::PipelineCache(RasterizerVulkan& rasterizer_, const Device& device
325 .support_int64 = device.IsShaderInt64Supported(), 326 .support_int64 = device.IsShaderInt64Supported(),
326 .needs_demote_reorder = driver_id == VK_DRIVER_ID_AMD_PROPRIETARY_KHR || 327 .needs_demote_reorder = driver_id == VK_DRIVER_ID_AMD_PROPRIETARY_KHR ||
327 driver_id == VK_DRIVER_ID_AMD_OPEN_SOURCE_KHR, 328 driver_id == VK_DRIVER_ID_AMD_OPEN_SOURCE_KHR,
329 .support_snorm_render_buffer = true,
328 }; 330 };
329} 331}
330 332
diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.cpp b/src/video_core/renderer_vulkan/vk_rasterizer.cpp
index 47dfb45a1..f69c0c50f 100644
--- a/src/video_core/renderer_vulkan/vk_rasterizer.cpp
+++ b/src/video_core/renderer_vulkan/vk_rasterizer.cpp
@@ -127,11 +127,10 @@ VkRect2D GetScissorState(const Maxwell& regs, size_t index, u32 up_scale = 1, u3
127 return scissor; 127 return scissor;
128} 128}
129 129
130DrawParams MakeDrawParams(const Maxwell& regs, u32 num_instances, bool is_instanced, 130DrawParams MakeDrawParams(const Maxwell& regs, u32 num_instances, bool is_indexed) {
131 bool is_indexed) {
132 DrawParams params{ 131 DrawParams params{
133 .base_instance = regs.global_base_instance_index, 132 .base_instance = regs.global_base_instance_index,
134 .num_instances = is_instanced ? num_instances : 1, 133 .num_instances = num_instances,
135 .base_vertex = is_indexed ? regs.global_base_vertex_index : regs.vertex_buffer.first, 134 .base_vertex = is_indexed ? regs.global_base_vertex_index : regs.vertex_buffer.first,
136 .num_vertices = is_indexed ? regs.index_buffer.count : regs.vertex_buffer.count, 135 .num_vertices = is_indexed ? regs.index_buffer.count : regs.vertex_buffer.count,
137 .first_index = is_indexed ? regs.index_buffer.first : 0, 136 .first_index = is_indexed ? regs.index_buffer.first : 0,
@@ -157,12 +156,10 @@ RasterizerVulkan::RasterizerVulkan(Core::Frontend::EmuWindow& emu_window_, Tegra
157 staging_pool(device, memory_allocator, scheduler), descriptor_pool(device, scheduler), 156 staging_pool(device, memory_allocator, scheduler), descriptor_pool(device, scheduler),
158 update_descriptor_queue(device, scheduler), 157 update_descriptor_queue(device, scheduler),
159 blit_image(device, scheduler, state_tracker, descriptor_pool), 158 blit_image(device, scheduler, state_tracker, descriptor_pool),
160 astc_decoder_pass(device, scheduler, descriptor_pool, staging_pool, update_descriptor_queue,
161 memory_allocator),
162 render_pass_cache(device), texture_cache_runtime{device, scheduler, 159 render_pass_cache(device), texture_cache_runtime{device, scheduler,
163 memory_allocator, staging_pool, 160 memory_allocator, staging_pool,
164 blit_image, astc_decoder_pass, 161 blit_image, render_pass_cache,
165 render_pass_cache}, 162 descriptor_pool, update_descriptor_queue},
166 texture_cache(texture_cache_runtime, *this), 163 texture_cache(texture_cache_runtime, *this),
167 buffer_cache_runtime(device, memory_allocator, scheduler, staging_pool, 164 buffer_cache_runtime(device, memory_allocator, scheduler, staging_pool,
168 update_descriptor_queue, descriptor_pool), 165 update_descriptor_queue, descriptor_pool),
@@ -177,7 +174,7 @@ RasterizerVulkan::RasterizerVulkan(Core::Frontend::EmuWindow& emu_window_, Tegra
177 174
178RasterizerVulkan::~RasterizerVulkan() = default; 175RasterizerVulkan::~RasterizerVulkan() = default;
179 176
180void RasterizerVulkan::Draw(bool is_indexed, bool is_instanced) { 177void RasterizerVulkan::Draw(bool is_indexed, u32 instance_count) {
181 MICROPROFILE_SCOPE(Vulkan_Drawing); 178 MICROPROFILE_SCOPE(Vulkan_Drawing);
182 179
183 SCOPE_EXIT({ gpu.TickWork(); }); 180 SCOPE_EXIT({ gpu.TickWork(); });
@@ -194,13 +191,15 @@ void RasterizerVulkan::Draw(bool is_indexed, bool is_instanced) {
194 pipeline->SetEngine(maxwell3d, gpu_memory); 191 pipeline->SetEngine(maxwell3d, gpu_memory);
195 pipeline->Configure(is_indexed); 192 pipeline->Configure(is_indexed);
196 193
194 BindInlineIndexBuffer();
195
197 BeginTransformFeedback(); 196 BeginTransformFeedback();
198 197
199 UpdateDynamicStates(); 198 UpdateDynamicStates();
200 199
201 const auto& regs{maxwell3d->regs}; 200 const auto& regs{maxwell3d->regs};
202 const u32 num_instances{maxwell3d->mme_draw.instance_count}; 201 const u32 num_instances{instance_count};
203 const DrawParams draw_params{MakeDrawParams(regs, num_instances, is_instanced, is_indexed)}; 202 const DrawParams draw_params{MakeDrawParams(regs, num_instances, is_indexed)};
204 scheduler.Record([draw_params](vk::CommandBuffer cmdbuf) { 203 scheduler.Record([draw_params](vk::CommandBuffer cmdbuf) {
205 if (draw_params.is_indexed) { 204 if (draw_params.is_indexed) {
206 cmdbuf.DrawIndexed(draw_params.num_vertices, draw_params.num_instances, 205 cmdbuf.DrawIndexed(draw_params.num_vertices, draw_params.num_instances,
@@ -304,14 +303,19 @@ void RasterizerVulkan::Clear() {
304 } 303 }
305 } 304 }
306 305
307 scheduler.Record([color_attachment, clear_value, clear_rect](vk::CommandBuffer cmdbuf) { 306 if (regs.clear_surface.R && regs.clear_surface.G && regs.clear_surface.B &&
308 const VkClearAttachment attachment{ 307 regs.clear_surface.A) {
309 .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, 308 scheduler.Record([color_attachment, clear_value, clear_rect](vk::CommandBuffer cmdbuf) {
310 .colorAttachment = color_attachment, 309 const VkClearAttachment attachment{
311 .clearValue = clear_value, 310 .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
312 }; 311 .colorAttachment = color_attachment,
313 cmdbuf.ClearAttachments(attachment, clear_rect); 312 .clearValue = clear_value,
314 }); 313 };
314 cmdbuf.ClearAttachments(attachment, clear_rect);
315 });
316 } else {
317 UNIMPLEMENTED_MSG("Unimplemented Clear only the specified channel");
318 }
315 } 319 }
316 320
317 if (!use_depth && !use_stencil) { 321 if (!use_depth && !use_stencil) {
@@ -679,6 +683,22 @@ void RasterizerVulkan::UpdateViewportsState(Tegra::Engines::Maxwell3D::Regs& reg
679 if (!state_tracker.TouchViewports()) { 683 if (!state_tracker.TouchViewports()) {
680 return; 684 return;
681 } 685 }
686 if (!regs.viewport_scale_offset_enabled) {
687 const auto x = static_cast<float>(regs.surface_clip.x);
688 const auto y = static_cast<float>(regs.surface_clip.y);
689 const auto width = static_cast<float>(regs.surface_clip.width);
690 const auto height = static_cast<float>(regs.surface_clip.height);
691 VkViewport viewport{
692 .x = x,
693 .y = y,
694 .width = width != 0.0f ? width : 1.0f,
695 .height = height != 0.0f ? height : 1.0f,
696 .minDepth = 0.0f,
697 .maxDepth = 1.0f,
698 };
699 scheduler.Record([viewport](vk::CommandBuffer cmdbuf) { cmdbuf.SetViewport(0, viewport); });
700 return;
701 }
682 const bool is_rescaling{texture_cache.IsRescaling()}; 702 const bool is_rescaling{texture_cache.IsRescaling()};
683 const float scale = is_rescaling ? Settings::values.resolution_info.up_factor : 1.0f; 703 const float scale = is_rescaling ? Settings::values.resolution_info.up_factor : 1.0f;
684 const std::array viewports{ 704 const std::array viewports{
@@ -1009,4 +1029,17 @@ void RasterizerVulkan::ReleaseChannel(s32 channel_id) {
1009 query_cache.EraseChannel(channel_id); 1029 query_cache.EraseChannel(channel_id);
1010} 1030}
1011 1031
1032void RasterizerVulkan::BindInlineIndexBuffer() {
1033 if (maxwell3d->inline_index_draw_indexes.empty()) {
1034 return;
1035 }
1036 const auto data_count = static_cast<u32>(maxwell3d->inline_index_draw_indexes.size());
1037 auto buffer = buffer_cache_runtime.UploadStagingBuffer(data_count);
1038 std::memcpy(buffer.mapped_span.data(), maxwell3d->inline_index_draw_indexes.data(), data_count);
1039 buffer_cache_runtime.BindIndexBuffer(
1040 maxwell3d->regs.draw.topology, maxwell3d->regs.index_buffer.format,
1041 maxwell3d->regs.index_buffer.first, maxwell3d->regs.index_buffer.count, buffer.buffer,
1042 static_cast<u32>(buffer.offset), data_count);
1043}
1044
1012} // namespace Vulkan 1045} // namespace Vulkan
diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.h b/src/video_core/renderer_vulkan/vk_rasterizer.h
index 4cde3c983..b0bc306f5 100644
--- a/src/video_core/renderer_vulkan/vk_rasterizer.h
+++ b/src/video_core/renderer_vulkan/vk_rasterizer.h
@@ -64,7 +64,7 @@ public:
64 StateTracker& state_tracker_, Scheduler& scheduler_); 64 StateTracker& state_tracker_, Scheduler& scheduler_);
65 ~RasterizerVulkan() override; 65 ~RasterizerVulkan() override;
66 66
67 void Draw(bool is_indexed, bool is_instanced) override; 67 void Draw(bool is_indexed, u32 instance_count) override;
68 void Clear() override; 68 void Clear() override;
69 void DispatchCompute() override; 69 void DispatchCompute() override;
70 void ResetCounter(VideoCore::QueryType type) override; 70 void ResetCounter(VideoCore::QueryType type) override;
@@ -141,6 +141,8 @@ private:
141 141
142 void UpdateVertexInput(Tegra::Engines::Maxwell3D::Regs& regs); 142 void UpdateVertexInput(Tegra::Engines::Maxwell3D::Regs& regs);
143 143
144 void BindInlineIndexBuffer();
145
144 Tegra::GPU& gpu; 146 Tegra::GPU& gpu;
145 147
146 ScreenInfo& screen_info; 148 ScreenInfo& screen_info;
@@ -153,7 +155,6 @@ private:
153 DescriptorPool descriptor_pool; 155 DescriptorPool descriptor_pool;
154 UpdateDescriptorQueue update_descriptor_queue; 156 UpdateDescriptorQueue update_descriptor_queue;
155 BlitImageHelper blit_image; 157 BlitImageHelper blit_image;
156 ASTCDecoderPass astc_decoder_pass;
157 RenderPassCache render_pass_cache; 158 RenderPassCache render_pass_cache;
158 159
159 TextureCacheRuntime texture_cache_runtime; 160 TextureCacheRuntime texture_cache_runtime;
diff --git a/src/video_core/renderer_vulkan/vk_scheduler.h b/src/video_core/renderer_vulkan/vk_scheduler.h
index c04aad08f..929216749 100644
--- a/src/video_core/renderer_vulkan/vk_scheduler.h
+++ b/src/video_core/renderer_vulkan/vk_scheduler.h
@@ -144,7 +144,6 @@ private:
144 using FuncType = TypedCommand<T>; 144 using FuncType = TypedCommand<T>;
145 static_assert(sizeof(FuncType) < sizeof(data), "Lambda is too large"); 145 static_assert(sizeof(FuncType) < sizeof(data), "Lambda is too large");
146 146
147 recorded_counts++;
148 command_offset = Common::AlignUp(command_offset, alignof(FuncType)); 147 command_offset = Common::AlignUp(command_offset, alignof(FuncType));
149 if (command_offset > sizeof(data) - sizeof(FuncType)) { 148 if (command_offset > sizeof(data) - sizeof(FuncType)) {
150 return false; 149 return false;
@@ -166,7 +165,7 @@ private:
166 } 165 }
167 166
168 bool Empty() const { 167 bool Empty() const {
169 return recorded_counts == 0; 168 return command_offset == 0;
170 } 169 }
171 170
172 bool HasSubmit() const { 171 bool HasSubmit() const {
@@ -177,7 +176,6 @@ private:
177 Command* first = nullptr; 176 Command* first = nullptr;
178 Command* last = nullptr; 177 Command* last = nullptr;
179 178
180 size_t recorded_counts = 0;
181 size_t command_offset = 0; 179 size_t command_offset = 0;
182 bool submit = false; 180 bool submit = false;
183 alignas(std::max_align_t) std::array<u8, 0x8000> data{}; 181 alignas(std::max_align_t) std::array<u8, 0x8000> data{};
diff --git a/src/video_core/renderer_vulkan/vk_state_tracker.cpp b/src/video_core/renderer_vulkan/vk_state_tracker.cpp
index b87c3be66..edb41b171 100644
--- a/src/video_core/renderer_vulkan/vk_state_tracker.cpp
+++ b/src/video_core/renderer_vulkan/vk_state_tracker.cpp
@@ -51,7 +51,7 @@ Flags MakeInvalidationFlags() {
51void SetupDirtyViewports(Tables& tables) { 51void SetupDirtyViewports(Tables& tables) {
52 FillBlock(tables[0], OFF(viewport_transform), NUM(viewport_transform), Viewports); 52 FillBlock(tables[0], OFF(viewport_transform), NUM(viewport_transform), Viewports);
53 FillBlock(tables[0], OFF(viewports), NUM(viewports), Viewports); 53 FillBlock(tables[0], OFF(viewports), NUM(viewports), Viewports);
54 tables[0][OFF(viewport_scale_offset_enbled)] = Viewports; 54 tables[0][OFF(viewport_scale_offset_enabled)] = Viewports;
55 tables[1][OFF(window_origin)] = Viewports; 55 tables[1][OFF(window_origin)] = Viewports;
56} 56}
57 57
diff --git a/src/video_core/renderer_vulkan/vk_texture_cache.cpp b/src/video_core/renderer_vulkan/vk_texture_cache.cpp
index 305ad8aee..853b80d8a 100644
--- a/src/video_core/renderer_vulkan/vk_texture_cache.cpp
+++ b/src/video_core/renderer_vulkan/vk_texture_cache.cpp
@@ -791,12 +791,17 @@ TextureCacheRuntime::TextureCacheRuntime(const Device& device_, Scheduler& sched
791 MemoryAllocator& memory_allocator_, 791 MemoryAllocator& memory_allocator_,
792 StagingBufferPool& staging_buffer_pool_, 792 StagingBufferPool& staging_buffer_pool_,
793 BlitImageHelper& blit_image_helper_, 793 BlitImageHelper& blit_image_helper_,
794 ASTCDecoderPass& astc_decoder_pass_, 794 RenderPassCache& render_pass_cache_,
795 RenderPassCache& render_pass_cache_) 795 DescriptorPool& descriptor_pool,
796 UpdateDescriptorQueue& update_descriptor_queue)
796 : device{device_}, scheduler{scheduler_}, memory_allocator{memory_allocator_}, 797 : device{device_}, scheduler{scheduler_}, memory_allocator{memory_allocator_},
797 staging_buffer_pool{staging_buffer_pool_}, blit_image_helper{blit_image_helper_}, 798 staging_buffer_pool{staging_buffer_pool_}, blit_image_helper{blit_image_helper_},
798 astc_decoder_pass{astc_decoder_pass_}, render_pass_cache{render_pass_cache_}, 799 render_pass_cache{render_pass_cache_}, resolution{Settings::values.resolution_info} {
799 resolution{Settings::values.resolution_info} {} 800 if (Settings::values.accelerate_astc) {
801 astc_decoder_pass.emplace(device, scheduler, descriptor_pool, staging_buffer_pool,
802 update_descriptor_queue, memory_allocator);
803 }
804}
800 805
801void TextureCacheRuntime::Finish() { 806void TextureCacheRuntime::Finish() {
802 scheduler.Finish(); 807 scheduler.Finish();
@@ -1782,17 +1787,17 @@ void Framebuffer::CreateFramebuffer(TextureCacheRuntime& runtime,
1782 1787
1783 const auto& resolution = runtime.resolution; 1788 const auto& resolution = runtime.resolution;
1784 1789
1785 u32 width = 0; 1790 u32 width = std::numeric_limits<u32>::max();
1786 u32 height = 0; 1791 u32 height = std::numeric_limits<u32>::max();
1787 for (size_t index = 0; index < NUM_RT; ++index) { 1792 for (size_t index = 0; index < NUM_RT; ++index) {
1788 const ImageView* const color_buffer = color_buffers[index]; 1793 const ImageView* const color_buffer = color_buffers[index];
1789 if (!color_buffer) { 1794 if (!color_buffer) {
1790 renderpass_key.color_formats[index] = PixelFormat::Invalid; 1795 renderpass_key.color_formats[index] = PixelFormat::Invalid;
1791 continue; 1796 continue;
1792 } 1797 }
1793 width = std::max(width, is_rescaled ? resolution.ScaleUp(color_buffer->size.width) 1798 width = std::min(width, is_rescaled ? resolution.ScaleUp(color_buffer->size.width)
1794 : color_buffer->size.width); 1799 : color_buffer->size.width);
1795 height = std::max(height, is_rescaled ? resolution.ScaleUp(color_buffer->size.height) 1800 height = std::min(height, is_rescaled ? resolution.ScaleUp(color_buffer->size.height)
1796 : color_buffer->size.height); 1801 : color_buffer->size.height);
1797 attachments.push_back(color_buffer->RenderTarget()); 1802 attachments.push_back(color_buffer->RenderTarget());
1798 renderpass_key.color_formats[index] = color_buffer->format; 1803 renderpass_key.color_formats[index] = color_buffer->format;
@@ -1804,9 +1809,9 @@ void Framebuffer::CreateFramebuffer(TextureCacheRuntime& runtime,
1804 } 1809 }
1805 const size_t num_colors = attachments.size(); 1810 const size_t num_colors = attachments.size();
1806 if (depth_buffer) { 1811 if (depth_buffer) {
1807 width = std::max(width, is_rescaled ? resolution.ScaleUp(depth_buffer->size.width) 1812 width = std::min(width, is_rescaled ? resolution.ScaleUp(depth_buffer->size.width)
1808 : depth_buffer->size.width); 1813 : depth_buffer->size.width);
1809 height = std::max(height, is_rescaled ? resolution.ScaleUp(depth_buffer->size.height) 1814 height = std::min(height, is_rescaled ? resolution.ScaleUp(depth_buffer->size.height)
1810 : depth_buffer->size.height); 1815 : depth_buffer->size.height);
1811 attachments.push_back(depth_buffer->RenderTarget()); 1816 attachments.push_back(depth_buffer->RenderTarget());
1812 renderpass_key.depth_format = depth_buffer->format; 1817 renderpass_key.depth_format = depth_buffer->format;
@@ -1845,7 +1850,7 @@ void TextureCacheRuntime::AccelerateImageUpload(
1845 Image& image, const StagingBufferRef& map, 1850 Image& image, const StagingBufferRef& map,
1846 std::span<const VideoCommon::SwizzleParameters> swizzles) { 1851 std::span<const VideoCommon::SwizzleParameters> swizzles) {
1847 if (IsPixelFormatASTC(image.info.format)) { 1852 if (IsPixelFormatASTC(image.info.format)) {
1848 return astc_decoder_pass.Assemble(image, map, swizzles); 1853 return astc_decoder_pass->Assemble(image, map, swizzles);
1849 } 1854 }
1850 ASSERT(false); 1855 ASSERT(false);
1851} 1856}
diff --git a/src/video_core/renderer_vulkan/vk_texture_cache.h b/src/video_core/renderer_vulkan/vk_texture_cache.h
index 0b7ac0df1..7ec0df134 100644
--- a/src/video_core/renderer_vulkan/vk_texture_cache.h
+++ b/src/video_core/renderer_vulkan/vk_texture_cache.h
@@ -6,6 +6,7 @@
6#include <span> 6#include <span>
7 7
8#include "shader_recompiler/shader_info.h" 8#include "shader_recompiler/shader_info.h"
9#include "video_core/renderer_vulkan/vk_compute_pass.h"
9#include "video_core/renderer_vulkan/vk_staging_buffer_pool.h" 10#include "video_core/renderer_vulkan/vk_staging_buffer_pool.h"
10#include "video_core/texture_cache/image_view_base.h" 11#include "video_core/texture_cache/image_view_base.h"
11#include "video_core/texture_cache/texture_cache_base.h" 12#include "video_core/texture_cache/texture_cache_base.h"
@@ -25,14 +26,15 @@ using VideoCommon::RenderTargets;
25using VideoCommon::SlotVector; 26using VideoCommon::SlotVector;
26using VideoCore::Surface::PixelFormat; 27using VideoCore::Surface::PixelFormat;
27 28
28class ASTCDecoderPass;
29class BlitImageHelper; 29class BlitImageHelper;
30class DescriptorPool;
30class Device; 31class Device;
31class Image; 32class Image;
32class ImageView; 33class ImageView;
33class Framebuffer; 34class Framebuffer;
34class RenderPassCache; 35class RenderPassCache;
35class StagingBufferPool; 36class StagingBufferPool;
37class UpdateDescriptorQueue;
36class Scheduler; 38class Scheduler;
37 39
38class TextureCacheRuntime { 40class TextureCacheRuntime {
@@ -41,8 +43,9 @@ public:
41 MemoryAllocator& memory_allocator_, 43 MemoryAllocator& memory_allocator_,
42 StagingBufferPool& staging_buffer_pool_, 44 StagingBufferPool& staging_buffer_pool_,
43 BlitImageHelper& blit_image_helper_, 45 BlitImageHelper& blit_image_helper_,
44 ASTCDecoderPass& astc_decoder_pass_, 46 RenderPassCache& render_pass_cache_,
45 RenderPassCache& render_pass_cache_); 47 DescriptorPool& descriptor_pool,
48 UpdateDescriptorQueue& update_descriptor_queue);
46 49
47 void Finish(); 50 void Finish();
48 51
@@ -97,8 +100,8 @@ public:
97 MemoryAllocator& memory_allocator; 100 MemoryAllocator& memory_allocator;
98 StagingBufferPool& staging_buffer_pool; 101 StagingBufferPool& staging_buffer_pool;
99 BlitImageHelper& blit_image_helper; 102 BlitImageHelper& blit_image_helper;
100 ASTCDecoderPass& astc_decoder_pass;
101 RenderPassCache& render_pass_cache; 103 RenderPassCache& render_pass_cache;
104 std::optional<ASTCDecoderPass> astc_decoder_pass;
102 const Settings::ResolutionScalingInfo& resolution; 105 const Settings::ResolutionScalingInfo& resolution;
103 106
104 constexpr static size_t indexing_slots = 8 * sizeof(size_t); 107 constexpr static size_t indexing_slots = 8 * sizeof(size_t);
diff --git a/src/video_core/shader_environment.cpp b/src/video_core/shader_environment.cpp
index fbabb3219..f24f320b6 100644
--- a/src/video_core/shader_environment.cpp
+++ b/src/video_core/shader_environment.cpp
@@ -19,6 +19,7 @@
19#include "video_core/engines/kepler_compute.h" 19#include "video_core/engines/kepler_compute.h"
20#include "video_core/memory_manager.h" 20#include "video_core/memory_manager.h"
21#include "video_core/shader_environment.h" 21#include "video_core/shader_environment.h"
22#include "video_core/texture_cache/format_lookup_table.h"
22#include "video_core/textures/texture.h" 23#include "video_core/textures/texture.h"
23 24
24namespace VideoCommon { 25namespace VideoCommon {
@@ -33,7 +34,7 @@ static u64 MakeCbufKey(u32 index, u32 offset) {
33 return (static_cast<u64>(index) << 32) | offset; 34 return (static_cast<u64>(index) << 32) | offset;
34} 35}
35 36
36static Shader::TextureType ConvertType(const Tegra::Texture::TICEntry& entry) { 37static Shader::TextureType ConvertTextureType(const Tegra::Texture::TICEntry& entry) {
37 switch (entry.texture_type) { 38 switch (entry.texture_type) {
38 case Tegra::Texture::TextureType::Texture1D: 39 case Tegra::Texture::TextureType::Texture1D:
39 return Shader::TextureType::Color1D; 40 return Shader::TextureType::Color1D;
@@ -59,6 +60,26 @@ static Shader::TextureType ConvertType(const Tegra::Texture::TICEntry& entry) {
59 } 60 }
60} 61}
61 62
63static Shader::TexturePixelFormat ConvertTexturePixelFormat(const Tegra::Texture::TICEntry& entry) {
64 switch (PixelFormatFromTextureInfo(entry.format, entry.r_type, entry.g_type, entry.b_type,
65 entry.a_type, entry.srgb_conversion)) {
66 case VideoCore::Surface::PixelFormat::A8B8G8R8_SNORM:
67 return Shader::TexturePixelFormat::A8B8G8R8_SNORM;
68 case VideoCore::Surface::PixelFormat::R8_SNORM:
69 return Shader::TexturePixelFormat::R8_SNORM;
70 case VideoCore::Surface::PixelFormat::R8G8_SNORM:
71 return Shader::TexturePixelFormat::R8G8_SNORM;
72 case VideoCore::Surface::PixelFormat::R16G16B16A16_SNORM:
73 return Shader::TexturePixelFormat::R16G16B16A16_SNORM;
74 case VideoCore::Surface::PixelFormat::R16G16_SNORM:
75 return Shader::TexturePixelFormat::R16G16_SNORM;
76 case VideoCore::Surface::PixelFormat::R16_SNORM:
77 return Shader::TexturePixelFormat::R16_SNORM;
78 default:
79 return Shader::TexturePixelFormat::OTHER;
80 }
81}
82
62static std::string_view StageToPrefix(Shader::Stage stage) { 83static std::string_view StageToPrefix(Shader::Stage stage) {
63 switch (stage) { 84 switch (stage) {
64 case Shader::Stage::VertexB: 85 case Shader::Stage::VertexB:
@@ -178,22 +199,31 @@ void GenericEnvironment::Dump(u64 hash) {
178void GenericEnvironment::Serialize(std::ofstream& file) const { 199void GenericEnvironment::Serialize(std::ofstream& file) const {
179 const u64 code_size{static_cast<u64>(CachedSize())}; 200 const u64 code_size{static_cast<u64>(CachedSize())};
180 const u64 num_texture_types{static_cast<u64>(texture_types.size())}; 201 const u64 num_texture_types{static_cast<u64>(texture_types.size())};
202 const u64 num_texture_pixel_formats{static_cast<u64>(texture_pixel_formats.size())};
181 const u64 num_cbuf_values{static_cast<u64>(cbuf_values.size())}; 203 const u64 num_cbuf_values{static_cast<u64>(cbuf_values.size())};
182 204
183 file.write(reinterpret_cast<const char*>(&code_size), sizeof(code_size)) 205 file.write(reinterpret_cast<const char*>(&code_size), sizeof(code_size))
184 .write(reinterpret_cast<const char*>(&num_texture_types), sizeof(num_texture_types)) 206 .write(reinterpret_cast<const char*>(&num_texture_types), sizeof(num_texture_types))
207 .write(reinterpret_cast<const char*>(&num_texture_pixel_formats),
208 sizeof(num_texture_pixel_formats))
185 .write(reinterpret_cast<const char*>(&num_cbuf_values), sizeof(num_cbuf_values)) 209 .write(reinterpret_cast<const char*>(&num_cbuf_values), sizeof(num_cbuf_values))
186 .write(reinterpret_cast<const char*>(&local_memory_size), sizeof(local_memory_size)) 210 .write(reinterpret_cast<const char*>(&local_memory_size), sizeof(local_memory_size))
187 .write(reinterpret_cast<const char*>(&texture_bound), sizeof(texture_bound)) 211 .write(reinterpret_cast<const char*>(&texture_bound), sizeof(texture_bound))
188 .write(reinterpret_cast<const char*>(&start_address), sizeof(start_address)) 212 .write(reinterpret_cast<const char*>(&start_address), sizeof(start_address))
189 .write(reinterpret_cast<const char*>(&cached_lowest), sizeof(cached_lowest)) 213 .write(reinterpret_cast<const char*>(&cached_lowest), sizeof(cached_lowest))
190 .write(reinterpret_cast<const char*>(&cached_highest), sizeof(cached_highest)) 214 .write(reinterpret_cast<const char*>(&cached_highest), sizeof(cached_highest))
215 .write(reinterpret_cast<const char*>(&viewport_transform_state),
216 sizeof(viewport_transform_state))
191 .write(reinterpret_cast<const char*>(&stage), sizeof(stage)) 217 .write(reinterpret_cast<const char*>(&stage), sizeof(stage))
192 .write(reinterpret_cast<const char*>(code.data()), code_size); 218 .write(reinterpret_cast<const char*>(code.data()), code_size);
193 for (const auto& [key, type] : texture_types) { 219 for (const auto& [key, type] : texture_types) {
194 file.write(reinterpret_cast<const char*>(&key), sizeof(key)) 220 file.write(reinterpret_cast<const char*>(&key), sizeof(key))
195 .write(reinterpret_cast<const char*>(&type), sizeof(type)); 221 .write(reinterpret_cast<const char*>(&type), sizeof(type));
196 } 222 }
223 for (const auto& [key, format] : texture_pixel_formats) {
224 file.write(reinterpret_cast<const char*>(&key), sizeof(key))
225 .write(reinterpret_cast<const char*>(&format), sizeof(format));
226 }
197 for (const auto& [key, type] : cbuf_values) { 227 for (const auto& [key, type] : cbuf_values) {
198 file.write(reinterpret_cast<const char*>(&key), sizeof(key)) 228 file.write(reinterpret_cast<const char*>(&key), sizeof(key))
199 .write(reinterpret_cast<const char*>(&type), sizeof(type)); 229 .write(reinterpret_cast<const char*>(&type), sizeof(type));
@@ -237,15 +267,13 @@ std::optional<u64> GenericEnvironment::TryFindSize() {
237 return std::nullopt; 267 return std::nullopt;
238} 268}
239 269
240Shader::TextureType GenericEnvironment::ReadTextureTypeImpl(GPUVAddr tic_addr, u32 tic_limit, 270Tegra::Texture::TICEntry GenericEnvironment::ReadTextureInfo(GPUVAddr tic_addr, u32 tic_limit,
241 bool via_header_index, u32 raw) { 271 bool via_header_index, u32 raw) {
242 const auto handle{Tegra::Texture::TexturePair(raw, via_header_index)}; 272 const auto handle{Tegra::Texture::TexturePair(raw, via_header_index)};
243 const GPUVAddr descriptor_addr{tic_addr + handle.first * sizeof(Tegra::Texture::TICEntry)}; 273 const GPUVAddr descriptor_addr{tic_addr + handle.first * sizeof(Tegra::Texture::TICEntry)};
244 Tegra::Texture::TICEntry entry; 274 Tegra::Texture::TICEntry entry;
245 gpu_memory->ReadBlock(descriptor_addr, &entry, sizeof(entry)); 275 gpu_memory->ReadBlock(descriptor_addr, &entry, sizeof(entry));
246 const Shader::TextureType result{ConvertType(entry)}; 276 return entry;
247 texture_types.emplace(raw, result);
248 return result;
249} 277}
250 278
251GraphicsEnvironment::GraphicsEnvironment(Tegra::Engines::Maxwell3D& maxwell3d_, 279GraphicsEnvironment::GraphicsEnvironment(Tegra::Engines::Maxwell3D& maxwell3d_,
@@ -305,8 +333,27 @@ u32 GraphicsEnvironment::ReadCbufValue(u32 cbuf_index, u32 cbuf_offset) {
305Shader::TextureType GraphicsEnvironment::ReadTextureType(u32 handle) { 333Shader::TextureType GraphicsEnvironment::ReadTextureType(u32 handle) {
306 const auto& regs{maxwell3d->regs}; 334 const auto& regs{maxwell3d->regs};
307 const bool via_header_index{regs.sampler_binding == Maxwell::SamplerBinding::ViaHeaderBinding}; 335 const bool via_header_index{regs.sampler_binding == Maxwell::SamplerBinding::ViaHeaderBinding};
308 return ReadTextureTypeImpl(regs.tex_header.Address(), regs.tex_header.limit, via_header_index, 336 auto entry =
309 handle); 337 ReadTextureInfo(regs.tex_header.Address(), regs.tex_header.limit, via_header_index, handle);
338 const Shader::TextureType result{ConvertTextureType(entry)};
339 texture_types.emplace(handle, result);
340 return result;
341}
342
343Shader::TexturePixelFormat GraphicsEnvironment::ReadTexturePixelFormat(u32 handle) {
344 const auto& regs{maxwell3d->regs};
345 const bool via_header_index{regs.sampler_binding == Maxwell::SamplerBinding::ViaHeaderBinding};
346 auto entry =
347 ReadTextureInfo(regs.tex_header.Address(), regs.tex_header.limit, via_header_index, handle);
348 const Shader::TexturePixelFormat result(ConvertTexturePixelFormat(entry));
349 texture_pixel_formats.emplace(handle, result);
350 return result;
351}
352
353u32 GraphicsEnvironment::ReadViewportTransformState() {
354 const auto& regs{maxwell3d->regs};
355 viewport_transform_state = regs.viewport_scale_offset_enabled;
356 return viewport_transform_state;
310} 357}
311 358
312ComputeEnvironment::ComputeEnvironment(Tegra::Engines::KeplerCompute& kepler_compute_, 359ComputeEnvironment::ComputeEnvironment(Tegra::Engines::KeplerCompute& kepler_compute_,
@@ -337,21 +384,41 @@ u32 ComputeEnvironment::ReadCbufValue(u32 cbuf_index, u32 cbuf_offset) {
337Shader::TextureType ComputeEnvironment::ReadTextureType(u32 handle) { 384Shader::TextureType ComputeEnvironment::ReadTextureType(u32 handle) {
338 const auto& regs{kepler_compute->regs}; 385 const auto& regs{kepler_compute->regs};
339 const auto& qmd{kepler_compute->launch_description}; 386 const auto& qmd{kepler_compute->launch_description};
340 return ReadTextureTypeImpl(regs.tic.Address(), regs.tic.limit, qmd.linked_tsc != 0, handle); 387 auto entry = ReadTextureInfo(regs.tic.Address(), regs.tic.limit, qmd.linked_tsc != 0, handle);
388 const Shader::TextureType result{ConvertTextureType(entry)};
389 texture_types.emplace(handle, result);
390 return result;
391}
392
393Shader::TexturePixelFormat ComputeEnvironment::ReadTexturePixelFormat(u32 handle) {
394 const auto& regs{kepler_compute->regs};
395 const auto& qmd{kepler_compute->launch_description};
396 auto entry = ReadTextureInfo(regs.tic.Address(), regs.tic.limit, qmd.linked_tsc != 0, handle);
397 const Shader::TexturePixelFormat result(ConvertTexturePixelFormat(entry));
398 texture_pixel_formats.emplace(handle, result);
399 return result;
400}
401
402u32 ComputeEnvironment::ReadViewportTransformState() {
403 return viewport_transform_state;
341} 404}
342 405
343void FileEnvironment::Deserialize(std::ifstream& file) { 406void FileEnvironment::Deserialize(std::ifstream& file) {
344 u64 code_size{}; 407 u64 code_size{};
345 u64 num_texture_types{}; 408 u64 num_texture_types{};
409 u64 num_texture_pixel_formats{};
346 u64 num_cbuf_values{}; 410 u64 num_cbuf_values{};
347 file.read(reinterpret_cast<char*>(&code_size), sizeof(code_size)) 411 file.read(reinterpret_cast<char*>(&code_size), sizeof(code_size))
348 .read(reinterpret_cast<char*>(&num_texture_types), sizeof(num_texture_types)) 412 .read(reinterpret_cast<char*>(&num_texture_types), sizeof(num_texture_types))
413 .read(reinterpret_cast<char*>(&num_texture_pixel_formats),
414 sizeof(num_texture_pixel_formats))
349 .read(reinterpret_cast<char*>(&num_cbuf_values), sizeof(num_cbuf_values)) 415 .read(reinterpret_cast<char*>(&num_cbuf_values), sizeof(num_cbuf_values))
350 .read(reinterpret_cast<char*>(&local_memory_size), sizeof(local_memory_size)) 416 .read(reinterpret_cast<char*>(&local_memory_size), sizeof(local_memory_size))
351 .read(reinterpret_cast<char*>(&texture_bound), sizeof(texture_bound)) 417 .read(reinterpret_cast<char*>(&texture_bound), sizeof(texture_bound))
352 .read(reinterpret_cast<char*>(&start_address), sizeof(start_address)) 418 .read(reinterpret_cast<char*>(&start_address), sizeof(start_address))
353 .read(reinterpret_cast<char*>(&read_lowest), sizeof(read_lowest)) 419 .read(reinterpret_cast<char*>(&read_lowest), sizeof(read_lowest))
354 .read(reinterpret_cast<char*>(&read_highest), sizeof(read_highest)) 420 .read(reinterpret_cast<char*>(&read_highest), sizeof(read_highest))
421 .read(reinterpret_cast<char*>(&viewport_transform_state), sizeof(viewport_transform_state))
355 .read(reinterpret_cast<char*>(&stage), sizeof(stage)); 422 .read(reinterpret_cast<char*>(&stage), sizeof(stage));
356 code = std::make_unique<u64[]>(Common::DivCeil(code_size, sizeof(u64))); 423 code = std::make_unique<u64[]>(Common::DivCeil(code_size, sizeof(u64)));
357 file.read(reinterpret_cast<char*>(code.get()), code_size); 424 file.read(reinterpret_cast<char*>(code.get()), code_size);
@@ -362,6 +429,13 @@ void FileEnvironment::Deserialize(std::ifstream& file) {
362 .read(reinterpret_cast<char*>(&type), sizeof(type)); 429 .read(reinterpret_cast<char*>(&type), sizeof(type));
363 texture_types.emplace(key, type); 430 texture_types.emplace(key, type);
364 } 431 }
432 for (size_t i = 0; i < num_texture_pixel_formats; ++i) {
433 u32 key;
434 Shader::TexturePixelFormat format;
435 file.read(reinterpret_cast<char*>(&key), sizeof(key))
436 .read(reinterpret_cast<char*>(&format), sizeof(format));
437 texture_pixel_formats.emplace(key, format);
438 }
365 for (size_t i = 0; i < num_cbuf_values; ++i) { 439 for (size_t i = 0; i < num_cbuf_values; ++i) {
366 u64 key; 440 u64 key;
367 u32 value; 441 u32 value;
@@ -409,6 +483,18 @@ Shader::TextureType FileEnvironment::ReadTextureType(u32 handle) {
409 return it->second; 483 return it->second;
410} 484}
411 485
486Shader::TexturePixelFormat FileEnvironment::ReadTexturePixelFormat(u32 handle) {
487 const auto it{texture_pixel_formats.find(handle)};
488 if (it == texture_pixel_formats.end()) {
489 throw Shader::LogicError("Uncached read texture pixel format");
490 }
491 return it->second;
492}
493
494u32 FileEnvironment::ReadViewportTransformState() {
495 return viewport_transform_state;
496}
497
412u32 FileEnvironment::LocalMemorySize() const { 498u32 FileEnvironment::LocalMemorySize() const {
413 return local_memory_size; 499 return local_memory_size;
414} 500}
diff --git a/src/video_core/shader_environment.h b/src/video_core/shader_environment.h
index 8b3b8e9f5..bb55b029f 100644
--- a/src/video_core/shader_environment.h
+++ b/src/video_core/shader_environment.h
@@ -63,14 +63,15 @@ public:
63protected: 63protected:
64 std::optional<u64> TryFindSize(); 64 std::optional<u64> TryFindSize();
65 65
66 Shader::TextureType ReadTextureTypeImpl(GPUVAddr tic_addr, u32 tic_limit, bool via_header_index, 66 Tegra::Texture::TICEntry ReadTextureInfo(GPUVAddr tic_addr, u32 tic_limit,
67 u32 raw); 67 bool via_header_index, u32 raw);
68 68
69 Tegra::MemoryManager* gpu_memory{}; 69 Tegra::MemoryManager* gpu_memory{};
70 GPUVAddr program_base{}; 70 GPUVAddr program_base{};
71 71
72 std::vector<u64> code; 72 std::vector<u64> code;
73 std::unordered_map<u32, Shader::TextureType> texture_types; 73 std::unordered_map<u32, Shader::TextureType> texture_types;
74 std::unordered_map<u32, Shader::TexturePixelFormat> texture_pixel_formats;
74 std::unordered_map<u64, u32> cbuf_values; 75 std::unordered_map<u64, u32> cbuf_values;
75 76
76 u32 local_memory_size{}; 77 u32 local_memory_size{};
@@ -85,6 +86,8 @@ protected:
85 u32 cached_highest = 0; 86 u32 cached_highest = 0;
86 u32 initial_offset = 0; 87 u32 initial_offset = 0;
87 88
89 u32 viewport_transform_state = 1;
90
88 bool has_unbound_instructions = false; 91 bool has_unbound_instructions = false;
89}; 92};
90 93
@@ -102,6 +105,10 @@ public:
102 105
103 Shader::TextureType ReadTextureType(u32 handle) override; 106 Shader::TextureType ReadTextureType(u32 handle) override;
104 107
108 Shader::TexturePixelFormat ReadTexturePixelFormat(u32 handle) override;
109
110 u32 ReadViewportTransformState() override;
111
105private: 112private:
106 Tegra::Engines::Maxwell3D* maxwell3d{}; 113 Tegra::Engines::Maxwell3D* maxwell3d{};
107 size_t stage_index{}; 114 size_t stage_index{};
@@ -120,6 +127,10 @@ public:
120 127
121 Shader::TextureType ReadTextureType(u32 handle) override; 128 Shader::TextureType ReadTextureType(u32 handle) override;
122 129
130 Shader::TexturePixelFormat ReadTexturePixelFormat(u32 handle) override;
131
132 u32 ReadViewportTransformState() override;
133
123private: 134private:
124 Tegra::Engines::KeplerCompute* kepler_compute{}; 135 Tegra::Engines::KeplerCompute* kepler_compute{};
125}; 136};
@@ -143,6 +154,10 @@ public:
143 154
144 [[nodiscard]] Shader::TextureType ReadTextureType(u32 handle) override; 155 [[nodiscard]] Shader::TextureType ReadTextureType(u32 handle) override;
145 156
157 [[nodiscard]] Shader::TexturePixelFormat ReadTexturePixelFormat(u32 handle) override;
158
159 [[nodiscard]] u32 ReadViewportTransformState() override;
160
146 [[nodiscard]] u32 LocalMemorySize() const override; 161 [[nodiscard]] u32 LocalMemorySize() const override;
147 162
148 [[nodiscard]] u32 SharedMemorySize() const override; 163 [[nodiscard]] u32 SharedMemorySize() const override;
@@ -156,6 +171,7 @@ public:
156private: 171private:
157 std::unique_ptr<u64[]> code; 172 std::unique_ptr<u64[]> code;
158 std::unordered_map<u32, Shader::TextureType> texture_types; 173 std::unordered_map<u32, Shader::TextureType> texture_types;
174 std::unordered_map<u32, Shader::TexturePixelFormat> texture_pixel_formats;
159 std::unordered_map<u64, u32> cbuf_values; 175 std::unordered_map<u64, u32> cbuf_values;
160 std::array<u32, 3> workgroup_size{}; 176 std::array<u32, 3> workgroup_size{};
161 u32 local_memory_size{}; 177 u32 local_memory_size{};
@@ -164,6 +180,7 @@ private:
164 u32 read_lowest{}; 180 u32 read_lowest{};
165 u32 read_highest{}; 181 u32 read_highest{};
166 u32 initial_offset{}; 182 u32 initial_offset{};
183 u32 viewport_transform_state = 1;
167}; 184};
168 185
169void SerializePipeline(std::span<const char> key, std::span<const GenericEnvironment* const> envs, 186void SerializePipeline(std::span<const char> key, std::span<const GenericEnvironment* const> envs,
diff --git a/src/video_core/texture_cache/util.cpp b/src/video_core/texture_cache/util.cpp
index 1223df5a0..e8c908b42 100644
--- a/src/video_core/texture_cache/util.cpp
+++ b/src/video_core/texture_cache/util.cpp
@@ -516,7 +516,6 @@ void SwizzleBlockLinearImage(Tegra::MemoryManager& gpu_memory, GPUVAddr gpu_addr
516 const u32 num_blocks_per_layer = NumBlocks(level_size, tile_size); 516 const u32 num_blocks_per_layer = NumBlocks(level_size, tile_size);
517 const u32 host_bytes_per_layer = num_blocks_per_layer * bytes_per_block; 517 const u32 host_bytes_per_layer = num_blocks_per_layer * bytes_per_block;
518 518
519 UNIMPLEMENTED_IF(info.tile_width_spacing > 0);
520 UNIMPLEMENTED_IF(copy.image_offset.x != 0); 519 UNIMPLEMENTED_IF(copy.image_offset.x != 0);
521 UNIMPLEMENTED_IF(copy.image_offset.y != 0); 520 UNIMPLEMENTED_IF(copy.image_offset.y != 0);
522 UNIMPLEMENTED_IF(copy.image_offset.z != 0); 521 UNIMPLEMENTED_IF(copy.image_offset.z != 0);
diff --git a/src/yuzu/CMakeLists.txt b/src/yuzu/CMakeLists.txt
index 29d506c47..5cc1fbf32 100644
--- a/src/yuzu/CMakeLists.txt
+++ b/src/yuzu/CMakeLists.txt
@@ -315,7 +315,7 @@ target_include_directories(yuzu PRIVATE ../../externals/Vulkan-Headers/include)
315if (NOT WIN32) 315if (NOT WIN32)
316 target_include_directories(yuzu PRIVATE ${Qt5Gui_PRIVATE_INCLUDE_DIRS}) 316 target_include_directories(yuzu PRIVATE ${Qt5Gui_PRIVATE_INCLUDE_DIRS})
317endif() 317endif()
318if (${CMAKE_SYSTEM_NAME} STREQUAL "Linux") 318if (UNIX AND NOT APPLE)
319 target_link_libraries(yuzu PRIVATE Qt::DBus) 319 target_link_libraries(yuzu PRIVATE Qt::DBus)
320endif() 320endif()
321 321
@@ -385,6 +385,6 @@ if (NOT APPLE)
385 target_compile_definitions(yuzu PRIVATE HAS_OPENGL) 385 target_compile_definitions(yuzu PRIVATE HAS_OPENGL)
386endif() 386endif()
387 387
388if (ARCHITECTURE_x86_64) 388if (ARCHITECTURE_x86_64 OR ARCHITECTURE_arm64)
389 target_link_libraries(yuzu PRIVATE dynarmic) 389 target_link_libraries(yuzu PRIVATE dynarmic)
390endif() 390endif()
diff --git a/src/yuzu/bootmanager.cpp b/src/yuzu/bootmanager.cpp
index 6acfb7b06..d88efacd7 100644
--- a/src/yuzu/bootmanager.cpp
+++ b/src/yuzu/bootmanager.cpp
@@ -401,224 +401,127 @@ void GRenderWindow::closeEvent(QCloseEvent* event) {
401} 401}
402 402
403int GRenderWindow::QtKeyToSwitchKey(Qt::Key qt_key) { 403int GRenderWindow::QtKeyToSwitchKey(Qt::Key qt_key) {
404 switch (qt_key) { 404 static constexpr std::array<std::pair<Qt::Key, Settings::NativeKeyboard::Keys>, 106> key_map = {
405 case Qt::Key_A: 405 std::pair<Qt::Key, Settings::NativeKeyboard::Keys>{Qt::Key_A, Settings::NativeKeyboard::A},
406 return Settings::NativeKeyboard::A; 406 {Qt::Key_A, Settings::NativeKeyboard::A},
407 case Qt::Key_B: 407 {Qt::Key_B, Settings::NativeKeyboard::B},
408 return Settings::NativeKeyboard::B; 408 {Qt::Key_C, Settings::NativeKeyboard::C},
409 case Qt::Key_C: 409 {Qt::Key_D, Settings::NativeKeyboard::D},
410 return Settings::NativeKeyboard::C; 410 {Qt::Key_E, Settings::NativeKeyboard::E},
411 case Qt::Key_D: 411 {Qt::Key_F, Settings::NativeKeyboard::F},
412 return Settings::NativeKeyboard::D; 412 {Qt::Key_G, Settings::NativeKeyboard::G},
413 case Qt::Key_E: 413 {Qt::Key_H, Settings::NativeKeyboard::H},
414 return Settings::NativeKeyboard::E; 414 {Qt::Key_I, Settings::NativeKeyboard::I},
415 case Qt::Key_F: 415 {Qt::Key_J, Settings::NativeKeyboard::J},
416 return Settings::NativeKeyboard::F; 416 {Qt::Key_K, Settings::NativeKeyboard::K},
417 case Qt::Key_G: 417 {Qt::Key_L, Settings::NativeKeyboard::L},
418 return Settings::NativeKeyboard::G; 418 {Qt::Key_M, Settings::NativeKeyboard::M},
419 case Qt::Key_H: 419 {Qt::Key_N, Settings::NativeKeyboard::N},
420 return Settings::NativeKeyboard::H; 420 {Qt::Key_O, Settings::NativeKeyboard::O},
421 case Qt::Key_I: 421 {Qt::Key_P, Settings::NativeKeyboard::P},
422 return Settings::NativeKeyboard::I; 422 {Qt::Key_Q, Settings::NativeKeyboard::Q},
423 case Qt::Key_J: 423 {Qt::Key_R, Settings::NativeKeyboard::R},
424 return Settings::NativeKeyboard::J; 424 {Qt::Key_S, Settings::NativeKeyboard::S},
425 case Qt::Key_K: 425 {Qt::Key_T, Settings::NativeKeyboard::T},
426 return Settings::NativeKeyboard::K; 426 {Qt::Key_U, Settings::NativeKeyboard::U},
427 case Qt::Key_L: 427 {Qt::Key_V, Settings::NativeKeyboard::V},
428 return Settings::NativeKeyboard::L; 428 {Qt::Key_W, Settings::NativeKeyboard::W},
429 case Qt::Key_M: 429 {Qt::Key_X, Settings::NativeKeyboard::X},
430 return Settings::NativeKeyboard::M; 430 {Qt::Key_Y, Settings::NativeKeyboard::Y},
431 case Qt::Key_N: 431 {Qt::Key_Z, Settings::NativeKeyboard::Z},
432 return Settings::NativeKeyboard::N; 432 {Qt::Key_1, Settings::NativeKeyboard::N1},
433 case Qt::Key_O: 433 {Qt::Key_2, Settings::NativeKeyboard::N2},
434 return Settings::NativeKeyboard::O; 434 {Qt::Key_3, Settings::NativeKeyboard::N3},
435 case Qt::Key_P: 435 {Qt::Key_4, Settings::NativeKeyboard::N4},
436 return Settings::NativeKeyboard::P; 436 {Qt::Key_5, Settings::NativeKeyboard::N5},
437 case Qt::Key_Q: 437 {Qt::Key_6, Settings::NativeKeyboard::N6},
438 return Settings::NativeKeyboard::Q; 438 {Qt::Key_7, Settings::NativeKeyboard::N7},
439 case Qt::Key_R: 439 {Qt::Key_8, Settings::NativeKeyboard::N8},
440 return Settings::NativeKeyboard::R; 440 {Qt::Key_9, Settings::NativeKeyboard::N9},
441 case Qt::Key_S: 441 {Qt::Key_0, Settings::NativeKeyboard::N0},
442 return Settings::NativeKeyboard::S; 442 {Qt::Key_Return, Settings::NativeKeyboard::Return},
443 case Qt::Key_T: 443 {Qt::Key_Escape, Settings::NativeKeyboard::Escape},
444 return Settings::NativeKeyboard::T; 444 {Qt::Key_Backspace, Settings::NativeKeyboard::Backspace},
445 case Qt::Key_U: 445 {Qt::Key_Tab, Settings::NativeKeyboard::Tab},
446 return Settings::NativeKeyboard::U; 446 {Qt::Key_Space, Settings::NativeKeyboard::Space},
447 case Qt::Key_V: 447 {Qt::Key_Minus, Settings::NativeKeyboard::Minus},
448 return Settings::NativeKeyboard::V; 448 {Qt::Key_Plus, Settings::NativeKeyboard::Plus},
449 case Qt::Key_W: 449 {Qt::Key_questiondown, Settings::NativeKeyboard::Plus},
450 return Settings::NativeKeyboard::W; 450 {Qt::Key_BracketLeft, Settings::NativeKeyboard::OpenBracket},
451 case Qt::Key_X: 451 {Qt::Key_BraceLeft, Settings::NativeKeyboard::OpenBracket},
452 return Settings::NativeKeyboard::X; 452 {Qt::Key_BracketRight, Settings::NativeKeyboard::CloseBracket},
453 case Qt::Key_Y: 453 {Qt::Key_BraceRight, Settings::NativeKeyboard::CloseBracket},
454 return Settings::NativeKeyboard::Y; 454 {Qt::Key_Bar, Settings::NativeKeyboard::Pipe},
455 case Qt::Key_Z: 455 {Qt::Key_Dead_Tilde, Settings::NativeKeyboard::Tilde},
456 return Settings::NativeKeyboard::Z; 456 {Qt::Key_Ntilde, Settings::NativeKeyboard::Semicolon},
457 case Qt::Key_1: 457 {Qt::Key_Semicolon, Settings::NativeKeyboard::Semicolon},
458 return Settings::NativeKeyboard::N1; 458 {Qt::Key_Apostrophe, Settings::NativeKeyboard::Quote},
459 case Qt::Key_2: 459 {Qt::Key_Dead_Grave, Settings::NativeKeyboard::Backquote},
460 return Settings::NativeKeyboard::N2; 460 {Qt::Key_Comma, Settings::NativeKeyboard::Comma},
461 case Qt::Key_3: 461 {Qt::Key_Period, Settings::NativeKeyboard::Period},
462 return Settings::NativeKeyboard::N3; 462 {Qt::Key_Slash, Settings::NativeKeyboard::Slash},
463 case Qt::Key_4: 463 {Qt::Key_CapsLock, Settings::NativeKeyboard::CapsLockKey},
464 return Settings::NativeKeyboard::N4; 464 {Qt::Key_F1, Settings::NativeKeyboard::F1},
465 case Qt::Key_5: 465 {Qt::Key_F2, Settings::NativeKeyboard::F2},
466 return Settings::NativeKeyboard::N5; 466 {Qt::Key_F3, Settings::NativeKeyboard::F3},
467 case Qt::Key_6: 467 {Qt::Key_F4, Settings::NativeKeyboard::F4},
468 return Settings::NativeKeyboard::N6; 468 {Qt::Key_F5, Settings::NativeKeyboard::F5},
469 case Qt::Key_7: 469 {Qt::Key_F6, Settings::NativeKeyboard::F6},
470 return Settings::NativeKeyboard::N7; 470 {Qt::Key_F7, Settings::NativeKeyboard::F7},
471 case Qt::Key_8: 471 {Qt::Key_F8, Settings::NativeKeyboard::F8},
472 return Settings::NativeKeyboard::N8; 472 {Qt::Key_F9, Settings::NativeKeyboard::F9},
473 case Qt::Key_9: 473 {Qt::Key_F10, Settings::NativeKeyboard::F10},
474 return Settings::NativeKeyboard::N9; 474 {Qt::Key_F11, Settings::NativeKeyboard::F11},
475 case Qt::Key_0: 475 {Qt::Key_F12, Settings::NativeKeyboard::F12},
476 return Settings::NativeKeyboard::N0; 476 {Qt::Key_Print, Settings::NativeKeyboard::PrintScreen},
477 case Qt::Key_Return: 477 {Qt::Key_ScrollLock, Settings::NativeKeyboard::ScrollLockKey},
478 return Settings::NativeKeyboard::Return; 478 {Qt::Key_Pause, Settings::NativeKeyboard::Pause},
479 case Qt::Key_Escape: 479 {Qt::Key_Insert, Settings::NativeKeyboard::Insert},
480 return Settings::NativeKeyboard::Escape; 480 {Qt::Key_Home, Settings::NativeKeyboard::Home},
481 case Qt::Key_Backspace: 481 {Qt::Key_PageUp, Settings::NativeKeyboard::PageUp},
482 return Settings::NativeKeyboard::Backspace; 482 {Qt::Key_Delete, Settings::NativeKeyboard::Delete},
483 case Qt::Key_Tab: 483 {Qt::Key_End, Settings::NativeKeyboard::End},
484 return Settings::NativeKeyboard::Tab; 484 {Qt::Key_PageDown, Settings::NativeKeyboard::PageDown},
485 case Qt::Key_Space: 485 {Qt::Key_Right, Settings::NativeKeyboard::Right},
486 return Settings::NativeKeyboard::Space; 486 {Qt::Key_Left, Settings::NativeKeyboard::Left},
487 case Qt::Key_Minus: 487 {Qt::Key_Down, Settings::NativeKeyboard::Down},
488 return Settings::NativeKeyboard::Minus; 488 {Qt::Key_Up, Settings::NativeKeyboard::Up},
489 case Qt::Key_Plus: 489 {Qt::Key_NumLock, Settings::NativeKeyboard::NumLockKey},
490 case Qt::Key_questiondown: 490 // Numpad keys are missing here
491 return Settings::NativeKeyboard::Plus; 491 {Qt::Key_F13, Settings::NativeKeyboard::F13},
492 case Qt::Key_BracketLeft: 492 {Qt::Key_F14, Settings::NativeKeyboard::F14},
493 case Qt::Key_BraceLeft: 493 {Qt::Key_F15, Settings::NativeKeyboard::F15},
494 return Settings::NativeKeyboard::OpenBracket; 494 {Qt::Key_F16, Settings::NativeKeyboard::F16},
495 case Qt::Key_BracketRight: 495 {Qt::Key_F17, Settings::NativeKeyboard::F17},
496 case Qt::Key_BraceRight: 496 {Qt::Key_F18, Settings::NativeKeyboard::F18},
497 return Settings::NativeKeyboard::CloseBracket; 497 {Qt::Key_F19, Settings::NativeKeyboard::F19},
498 case Qt::Key_Bar: 498 {Qt::Key_F20, Settings::NativeKeyboard::F20},
499 return Settings::NativeKeyboard::Pipe; 499 {Qt::Key_F21, Settings::NativeKeyboard::F21},
500 case Qt::Key_Dead_Tilde: 500 {Qt::Key_F22, Settings::NativeKeyboard::F22},
501 return Settings::NativeKeyboard::Tilde; 501 {Qt::Key_F23, Settings::NativeKeyboard::F23},
502 case Qt::Key_Ntilde: 502 {Qt::Key_F24, Settings::NativeKeyboard::F24},
503 case Qt::Key_Semicolon: 503 // {Qt::..., Settings::NativeKeyboard::KPComma},
504 return Settings::NativeKeyboard::Semicolon; 504 // {Qt::..., Settings::NativeKeyboard::Ro},
505 case Qt::Key_Apostrophe: 505 {Qt::Key_Hiragana_Katakana, Settings::NativeKeyboard::KatakanaHiragana},
506 return Settings::NativeKeyboard::Quote; 506 {Qt::Key_yen, Settings::NativeKeyboard::Yen},
507 case Qt::Key_Dead_Grave: 507 {Qt::Key_Henkan, Settings::NativeKeyboard::Henkan},
508 return Settings::NativeKeyboard::Backquote; 508 {Qt::Key_Muhenkan, Settings::NativeKeyboard::Muhenkan},
509 case Qt::Key_Comma: 509 // {Qt::..., Settings::NativeKeyboard::NumPadCommaPc98},
510 return Settings::NativeKeyboard::Comma; 510 {Qt::Key_Hangul, Settings::NativeKeyboard::HangulEnglish},
511 case Qt::Key_Period: 511 {Qt::Key_Hangul_Hanja, Settings::NativeKeyboard::Hanja},
512 return Settings::NativeKeyboard::Period; 512 {Qt::Key_Katakana, Settings::NativeKeyboard::KatakanaKey},
513 case Qt::Key_Slash: 513 {Qt::Key_Hiragana, Settings::NativeKeyboard::HiraganaKey},
514 return Settings::NativeKeyboard::Slash; 514 {Qt::Key_Zenkaku_Hankaku, Settings::NativeKeyboard::ZenkakuHankaku},
515 case Qt::Key_CapsLock: 515 // Modifier keys are handled by the modifier property
516 return Settings::NativeKeyboard::CapsLock; 516 };
517 case Qt::Key_F1: 517
518 return Settings::NativeKeyboard::F1; 518 for (const auto& [qkey, nkey] : key_map) {
519 case Qt::Key_F2: 519 if (qt_key == qkey) {
520 return Settings::NativeKeyboard::F2; 520 return nkey;
521 case Qt::Key_F3: 521 }
522 return Settings::NativeKeyboard::F3;
523 case Qt::Key_F4:
524 return Settings::NativeKeyboard::F4;
525 case Qt::Key_F5:
526 return Settings::NativeKeyboard::F5;
527 case Qt::Key_F6:
528 return Settings::NativeKeyboard::F6;
529 case Qt::Key_F7:
530 return Settings::NativeKeyboard::F7;
531 case Qt::Key_F8:
532 return Settings::NativeKeyboard::F8;
533 case Qt::Key_F9:
534 return Settings::NativeKeyboard::F9;
535 case Qt::Key_F10:
536 return Settings::NativeKeyboard::F10;
537 case Qt::Key_F11:
538 return Settings::NativeKeyboard::F11;
539 case Qt::Key_F12:
540 return Settings::NativeKeyboard::F12;
541 case Qt::Key_Print:
542 return Settings::NativeKeyboard::PrintScreen;
543 case Qt::Key_ScrollLock:
544 return Settings::NativeKeyboard::ScrollLock;
545 case Qt::Key_Pause:
546 return Settings::NativeKeyboard::Pause;
547 case Qt::Key_Insert:
548 return Settings::NativeKeyboard::Insert;
549 case Qt::Key_Home:
550 return Settings::NativeKeyboard::Home;
551 case Qt::Key_PageUp:
552 return Settings::NativeKeyboard::PageUp;
553 case Qt::Key_Delete:
554 return Settings::NativeKeyboard::Delete;
555 case Qt::Key_End:
556 return Settings::NativeKeyboard::End;
557 case Qt::Key_PageDown:
558 return Settings::NativeKeyboard::PageDown;
559 case Qt::Key_Right:
560 return Settings::NativeKeyboard::Right;
561 case Qt::Key_Left:
562 return Settings::NativeKeyboard::Left;
563 case Qt::Key_Down:
564 return Settings::NativeKeyboard::Down;
565 case Qt::Key_Up:
566 return Settings::NativeKeyboard::Up;
567 case Qt::Key_NumLock:
568 return Settings::NativeKeyboard::NumLock;
569 // Numpad keys are missing here
570 case Qt::Key_F13:
571 return Settings::NativeKeyboard::F13;
572 case Qt::Key_F14:
573 return Settings::NativeKeyboard::F14;
574 case Qt::Key_F15:
575 return Settings::NativeKeyboard::F15;
576 case Qt::Key_F16:
577 return Settings::NativeKeyboard::F16;
578 case Qt::Key_F17:
579 return Settings::NativeKeyboard::F17;
580 case Qt::Key_F18:
581 return Settings::NativeKeyboard::F18;
582 case Qt::Key_F19:
583 return Settings::NativeKeyboard::F19;
584 case Qt::Key_F20:
585 return Settings::NativeKeyboard::F20;
586 case Qt::Key_F21:
587 return Settings::NativeKeyboard::F21;
588 case Qt::Key_F22:
589 return Settings::NativeKeyboard::F22;
590 case Qt::Key_F23:
591 return Settings::NativeKeyboard::F23;
592 case Qt::Key_F24:
593 return Settings::NativeKeyboard::F24;
594 // case Qt:::
595 // return Settings::NativeKeyboard::KPComma;
596 // case Qt:::
597 // return Settings::NativeKeyboard::Ro;
598 case Qt::Key_Hiragana_Katakana:
599 return Settings::NativeKeyboard::KatakanaHiragana;
600 case Qt::Key_yen:
601 return Settings::NativeKeyboard::Yen;
602 case Qt::Key_Henkan:
603 return Settings::NativeKeyboard::Henkan;
604 case Qt::Key_Muhenkan:
605 return Settings::NativeKeyboard::Muhenkan;
606 // case Qt:::
607 // return Settings::NativeKeyboard::NumPadCommaPc98;
608 case Qt::Key_Hangul:
609 return Settings::NativeKeyboard::HangulEnglish;
610 case Qt::Key_Hangul_Hanja:
611 return Settings::NativeKeyboard::Hanja;
612 case Qt::Key_Katakana:
613 return Settings::NativeKeyboard::KatakanaKey;
614 case Qt::Key_Hiragana:
615 return Settings::NativeKeyboard::HiraganaKey;
616 case Qt::Key_Zenkaku_Hankaku:
617 return Settings::NativeKeyboard::ZenkakuHankaku;
618 // Modifier keys are handled by the modifier property
619 default:
620 return Settings::NativeKeyboard::None;
621 } 522 }
523
524 return Settings::NativeKeyboard::None;
622} 525}
623 526
624int GRenderWindow::QtModifierToSwitchModifier(Qt::KeyboardModifiers qt_modifiers) { 527int GRenderWindow::QtModifierToSwitchModifier(Qt::KeyboardModifiers qt_modifiers) {
diff --git a/src/yuzu/configuration/config.cpp b/src/yuzu/configuration/config.cpp
index 195074bf2..343f3b8e5 100644
--- a/src/yuzu/configuration/config.cpp
+++ b/src/yuzu/configuration/config.cpp
@@ -819,6 +819,9 @@ void Config::ReadUIGamelistValues() {
819 qt_config->beginGroup(QStringLiteral("UIGameList")); 819 qt_config->beginGroup(QStringLiteral("UIGameList"));
820 820
821 ReadBasicSetting(UISettings::values.show_add_ons); 821 ReadBasicSetting(UISettings::values.show_add_ons);
822 ReadBasicSetting(UISettings::values.show_compat);
823 ReadBasicSetting(UISettings::values.show_size);
824 ReadBasicSetting(UISettings::values.show_types);
822 ReadBasicSetting(UISettings::values.game_icon_size); 825 ReadBasicSetting(UISettings::values.game_icon_size);
823 ReadBasicSetting(UISettings::values.folder_icon_size); 826 ReadBasicSetting(UISettings::values.folder_icon_size);
824 ReadBasicSetting(UISettings::values.row_1_text_id); 827 ReadBasicSetting(UISettings::values.row_1_text_id);
@@ -1414,6 +1417,9 @@ void Config::SaveUIGamelistValues() {
1414 qt_config->beginGroup(QStringLiteral("UIGameList")); 1417 qt_config->beginGroup(QStringLiteral("UIGameList"));
1415 1418
1416 WriteBasicSetting(UISettings::values.show_add_ons); 1419 WriteBasicSetting(UISettings::values.show_add_ons);
1420 WriteBasicSetting(UISettings::values.show_compat);
1421 WriteBasicSetting(UISettings::values.show_size);
1422 WriteBasicSetting(UISettings::values.show_types);
1417 WriteBasicSetting(UISettings::values.game_icon_size); 1423 WriteBasicSetting(UISettings::values.game_icon_size);
1418 WriteBasicSetting(UISettings::values.folder_icon_size); 1424 WriteBasicSetting(UISettings::values.folder_icon_size);
1419 WriteBasicSetting(UISettings::values.row_1_text_id); 1425 WriteBasicSetting(UISettings::values.row_1_text_id);
diff --git a/src/yuzu/configuration/configure_ui.cpp b/src/yuzu/configuration/configure_ui.cpp
index 48f71b53c..2ebb80302 100644
--- a/src/yuzu/configuration/configure_ui.cpp
+++ b/src/yuzu/configuration/configure_ui.cpp
@@ -72,6 +72,9 @@ ConfigureUi::ConfigureUi(Core::System& system_, QWidget* parent)
72 72
73 // Force game list reload if any of the relevant settings are changed. 73 // Force game list reload if any of the relevant settings are changed.
74 connect(ui->show_add_ons, &QCheckBox::stateChanged, this, &ConfigureUi::RequestGameListUpdate); 74 connect(ui->show_add_ons, &QCheckBox::stateChanged, this, &ConfigureUi::RequestGameListUpdate);
75 connect(ui->show_compat, &QCheckBox::stateChanged, this, &ConfigureUi::RequestGameListUpdate);
76 connect(ui->show_size, &QCheckBox::stateChanged, this, &ConfigureUi::RequestGameListUpdate);
77 connect(ui->show_types, &QCheckBox::stateChanged, this, &ConfigureUi::RequestGameListUpdate);
75 connect(ui->game_icon_size_combobox, QOverload<int>::of(&QComboBox::currentIndexChanged), this, 78 connect(ui->game_icon_size_combobox, QOverload<int>::of(&QComboBox::currentIndexChanged), this,
76 &ConfigureUi::RequestGameListUpdate); 79 &ConfigureUi::RequestGameListUpdate);
77 connect(ui->folder_icon_size_combobox, QOverload<int>::of(&QComboBox::currentIndexChanged), 80 connect(ui->folder_icon_size_combobox, QOverload<int>::of(&QComboBox::currentIndexChanged),
@@ -109,6 +112,9 @@ void ConfigureUi::ApplyConfiguration() {
109 UISettings::values.theme = 112 UISettings::values.theme =
110 ui->theme_combobox->itemData(ui->theme_combobox->currentIndex()).toString(); 113 ui->theme_combobox->itemData(ui->theme_combobox->currentIndex()).toString();
111 UISettings::values.show_add_ons = ui->show_add_ons->isChecked(); 114 UISettings::values.show_add_ons = ui->show_add_ons->isChecked();
115 UISettings::values.show_compat = ui->show_compat->isChecked();
116 UISettings::values.show_size = ui->show_size->isChecked();
117 UISettings::values.show_types = ui->show_types->isChecked();
112 UISettings::values.game_icon_size = ui->game_icon_size_combobox->currentData().toUInt(); 118 UISettings::values.game_icon_size = ui->game_icon_size_combobox->currentData().toUInt();
113 UISettings::values.folder_icon_size = ui->folder_icon_size_combobox->currentData().toUInt(); 119 UISettings::values.folder_icon_size = ui->folder_icon_size_combobox->currentData().toUInt();
114 UISettings::values.row_1_text_id = ui->row_1_text_combobox->currentData().toUInt(); 120 UISettings::values.row_1_text_id = ui->row_1_text_combobox->currentData().toUInt();
@@ -129,6 +135,9 @@ void ConfigureUi::SetConfiguration() {
129 ui->language_combobox->setCurrentIndex( 135 ui->language_combobox->setCurrentIndex(
130 ui->language_combobox->findData(UISettings::values.language)); 136 ui->language_combobox->findData(UISettings::values.language));
131 ui->show_add_ons->setChecked(UISettings::values.show_add_ons.GetValue()); 137 ui->show_add_ons->setChecked(UISettings::values.show_add_ons.GetValue());
138 ui->show_compat->setChecked(UISettings::values.show_compat.GetValue());
139 ui->show_size->setChecked(UISettings::values.show_size.GetValue());
140 ui->show_types->setChecked(UISettings::values.show_types.GetValue());
132 ui->game_icon_size_combobox->setCurrentIndex( 141 ui->game_icon_size_combobox->setCurrentIndex(
133 ui->game_icon_size_combobox->findData(UISettings::values.game_icon_size.GetValue())); 142 ui->game_icon_size_combobox->findData(UISettings::values.game_icon_size.GetValue()));
134 ui->folder_icon_size_combobox->setCurrentIndex( 143 ui->folder_icon_size_combobox->setCurrentIndex(
diff --git a/src/yuzu/configuration/configure_ui.ui b/src/yuzu/configuration/configure_ui.ui
index a50df7f6f..10bb27312 100644
--- a/src/yuzu/configuration/configure_ui.ui
+++ b/src/yuzu/configuration/configure_ui.ui
@@ -7,7 +7,7 @@
7 <x>0</x> 7 <x>0</x>
8 <y>0</y> 8 <y>0</y>
9 <width>363</width> 9 <width>363</width>
10 <height>507</height> 10 <height>562</height>
11 </rect> 11 </rect>
12 </property> 12 </property>
13 <property name="windowTitle"> 13 <property name="windowTitle">
@@ -77,6 +77,13 @@
77 <item> 77 <item>
78 <layout class="QVBoxLayout" name="GeneralVerticalLayout"> 78 <layout class="QVBoxLayout" name="GeneralVerticalLayout">
79 <item> 79 <item>
80 <widget class="QCheckBox" name="show_compat">
81 <property name="text">
82 <string>Show Compatibility List</string>
83 </property>
84 </widget>
85 </item>
86 <item>
80 <widget class="QCheckBox" name="show_add_ons"> 87 <widget class="QCheckBox" name="show_add_ons">
81 <property name="text"> 88 <property name="text">
82 <string>Show Add-Ons Column</string> 89 <string>Show Add-Ons Column</string>
@@ -84,6 +91,20 @@
84 </widget> 91 </widget>
85 </item> 92 </item>
86 <item> 93 <item>
94 <widget class="QCheckBox" name="show_size">
95 <property name="text">
96 <string>Show Size Column</string>
97 </property>
98 </widget>
99 </item>
100 <item>
101 <widget class="QCheckBox" name="show_types">
102 <property name="text">
103 <string>Show File Types Column</string>
104 </property>
105 </widget>
106 </item>
107 <item>
87 <layout class="QHBoxLayout" name="game_icon_size_qhbox_layout_2"> 108 <layout class="QHBoxLayout" name="game_icon_size_qhbox_layout_2">
88 <item> 109 <item>
89 <widget class="QLabel" name="game_icon_size_label"> 110 <widget class="QLabel" name="game_icon_size_label">
diff --git a/src/yuzu/game_list.cpp b/src/yuzu/game_list.cpp
index b127badc2..5c33c1b0f 100644
--- a/src/yuzu/game_list.cpp
+++ b/src/yuzu/game_list.cpp
@@ -335,6 +335,7 @@ GameList::GameList(FileSys::VirtualFilesystem vfs_, FileSys::ManualContentProvid
335 RetranslateUI(); 335 RetranslateUI();
336 336
337 tree_view->setColumnHidden(COLUMN_ADD_ONS, !UISettings::values.show_add_ons); 337 tree_view->setColumnHidden(COLUMN_ADD_ONS, !UISettings::values.show_add_ons);
338 tree_view->setColumnHidden(COLUMN_COMPATIBILITY, !UISettings::values.show_compat);
338 item_model->setSortRole(GameListItemPath::SortRole); 339 item_model->setSortRole(GameListItemPath::SortRole);
339 340
340 connect(main_window, &GMainWindow::UpdateThemedIcons, this, &GameList::OnUpdateThemedIcons); 341 connect(main_window, &GMainWindow::UpdateThemedIcons, this, &GameList::OnUpdateThemedIcons);
@@ -786,6 +787,9 @@ void GameList::PopulateAsync(QVector<UISettings::GameDir>& game_dirs) {
786 787
787 // Update the columns in case UISettings has changed 788 // Update the columns in case UISettings has changed
788 tree_view->setColumnHidden(COLUMN_ADD_ONS, !UISettings::values.show_add_ons); 789 tree_view->setColumnHidden(COLUMN_ADD_ONS, !UISettings::values.show_add_ons);
790 tree_view->setColumnHidden(COLUMN_COMPATIBILITY, !UISettings::values.show_compat);
791 tree_view->setColumnHidden(COLUMN_FILE_TYPE, !UISettings::values.show_types);
792 tree_view->setColumnHidden(COLUMN_SIZE, !UISettings::values.show_size);
789 793
790 // Delete any rows that might already exist if we're repopulating 794 // Delete any rows that might already exist if we're repopulating
791 item_model->removeRows(0, item_model->rowCount()); 795 item_model->removeRows(0, item_model->rowCount());
diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp
index ccae2b828..d95915016 100644
--- a/src/yuzu/main.cpp
+++ b/src/yuzu/main.cpp
@@ -9,7 +9,7 @@
9#ifdef __APPLE__ 9#ifdef __APPLE__
10#include <unistd.h> // for chdir 10#include <unistd.h> // for chdir
11#endif 11#endif
12#ifdef __linux__ 12#ifdef __unix__
13#include <csignal> 13#include <csignal>
14#include <sys/socket.h> 14#include <sys/socket.h>
15#endif 15#endif
@@ -275,7 +275,7 @@ static void OverrideWindowsFont() {
275#endif 275#endif
276 276
277bool GMainWindow::CheckDarkMode() { 277bool GMainWindow::CheckDarkMode() {
278#ifdef __linux__ 278#ifdef __unix__
279 const QPalette test_palette(qApp->palette()); 279 const QPalette test_palette(qApp->palette());
280 const QColor text_color = test_palette.color(QPalette::Active, QPalette::Text); 280 const QColor text_color = test_palette.color(QPalette::Active, QPalette::Text);
281 const QColor window_color = test_palette.color(QPalette::Active, QPalette::Window); 281 const QColor window_color = test_palette.color(QPalette::Active, QPalette::Window);
@@ -283,7 +283,7 @@ bool GMainWindow::CheckDarkMode() {
283#else 283#else
284 // TODO: Windows 284 // TODO: Windows
285 return false; 285 return false;
286#endif // __linux__ 286#endif // __unix__
287} 287}
288 288
289GMainWindow::GMainWindow(std::unique_ptr<Config> config_, bool has_broken_vulkan) 289GMainWindow::GMainWindow(std::unique_ptr<Config> config_, bool has_broken_vulkan)
@@ -291,7 +291,7 @@ GMainWindow::GMainWindow(std::unique_ptr<Config> config_, bool has_broken_vulkan
291 input_subsystem{std::make_shared<InputCommon::InputSubsystem>()}, config{std::move(config_)}, 291 input_subsystem{std::make_shared<InputCommon::InputSubsystem>()}, config{std::move(config_)},
292 vfs{std::make_shared<FileSys::RealVfsFilesystem>()}, 292 vfs{std::make_shared<FileSys::RealVfsFilesystem>()},
293 provider{std::make_unique<FileSys::ManualContentProvider>()} { 293 provider{std::make_unique<FileSys::ManualContentProvider>()} {
294#ifdef __linux__ 294#ifdef __unix__
295 SetupSigInterrupts(); 295 SetupSigInterrupts();
296#endif 296#endif
297 system->Initialize(); 297 system->Initialize();
@@ -342,6 +342,7 @@ GMainWindow::GMainWindow(std::unique_ptr<Config> config_, bool has_broken_vulkan
342 const auto override_build = 342 const auto override_build =
343 fmt::format(fmt::runtime(std::string(Common::g_title_bar_format_idle)), build_id); 343 fmt::format(fmt::runtime(std::string(Common::g_title_bar_format_idle)), build_id);
344 const auto yuzu_build_version = override_build.empty() ? yuzu_build : override_build; 344 const auto yuzu_build_version = override_build.empty() ? yuzu_build : override_build;
345 const auto processor_count = std::thread::hardware_concurrency();
345 346
346 LOG_INFO(Frontend, "yuzu Version: {}", yuzu_build_version); 347 LOG_INFO(Frontend, "yuzu Version: {}", yuzu_build_version);
347 LogRuntimes(); 348 LogRuntimes();
@@ -361,6 +362,7 @@ GMainWindow::GMainWindow(std::unique_ptr<Config> config_, bool has_broken_vulkan
361 } 362 }
362 LOG_INFO(Frontend, "Host CPU: {}", cpu_string); 363 LOG_INFO(Frontend, "Host CPU: {}", cpu_string);
363#endif 364#endif
365 LOG_INFO(Frontend, "Host CPU Threads: {}", processor_count);
364 LOG_INFO(Frontend, "Host OS: {}", PrettyProductName().toStdString()); 366 LOG_INFO(Frontend, "Host OS: {}", PrettyProductName().toStdString());
365 LOG_INFO(Frontend, "Host RAM: {:.2f} GiB", 367 LOG_INFO(Frontend, "Host RAM: {:.2f} GiB",
366 Common::GetMemInfo().TotalPhysicalMemory / f64{1_GiB}); 368 Common::GetMemInfo().TotalPhysicalMemory / f64{1_GiB});
@@ -509,7 +511,7 @@ GMainWindow::~GMainWindow() {
509 delete render_window; 511 delete render_window;
510 } 512 }
511 513
512#ifdef __linux__ 514#ifdef __unix__
513 ::close(sig_interrupt_fds[0]); 515 ::close(sig_interrupt_fds[0]);
514 ::close(sig_interrupt_fds[1]); 516 ::close(sig_interrupt_fds[1]);
515#endif 517#endif
@@ -1379,7 +1381,7 @@ void GMainWindow::OnDisplayTitleBars(bool show) {
1379} 1381}
1380 1382
1381void GMainWindow::SetupPrepareForSleep() { 1383void GMainWindow::SetupPrepareForSleep() {
1382#ifdef __linux__ 1384#ifdef __unix__
1383 auto bus = QDBusConnection::systemBus(); 1385 auto bus = QDBusConnection::systemBus();
1384 if (bus.isConnected()) { 1386 if (bus.isConnected()) {
1385 const bool success = bus.connect( 1387 const bool success = bus.connect(
@@ -1393,7 +1395,7 @@ void GMainWindow::SetupPrepareForSleep() {
1393 } else { 1395 } else {
1394 LOG_WARNING(Frontend, "QDBusConnection system bus is not connected"); 1396 LOG_WARNING(Frontend, "QDBusConnection system bus is not connected");
1395 } 1397 }
1396#endif // __linux__ 1398#endif // __unix__
1397} 1399}
1398 1400
1399void GMainWindow::OnPrepareForSleep(bool prepare_sleep) { 1401void GMainWindow::OnPrepareForSleep(bool prepare_sleep) {
@@ -1415,7 +1417,7 @@ void GMainWindow::OnPrepareForSleep(bool prepare_sleep) {
1415 } 1417 }
1416} 1418}
1417 1419
1418#ifdef __linux__ 1420#ifdef __unix__
1419static std::optional<QDBusObjectPath> HoldWakeLockLinux(u32 window_id = 0) { 1421static std::optional<QDBusObjectPath> HoldWakeLockLinux(u32 window_id = 0) {
1420 if (!QDBusConnection::sessionBus().isConnected()) { 1422 if (!QDBusConnection::sessionBus().isConnected()) {
1421 return {}; 1423 return {};
@@ -1500,14 +1502,14 @@ void GMainWindow::OnSigInterruptNotifierActivated() {
1500 1502
1501 emit SigInterrupt(); 1503 emit SigInterrupt();
1502} 1504}
1503#endif // __linux__ 1505#endif // __unix__
1504 1506
1505void GMainWindow::PreventOSSleep() { 1507void GMainWindow::PreventOSSleep() {
1506#ifdef _WIN32 1508#ifdef _WIN32
1507 SetThreadExecutionState(ES_CONTINUOUS | ES_SYSTEM_REQUIRED | ES_DISPLAY_REQUIRED); 1509 SetThreadExecutionState(ES_CONTINUOUS | ES_SYSTEM_REQUIRED | ES_DISPLAY_REQUIRED);
1508#elif defined(HAVE_SDL2) 1510#elif defined(HAVE_SDL2)
1509 SDL_DisableScreenSaver(); 1511 SDL_DisableScreenSaver();
1510#ifdef __linux__ 1512#ifdef __unix__
1511 auto reply = HoldWakeLockLinux(winId()); 1513 auto reply = HoldWakeLockLinux(winId());
1512 if (reply) { 1514 if (reply) {
1513 wake_lock = std::move(reply.value()); 1515 wake_lock = std::move(reply.value());
@@ -1521,7 +1523,7 @@ void GMainWindow::AllowOSSleep() {
1521 SetThreadExecutionState(ES_CONTINUOUS); 1523 SetThreadExecutionState(ES_CONTINUOUS);
1522#elif defined(HAVE_SDL2) 1524#elif defined(HAVE_SDL2)
1523 SDL_EnableScreenSaver(); 1525 SDL_EnableScreenSaver();
1524#ifdef __linux__ 1526#ifdef __unix__
1525 if (!wake_lock.path().isEmpty()) { 1527 if (!wake_lock.path().isEmpty()) {
1526 ReleaseWakeLockLinux(wake_lock); 1528 ReleaseWakeLockLinux(wake_lock);
1527 } 1529 }
@@ -2018,38 +2020,50 @@ static bool RomFSRawCopy(QProgressDialog& dialog, const FileSys::VirtualDir& src
2018 return true; 2020 return true;
2019} 2021}
2020 2022
2023QString GMainWindow::GetGameListErrorRemoving(InstalledEntryType type) const {
2024 switch (type) {
2025 case InstalledEntryType::Game:
2026 return tr("Error Removing Contents");
2027 case InstalledEntryType::Update:
2028 return tr("Error Removing Update");
2029 case InstalledEntryType::AddOnContent:
2030 return tr("Error Removing DLC");
2031 default:
2032 return QStringLiteral("Error Removing <Invalid Type>");
2033 }
2034}
2021void GMainWindow::OnGameListRemoveInstalledEntry(u64 program_id, InstalledEntryType type) { 2035void GMainWindow::OnGameListRemoveInstalledEntry(u64 program_id, InstalledEntryType type) {
2022 const QString entry_type = [type] { 2036 const QString entry_question = [type] {
2023 switch (type) { 2037 switch (type) {
2024 case InstalledEntryType::Game: 2038 case InstalledEntryType::Game:
2025 return tr("Contents"); 2039 return tr("Remove Installed Game Contents?");
2026 case InstalledEntryType::Update: 2040 case InstalledEntryType::Update:
2027 return tr("Update"); 2041 return tr("Remove Installed Game Update?");
2028 case InstalledEntryType::AddOnContent: 2042 case InstalledEntryType::AddOnContent:
2029 return tr("DLC"); 2043 return tr("Remove Installed Game DLC?");
2030 default: 2044 default:
2031 return QString{}; 2045 return QStringLiteral("Remove Installed Game <Invalid Type>?");
2032 } 2046 }
2033 }(); 2047 }();
2034 2048
2035 if (QMessageBox::question( 2049 if (QMessageBox::question(this, tr("Remove Entry"), entry_question,
2036 this, tr("Remove Entry"), tr("Remove Installed Game %1?").arg(entry_type), 2050 QMessageBox::Yes | QMessageBox::No,
2037 QMessageBox::Yes | QMessageBox::No, QMessageBox::No) != QMessageBox::Yes) { 2051 QMessageBox::No) != QMessageBox::Yes) {
2038 return; 2052 return;
2039 } 2053 }
2040 2054
2041 switch (type) { 2055 switch (type) {
2042 case InstalledEntryType::Game: 2056 case InstalledEntryType::Game:
2043 RemoveBaseContent(program_id, entry_type); 2057 RemoveBaseContent(program_id, type);
2044 [[fallthrough]]; 2058 [[fallthrough]];
2045 case InstalledEntryType::Update: 2059 case InstalledEntryType::Update:
2046 RemoveUpdateContent(program_id, entry_type); 2060 RemoveUpdateContent(program_id, type);
2047 if (type != InstalledEntryType::Game) { 2061 if (type != InstalledEntryType::Game) {
2048 break; 2062 break;
2049 } 2063 }
2050 [[fallthrough]]; 2064 [[fallthrough]];
2051 case InstalledEntryType::AddOnContent: 2065 case InstalledEntryType::AddOnContent:
2052 RemoveAddOnContent(program_id, entry_type); 2066 RemoveAddOnContent(program_id, type);
2053 break; 2067 break;
2054 } 2068 }
2055 Common::FS::RemoveDirRecursively(Common::FS::GetYuzuPath(Common::FS::YuzuPath::CacheDir) / 2069 Common::FS::RemoveDirRecursively(Common::FS::GetYuzuPath(Common::FS::YuzuPath::CacheDir) /
@@ -2057,7 +2071,7 @@ void GMainWindow::OnGameListRemoveInstalledEntry(u64 program_id, InstalledEntryT
2057 game_list->PopulateAsync(UISettings::values.game_dirs); 2071 game_list->PopulateAsync(UISettings::values.game_dirs);
2058} 2072}
2059 2073
2060void GMainWindow::RemoveBaseContent(u64 program_id, const QString& entry_type) { 2074void GMainWindow::RemoveBaseContent(u64 program_id, InstalledEntryType type) {
2061 const auto& fs_controller = system->GetFileSystemController(); 2075 const auto& fs_controller = system->GetFileSystemController();
2062 const auto res = fs_controller.GetUserNANDContents()->RemoveExistingEntry(program_id) || 2076 const auto res = fs_controller.GetUserNANDContents()->RemoveExistingEntry(program_id) ||
2063 fs_controller.GetSDMCContents()->RemoveExistingEntry(program_id); 2077 fs_controller.GetSDMCContents()->RemoveExistingEntry(program_id);
@@ -2067,12 +2081,12 @@ void GMainWindow::RemoveBaseContent(u64 program_id, const QString& entry_type) {
2067 tr("Successfully removed the installed base game.")); 2081 tr("Successfully removed the installed base game."));
2068 } else { 2082 } else {
2069 QMessageBox::warning( 2083 QMessageBox::warning(
2070 this, tr("Error Removing %1").arg(entry_type), 2084 this, GetGameListErrorRemoving(type),
2071 tr("The base game is not installed in the NAND and cannot be removed.")); 2085 tr("The base game is not installed in the NAND and cannot be removed."));
2072 } 2086 }
2073} 2087}
2074 2088
2075void GMainWindow::RemoveUpdateContent(u64 program_id, const QString& entry_type) { 2089void GMainWindow::RemoveUpdateContent(u64 program_id, InstalledEntryType type) {
2076 const auto update_id = program_id | 0x800; 2090 const auto update_id = program_id | 0x800;
2077 const auto& fs_controller = system->GetFileSystemController(); 2091 const auto& fs_controller = system->GetFileSystemController();
2078 const auto res = fs_controller.GetUserNANDContents()->RemoveExistingEntry(update_id) || 2092 const auto res = fs_controller.GetUserNANDContents()->RemoveExistingEntry(update_id) ||
@@ -2082,12 +2096,12 @@ void GMainWindow::RemoveUpdateContent(u64 program_id, const QString& entry_type)
2082 QMessageBox::information(this, tr("Successfully Removed"), 2096 QMessageBox::information(this, tr("Successfully Removed"),
2083 tr("Successfully removed the installed update.")); 2097 tr("Successfully removed the installed update."));
2084 } else { 2098 } else {
2085 QMessageBox::warning(this, tr("Error Removing %1").arg(entry_type), 2099 QMessageBox::warning(this, GetGameListErrorRemoving(type),
2086 tr("There is no update installed for this title.")); 2100 tr("There is no update installed for this title."));
2087 } 2101 }
2088} 2102}
2089 2103
2090void GMainWindow::RemoveAddOnContent(u64 program_id, const QString& entry_type) { 2104void GMainWindow::RemoveAddOnContent(u64 program_id, InstalledEntryType type) {
2091 u32 count{}; 2105 u32 count{};
2092 const auto& fs_controller = system->GetFileSystemController(); 2106 const auto& fs_controller = system->GetFileSystemController();
2093 const auto dlc_entries = system->GetContentProvider().ListEntriesFilter( 2107 const auto dlc_entries = system->GetContentProvider().ListEntriesFilter(
@@ -2105,7 +2119,7 @@ void GMainWindow::RemoveAddOnContent(u64 program_id, const QString& entry_type)
2105 } 2119 }
2106 2120
2107 if (count == 0) { 2121 if (count == 0) {
2108 QMessageBox::warning(this, tr("Error Removing %1").arg(entry_type), 2122 QMessageBox::warning(this, GetGameListErrorRemoving(type),
2109 tr("There are no DLC installed for this title.")); 2123 tr("There are no DLC installed for this title."));
2110 return; 2124 return;
2111 } 2125 }
@@ -4084,7 +4098,7 @@ void GMainWindow::SetDiscordEnabled([[maybe_unused]] bool state) {
4084} 4098}
4085 4099
4086void GMainWindow::changeEvent(QEvent* event) { 4100void GMainWindow::changeEvent(QEvent* event) {
4087#ifdef __linux__ 4101#ifdef __unix__
4088 // PaletteChange event appears to only reach so far into the GUI, explicitly asking to 4102 // PaletteChange event appears to only reach so far into the GUI, explicitly asking to
4089 // UpdateUITheme is a decent work around 4103 // UpdateUITheme is a decent work around
4090 if (event->type() == QEvent::PaletteChange) { 4104 if (event->type() == QEvent::PaletteChange) {
@@ -4099,7 +4113,7 @@ void GMainWindow::changeEvent(QEvent* event) {
4099 } 4113 }
4100 last_window_color = window_color; 4114 last_window_color = window_color;
4101 } 4115 }
4102#endif // __linux__ 4116#endif // __unix__
4103 QWidget::changeEvent(event); 4117 QWidget::changeEvent(event);
4104} 4118}
4105 4119
diff --git a/src/yuzu/main.h b/src/yuzu/main.h
index f7aa8e417..b73f550dd 100644
--- a/src/yuzu/main.h
+++ b/src/yuzu/main.h
@@ -15,7 +15,7 @@
15#include "yuzu/compatibility_list.h" 15#include "yuzu/compatibility_list.h"
16#include "yuzu/hotkeys.h" 16#include "yuzu/hotkeys.h"
17 17
18#ifdef __linux__ 18#ifdef __unix__
19#include <QVariant> 19#include <QVariant>
20#include <QtDBus/QDBusInterface> 20#include <QtDBus/QDBusInterface>
21#include <QtDBus/QtDBus> 21#include <QtDBus/QtDBus>
@@ -255,7 +255,7 @@ private:
255 void changeEvent(QEvent* event) override; 255 void changeEvent(QEvent* event) override;
256 void closeEvent(QCloseEvent* event) override; 256 void closeEvent(QCloseEvent* event) override;
257 257
258#ifdef __linux__ 258#ifdef __unix__
259 void SetupSigInterrupts(); 259 void SetupSigInterrupts();
260 static void HandleSigInterrupt(int); 260 static void HandleSigInterrupt(int);
261 void OnSigInterruptNotifierActivated(); 261 void OnSigInterruptNotifierActivated();
@@ -324,9 +324,10 @@ private slots:
324 void OnMouseActivity(); 324 void OnMouseActivity();
325 325
326private: 326private:
327 void RemoveBaseContent(u64 program_id, const QString& entry_type); 327 QString GetGameListErrorRemoving(InstalledEntryType type) const;
328 void RemoveUpdateContent(u64 program_id, const QString& entry_type); 328 void RemoveBaseContent(u64 program_id, InstalledEntryType type);
329 void RemoveAddOnContent(u64 program_id, const QString& entry_type); 329 void RemoveUpdateContent(u64 program_id, InstalledEntryType type);
330 void RemoveAddOnContent(u64 program_id, InstalledEntryType type);
330 void RemoveTransferableShaderCache(u64 program_id, GameListRemoveTarget target); 331 void RemoveTransferableShaderCache(u64 program_id, GameListRemoveTarget target);
331 void RemoveAllTransferableShaderCaches(u64 program_id); 332 void RemoveAllTransferableShaderCaches(u64 program_id);
332 void RemoveCustomConfiguration(u64 program_id, const std::string& game_path); 333 void RemoveCustomConfiguration(u64 program_id, const std::string& game_path);
@@ -435,7 +436,7 @@ private:
435 // True if TAS recording dialog is visible 436 // True if TAS recording dialog is visible
436 bool is_tas_recording_dialog_active{}; 437 bool is_tas_recording_dialog_active{};
437 438
438#ifdef __linux__ 439#ifdef __unix__
439 QSocketNotifier* sig_interrupt_notifier; 440 QSocketNotifier* sig_interrupt_notifier;
440 static std::array<int, 3> sig_interrupt_fds; 441 static std::array<int, 3> sig_interrupt_fds;
441 442
diff --git a/src/yuzu/main.ui b/src/yuzu/main.ui
index 74d49dbd4..e670acc30 100644
--- a/src/yuzu/main.ui
+++ b/src/yuzu/main.ui
@@ -55,7 +55,6 @@
55 <addaction name="separator"/> 55 <addaction name="separator"/>
56 <addaction name="menu_recent_files"/> 56 <addaction name="menu_recent_files"/>
57 <addaction name="separator"/> 57 <addaction name="separator"/>
58 <addaction name="separator"/>
59 <addaction name="action_Load_Amiibo"/> 58 <addaction name="action_Load_Amiibo"/>
60 <addaction name="separator"/> 59 <addaction name="separator"/>
61 <addaction name="action_Open_yuzu_Folder"/> 60 <addaction name="action_Open_yuzu_Folder"/>
diff --git a/src/yuzu/uisettings.h b/src/yuzu/uisettings.h
index 753797efc..452038cd9 100644
--- a/src/yuzu/uisettings.h
+++ b/src/yuzu/uisettings.h
@@ -129,6 +129,13 @@ struct Values {
129 Settings::Setting<bool> favorites_expanded{true, "favorites_expanded"}; 129 Settings::Setting<bool> favorites_expanded{true, "favorites_expanded"};
130 QVector<u64> favorited_ids; 130 QVector<u64> favorited_ids;
131 131
132 // Compatibility List
133 Settings::Setting<bool> show_compat{false, "show_compat"};
134
135 // Size & File Types Column
136 Settings::Setting<bool> show_size{true, "show_size"};
137 Settings::Setting<bool> show_types{true, "show_types"};
138
132 bool configuration_applied; 139 bool configuration_applied;
133 bool reset_to_defaults; 140 bool reset_to_defaults;
134 Settings::Setting<bool> disable_web_applet{true, "disable_web_applet"}; 141 Settings::Setting<bool> disable_web_applet{true, "disable_web_applet"};