summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/CMakeLists.txt8
-rw-r--r--src/audio_core/CMakeLists.txt3
-rw-r--r--src/audio_core/cubeb_sink.cpp25
-rw-r--r--src/audio_core/sdl2_sink.cpp4
-rw-r--r--src/audio_core/time_stretch.cpp68
-rw-r--r--src/audio_core/time_stretch.h34
-rw-r--r--src/common/atomic_ops.h90
-rw-r--r--src/common/fiber.cpp5
-rw-r--r--src/common/host_memory.cpp4
-rw-r--r--src/common/logging/filter.cpp2
-rw-r--r--src/common/logging/types.h2
-rw-r--r--src/common/math_util.h50
-rw-r--r--src/common/settings.h6
-rw-r--r--src/common/settings_input.h1
-rw-r--r--src/common/thread.h2
-rw-r--r--src/common/threadsafe_queue.h4
-rw-r--r--src/common/x64/native_clock.cpp59
-rw-r--r--src/core/CMakeLists.txt54
-rw-r--r--src/core/arm/arm_interface.cpp228
-rw-r--r--src/core/arm/arm_interface.h21
-rw-r--r--src/core/arm/dynarmic/arm_dynarmic_32.cpp152
-rw-r--r--src/core/arm/dynarmic/arm_dynarmic_32.h19
-rw-r--r--src/core/arm/dynarmic/arm_dynarmic_64.cpp206
-rw-r--r--src/core/arm/dynarmic/arm_dynarmic_64.h18
-rw-r--r--src/core/arm/symbols.cpp190
-rw-r--r--src/core/arm/symbols.h27
-rw-r--r--src/core/core_timing.h6
-rw-r--r--src/core/file_sys/patch_manager.cpp63
-rw-r--r--src/core/file_sys/registered_cache.cpp6
-rw-r--r--src/core/frontend/applets/mii.cpp19
-rw-r--r--src/core/frontend/applets/mii.h34
-rw-r--r--src/core/frontend/applets/mii_edit.cpp18
-rw-r--r--src/core/frontend/applets/mii_edit.h23
-rw-r--r--src/core/hid/emulated_console.cpp17
-rw-r--r--src/core/hid/emulated_console.h1
-rw-r--r--src/core/hid/emulated_controller.cpp377
-rw-r--r--src/core/hid/emulated_controller.h3
-rw-r--r--src/core/hid/emulated_devices.cpp78
-rw-r--r--src/core/hid/emulated_devices.h35
-rw-r--r--src/core/hid/hid_types.h5
-rw-r--r--src/core/hle/kernel/board/nintendo/nx/k_system_control.cpp6
-rw-r--r--src/core/hle/kernel/global_scheduler_context.h3
-rw-r--r--src/core/hle/kernel/hle_ipc.cpp16
-rw-r--r--src/core/hle/kernel/hle_ipc.h8
-rw-r--r--src/core/hle/kernel/k_auto_object.h7
-rw-r--r--src/core/hle/kernel/k_code_memory.cpp14
-rw-r--r--src/core/hle/kernel/k_page_linked_list.h4
-rw-r--r--src/core/hle/kernel/k_page_table.cpp253
-rw-r--r--src/core/hle/kernel/k_page_table.h38
-rw-r--r--src/core/hle/kernel/k_process.h2
-rw-r--r--src/core/hle/kernel/k_scheduler.cpp8
-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_session.cpp3
-rw-r--r--src/core/hle/kernel/k_spin_lock.cpp39
-rw-r--r--src/core/hle/kernel/k_spin_lock.h4
-rw-r--r--src/core/hle/kernel/k_thread.cpp39
-rw-r--r--src/core/hle/kernel/k_thread.h16
-rw-r--r--src/core/hle/kernel/kernel.cpp93
-rw-r--r--src/core/hle/kernel/kernel.h30
-rw-r--r--src/core/hle/kernel/physical_core.cpp4
-rw-r--r--src/core/hle/kernel/physical_core.h7
-rw-r--r--src/core/hle/kernel/svc.cpp16
-rw-r--r--src/core/hle/kernel/time_manager.cpp4
-rw-r--r--src/core/hle/service/am/am.cpp9
-rw-r--r--src/core/hle/service/am/am.h1
-rw-r--r--src/core/hle/service/am/applets/applet_mii.cpp100
-rw-r--r--src/core/hle/service/am/applets/applet_mii.h90
-rw-r--r--src/core/hle/service/am/applets/applet_mii_edit.cpp139
-rw-r--r--src/core/hle/service/am/applets/applet_mii_edit.h45
-rw-r--r--src/core/hle/service/am/applets/applet_mii_edit_types.h83
-rw-r--r--src/core/hle/service/am/applets/applet_software_keyboard.cpp474
-rw-r--r--src/core/hle/service/am/applets/applet_software_keyboard.h35
-rw-r--r--src/core/hle/service/am/applets/applet_software_keyboard_types.h72
-rw-r--r--src/core/hle/service/am/applets/applet_web_browser.cpp8
-rw-r--r--src/core/hle/service/am/applets/applets.cpp28
-rw-r--r--src/core/hle/service/am/applets/applets.h8
-rw-r--r--src/core/hle/service/audio/audout_u.cpp7
-rw-r--r--src/core/hle/service/audio/audren_u.cpp3
-rw-r--r--src/core/hle/service/filesystem/filesystem.cpp2
-rw-r--r--src/core/hle/service/filesystem/fsp_srv.cpp13
-rw-r--r--src/core/hle/service/hid/controllers/npad.cpp307
-rw-r--r--src/core/hle/service/hid/controllers/npad.h149
-rw-r--r--src/core/hle/service/hid/errors.h4
-rw-r--r--src/core/hle/service/hid/hid.cpp160
-rw-r--r--src/core/hle/service/hid/hid.h1
-rw-r--r--src/core/hle/service/hid/hidbus.cpp531
-rw-r--r--src/core/hle/service/hid/hidbus.h131
-rw-r--r--src/core/hle/service/hid/hidbus/hidbus_base.cpp72
-rw-r--r--src/core/hle/service/hid/hidbus/hidbus_base.h179
-rw-r--r--src/core/hle/service/hid/hidbus/ringcon.cpp286
-rw-r--r--src/core/hle/service/hid/hidbus/ringcon.h254
-rw-r--r--src/core/hle/service/hid/hidbus/starlink.cpp51
-rw-r--r--src/core/hle/service/hid/hidbus/starlink.h39
-rw-r--r--src/core/hle/service/hid/hidbus/stubbed.cpp52
-rw-r--r--src/core/hle/service/hid/hidbus/stubbed.h39
-rw-r--r--src/core/hle/service/jit/jit.cpp332
-rw-r--r--src/core/hle/service/jit/jit.h20
-rw-r--r--src/core/hle/service/jit/jit_context.cpp424
-rw-r--r--src/core/hle/service/jit/jit_context.h65
-rw-r--r--src/core/hle/service/ldr/ldr.cpp33
-rw-r--r--src/core/hle/service/mii/mii_manager.cpp3
-rw-r--r--src/core/hle/service/mii/mii_manager.h303
-rw-r--r--src/core/hle/service/mii/raw_data.h2
-rw-r--r--src/core/hle/service/mii/types.h307
-rw-r--r--src/core/hle/service/nfp/nfp.cpp1
-rw-r--r--src/core/hle/service/nfp/nfp.h2
-rw-r--r--src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp10
-rw-r--r--src/core/hle/service/nvdrv/devices/nvdisp_disp0.h7
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp2
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp7
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_gpu.h10
-rw-r--r--src/core/hle/service/nvdrv/nvdata.h10
-rw-r--r--src/core/hle/service/nvdrv/nvdrv.h3
-rw-r--r--src/core/hle/service/nvdrv/nvdrv_interface.cpp14
-rw-r--r--src/core/hle/service/nvflinger/binder.h42
-rw-r--r--src/core/hle/service/nvflinger/buffer_item.h46
-rw-r--r--src/core/hle/service/nvflinger/buffer_item_consumer.cpp59
-rw-r--r--src/core/hle/service/nvflinger/buffer_item_consumer.h28
-rw-r--r--src/core/hle/service/nvflinger/buffer_queue.cpp206
-rw-r--r--src/core/hle/service/nvflinger/buffer_queue.h154
-rw-r--r--src/core/hle/service/nvflinger/buffer_queue_consumer.cpp231
-rw-r--r--src/core/hle/service/nvflinger/buffer_queue_consumer.h37
-rw-r--r--src/core/hle/service/nvflinger/buffer_queue_core.cpp117
-rw-r--r--src/core/hle/service/nvflinger/buffer_queue_core.h79
-rw-r--r--src/core/hle/service/nvflinger/buffer_queue_defs.h21
-rw-r--r--src/core/hle/service/nvflinger/buffer_queue_producer.cpp923
-rw-r--r--src/core/hle/service/nvflinger/buffer_queue_producer.h83
-rw-r--r--src/core/hle/service/nvflinger/buffer_slot.h39
-rw-r--r--src/core/hle/service/nvflinger/buffer_transform_flags.h25
-rw-r--r--src/core/hle/service/nvflinger/consumer_base.cpp133
-rw-r--r--src/core/hle/service/nvflinger/consumer_base.h60
-rw-r--r--src/core/hle/service/nvflinger/consumer_listener.h26
-rw-r--r--src/core/hle/service/nvflinger/graphic_buffer_producer.cpp18
-rw-r--r--src/core/hle/service/nvflinger/graphic_buffer_producer.h76
-rw-r--r--src/core/hle/service/nvflinger/hos_binder_driver_server.cpp36
-rw-r--r--src/core/hle/service/nvflinger/hos_binder_driver_server.h37
-rw-r--r--src/core/hle/service/nvflinger/nvflinger.cpp85
-rw-r--r--src/core/hle/service/nvflinger/nvflinger.h26
-rw-r--r--src/core/hle/service/nvflinger/parcel.h172
-rw-r--r--src/core/hle/service/nvflinger/pixel_format.h21
-rw-r--r--src/core/hle/service/nvflinger/producer_listener.h16
-rw-r--r--src/core/hle/service/nvflinger/status.h28
-rw-r--r--src/core/hle/service/nvflinger/ui/fence.h32
-rw-r--r--src/core/hle/service/nvflinger/ui/graphic_buffer.h100
-rw-r--r--src/core/hle/service/nvflinger/window.h53
-rw-r--r--src/core/hle/service/service.cpp13
-rw-r--r--src/core/hle/service/service.h23
-rw-r--r--src/core/hle/service/sm/sm.cpp10
-rw-r--r--src/core/hle/service/sm/sm.h2
-rw-r--r--src/core/hle/service/sockets/bsd.cpp6
-rw-r--r--src/core/hle/service/sockets/sfdnsres.cpp205
-rw-r--r--src/core/hle/service/sockets/sfdnsres.h1
-rw-r--r--src/core/hle/service/sockets/sockets.h1
-rw-r--r--src/core/hle/service/vi/display/vi_display.cpp50
-rw-r--r--src/core/hle/service/vi/display/vi_display.h29
-rw-r--r--src/core/hle/service/vi/layer/vi_layer.cpp6
-rw-r--r--src/core/hle/service/vi/layer/vi_layer.h57
-rw-r--r--src/core/hle/service/vi/vi.cpp691
-rw-r--r--src/core/hle/service/vi/vi.h10
-rw-r--r--src/core/hle/service/vi/vi_m.cpp9
-rw-r--r--src/core/hle/service/vi/vi_m.h7
-rw-r--r--src/core/hle/service/vi/vi_s.cpp9
-rw-r--r--src/core/hle/service/vi/vi_s.h7
-rw-r--r--src/core/hle/service/vi/vi_u.cpp9
-rw-r--r--src/core/hle/service/vi/vi_u.h7
-rw-r--r--src/core/network/network.cpp4
-rw-r--r--src/core/network/sockets.h2
-rw-r--r--src/core/perf_stats.cpp10
-rw-r--r--src/core/tools/freezer.cpp18
-rw-r--r--src/input_common/drivers/sdl_driver.cpp12
-rw-r--r--src/input_common/helpers/udp_protocol.h9
-rw-r--r--src/input_common/input_engine.cpp46
-rw-r--r--src/shader_recompiler/backend/glsl/emit_glsl_context_get_set.cpp25
-rw-r--r--src/shader_recompiler/backend/glsl/glsl_emit_context.cpp19
-rw-r--r--src/shader_recompiler/backend/glsl/glsl_emit_context.h1
-rw-r--r--src/shader_recompiler/backend/spirv/emit_spirv_atomic.cpp2
-rw-r--r--src/shader_recompiler/backend/spirv/emit_spirv_context_get_set.cpp1
-rw-r--r--src/shader_recompiler/backend/spirv/spirv_emit_context.cpp22
-rw-r--r--src/shader_recompiler/frontend/ir/microinstruction.cpp5
-rw-r--r--src/shader_recompiler/frontend/ir/value.h4
-rw-r--r--src/shader_recompiler/frontend/maxwell/translate/impl/exit_program.cpp4
-rw-r--r--src/shader_recompiler/frontend/maxwell/translate/impl/texture_load.cpp2
-rw-r--r--src/shader_recompiler/frontend/maxwell/translate/impl/texture_mipmap_level.cpp2
-rw-r--r--src/shader_recompiler/ir_opt/collect_shader_info_pass.cpp9
-rw-r--r--src/shader_recompiler/ir_opt/dead_code_elimination_pass.cpp98
-rw-r--r--src/shader_recompiler/program_header.h5
-rw-r--r--src/shader_recompiler/shader_info.h1
-rw-r--r--src/video_core/buffer_cache/buffer_base.h3
-rw-r--r--src/video_core/buffer_cache/buffer_cache.h49
-rw-r--r--src/video_core/command_classes/codecs/codec.cpp35
-rw-r--r--src/video_core/engines/maxwell_3d.cpp5
-rw-r--r--src/video_core/engines/maxwell_3d.h7
-rw-r--r--src/video_core/framebuffer_config.h29
-rw-r--r--src/video_core/gpu.cpp6
-rw-r--r--src/video_core/gpu.h1
-rw-r--r--src/video_core/gpu_thread.cpp2
-rw-r--r--src/video_core/host_shaders/CMakeLists.txt1
-rw-r--r--src/video_core/host_shaders/opengl_convert_s8d24.comp18
-rw-r--r--src/video_core/renderer_base.cpp4
-rw-r--r--src/video_core/renderer_base.h3
-rw-r--r--src/video_core/renderer_opengl/gl_buffer_cache.cpp14
-rw-r--r--src/video_core/renderer_opengl/gl_buffer_cache.h12
-rw-r--r--src/video_core/renderer_opengl/gl_device.cpp10
-rw-r--r--src/video_core/renderer_opengl/gl_device.h7
-rw-r--r--src/video_core/renderer_opengl/gl_graphics_pipeline.cpp2
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer.cpp17
-rw-r--r--src/video_core/renderer_opengl/gl_shader_cache.cpp4
-rw-r--r--src/video_core/renderer_opengl/gl_texture_cache.cpp57
-rw-r--r--src/video_core/renderer_opengl/gl_texture_cache.h16
-rw-r--r--src/video_core/renderer_opengl/maxwell_to_gl.h2
-rw-r--r--src/video_core/renderer_opengl/renderer_opengl.cpp15
-rw-r--r--src/video_core/renderer_opengl/renderer_opengl.h6
-rw-r--r--src/video_core/renderer_opengl/util_shaders.cpp24
-rw-r--r--src/video_core/renderer_opengl/util_shaders.h3
-rw-r--r--src/video_core/renderer_vulkan/blit_image.cpp7
-rw-r--r--src/video_core/renderer_vulkan/maxwell_to_vk.cpp2
-rw-r--r--src/video_core/renderer_vulkan/pipeline_statistics.cpp4
-rw-r--r--src/video_core/renderer_vulkan/vk_blit_screen.cpp15
-rw-r--r--src/video_core/renderer_vulkan/vk_buffer_cache.cpp12
-rw-r--r--src/video_core/renderer_vulkan/vk_buffer_cache.h6
-rw-r--r--src/video_core/renderer_vulkan/vk_compute_pipeline.cpp2
-rw-r--r--src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp2
-rw-r--r--src/video_core/renderer_vulkan/vk_pipeline_cache.cpp4
-rw-r--r--src/video_core/renderer_vulkan/vk_render_pass_cache.cpp2
-rw-r--r--src/video_core/renderer_vulkan/vk_scheduler.cpp6
-rw-r--r--src/video_core/renderer_vulkan/vk_staging_buffer_pool.cpp2
-rw-r--r--src/video_core/renderer_vulkan/vk_texture_cache.cpp61
-rw-r--r--src/video_core/renderer_vulkan/vk_texture_cache.h4
-rw-r--r--src/video_core/shader_cache.cpp2
-rw-r--r--src/video_core/surface.cpp8
-rw-r--r--src/video_core/surface.h10
-rw-r--r--src/video_core/texture_cache/format_lookup_table.cpp6
-rw-r--r--src/video_core/texture_cache/formatter.h4
-rw-r--r--src/video_core/texture_cache/image_base.h7
-rw-r--r--src/video_core/texture_cache/texture_cache.h64
-rw-r--r--src/video_core/texture_cache/texture_cache_base.h7
-rw-r--r--src/video_core/vulkan_common/vulkan_device.cpp70
-rw-r--r--src/video_core/vulkan_common/vulkan_device.h11
-rw-r--r--src/video_core/vulkan_common/vulkan_memory_allocator.cpp2
-rw-r--r--src/video_core/vulkan_common/vulkan_wrapper.cpp13
-rw-r--r--src/video_core/vulkan_common/vulkan_wrapper.h4
-rw-r--r--src/web_service/web_backend.cpp4
-rw-r--r--src/yuzu/CMakeLists.txt5
-rw-r--r--src/yuzu/aboutdialog.ui2
-rw-r--r--src/yuzu/applets/qt_web_browser.cpp3
-rw-r--r--src/yuzu/applets/qt_web_browser.h1
-rw-r--r--src/yuzu/bootmanager.cpp6
-rw-r--r--src/yuzu/compatdb.ui2
-rw-r--r--src/yuzu/configuration/config.cpp36
-rw-r--r--src/yuzu/configuration/config.h3
-rw-r--r--src/yuzu/configuration/configure_cpu.ui5
-rw-r--r--src/yuzu/configuration/configure_debug.cpp8
-rw-r--r--src/yuzu/configuration/configure_debug.ui93
-rw-r--r--src/yuzu/configuration/configure_dialog.cpp3
-rw-r--r--src/yuzu/configuration/configure_hotkeys.cpp7
-rw-r--r--src/yuzu/configuration/configure_input.cpp5
-rw-r--r--src/yuzu/configuration/configure_input_advanced.cpp9
-rw-r--r--src/yuzu/configuration/configure_input_advanced.h1
-rw-r--r--src/yuzu/configuration/configure_input_advanced.ui14
-rw-r--r--src/yuzu/configuration/configure_input_player.cpp22
-rw-r--r--src/yuzu/configuration/configure_per_game_addons.cpp6
-rw-r--r--src/yuzu/configuration/configure_ringcon.cpp424
-rw-r--r--src/yuzu/configuration/configure_ringcon.h85
-rw-r--r--src/yuzu/configuration/configure_ringcon.ui278
-rw-r--r--src/yuzu/game_list_p.h4
-rw-r--r--src/yuzu/main.cpp75
-rw-r--r--src/yuzu/main.h6
-rw-r--r--src/yuzu/uisettings.cpp8
-rw-r--r--src/yuzu/uisettings.h3
-rw-r--r--src/yuzu/util/controller_navigation.cpp2
-rw-r--r--src/yuzu_cmd/CMakeLists.txt2
-rw-r--r--src/yuzu_cmd/default_ini.h6
-rw-r--r--src/yuzu_cmd/emu_window/emu_window_sdl2.cpp13
-rw-r--r--src/yuzu_cmd/yuzu.cpp1
275 files changed, 10492 insertions, 3318 deletions
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 1bdf70b76..9182dbfd4 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -65,12 +65,14 @@ if (MSVC)
65 /we4305 # 'context': truncation from 'type1' to 'type2' 65 /we4305 # 'context': truncation from 'type1' to 'type2'
66 /we4388 # 'expression': signed/unsigned mismatch 66 /we4388 # 'expression': signed/unsigned mismatch
67 /we4389 # 'operator': signed/unsigned mismatch 67 /we4389 # 'operator': signed/unsigned mismatch
68 /we4505 # 'function': unreferenced local function has been removed
68 /we4547 # 'operator': operator before comma has no effect; expected operator with side-effect 69 /we4547 # 'operator': operator before comma has no effect; expected operator with side-effect
69 /we4549 # 'operator1': operator before comma has no effect; did you intend 'operator2'? 70 /we4549 # 'operator1': operator before comma has no effect; did you intend 'operator2'?
70 /we4555 # Expression has no effect; expected expression with side-effect 71 /we4555 # Expression has no effect; expected expression with side-effect
71 /we4715 # 'function': not all control paths return a value 72 /we4715 # 'function': not all control paths return a value
72 /we4834 # Discarding return value of function with 'nodiscard' attribute 73 /we4834 # Discarding return value of function with 'nodiscard' attribute
73 /we5038 # data member 'member1' will be initialized after data member 'member2' 74 /we5038 # data member 'member1' will be initialized after data member 'member2'
75 /we5245 # 'function': unreferenced function with internal linkage has been removed
74 ) 76 )
75 77
76 if (ARCHITECTURE_x86_64) 78 if (ARCHITECTURE_x86_64)
@@ -103,12 +105,6 @@ else()
103 -Wno-unused-parameter 105 -Wno-unused-parameter
104 ) 106 )
105 107
106 # TODO: Remove when we update to a GCC compiler that enables this
107 # by default (i.e. GCC 10 or newer).
108 if (CMAKE_CXX_COMPILER_ID STREQUAL GNU)
109 add_compile_options(-fconcepts)
110 endif()
111
112 if (ARCHITECTURE_x86_64) 108 if (ARCHITECTURE_x86_64)
113 add_compile_options("-mcx16") 109 add_compile_options("-mcx16")
114 endif() 110 endif()
diff --git a/src/audio_core/CMakeLists.txt b/src/audio_core/CMakeLists.txt
index 090dd19b1..e553b8203 100644
--- a/src/audio_core/CMakeLists.txt
+++ b/src/audio_core/CMakeLists.txt
@@ -36,8 +36,6 @@ add_library(audio_core STATIC
36 splitter_context.h 36 splitter_context.h
37 stream.cpp 37 stream.cpp
38 stream.h 38 stream.h
39 time_stretch.cpp
40 time_stretch.h
41 voice_context.cpp 39 voice_context.cpp
42 voice_context.h 40 voice_context.h
43 41
@@ -63,7 +61,6 @@ if (NOT MSVC)
63endif() 61endif()
64 62
65target_link_libraries(audio_core PUBLIC common core) 63target_link_libraries(audio_core PUBLIC common core)
66target_link_libraries(audio_core PRIVATE SoundTouch)
67 64
68if(ENABLE_CUBEB) 65if(ENABLE_CUBEB)
69 target_link_libraries(audio_core PRIVATE cubeb) 66 target_link_libraries(audio_core PRIVATE cubeb)
diff --git a/src/audio_core/cubeb_sink.cpp b/src/audio_core/cubeb_sink.cpp
index 93c35e785..13de3087c 100644
--- a/src/audio_core/cubeb_sink.cpp
+++ b/src/audio_core/cubeb_sink.cpp
@@ -7,7 +7,6 @@
7#include <cstring> 7#include <cstring>
8#include "audio_core/cubeb_sink.h" 8#include "audio_core/cubeb_sink.h"
9#include "audio_core/stream.h" 9#include "audio_core/stream.h"
10#include "audio_core/time_stretch.h"
11#include "common/assert.h" 10#include "common/assert.h"
12#include "common/logging/log.h" 11#include "common/logging/log.h"
13#include "common/ring_buffer.h" 12#include "common/ring_buffer.h"
@@ -23,8 +22,7 @@ class CubebSinkStream final : public SinkStream {
23public: 22public:
24 CubebSinkStream(cubeb* ctx_, u32 sample_rate, u32 num_channels_, cubeb_devid output_device, 23 CubebSinkStream(cubeb* ctx_, u32 sample_rate, u32 num_channels_, cubeb_devid output_device,
25 const std::string& name) 24 const std::string& name)
26 : ctx{ctx_}, num_channels{std::min(num_channels_, 6u)}, time_stretch{sample_rate, 25 : ctx{ctx_}, num_channels{std::min(num_channels_, 6u)} {
27 num_channels} {
28 26
29 cubeb_stream_params params{}; 27 cubeb_stream_params params{};
30 params.rate = sample_rate; 28 params.rate = sample_rate;
@@ -131,7 +129,6 @@ private:
131 Common::RingBuffer<s16, 0x10000> queue; 129 Common::RingBuffer<s16, 0x10000> queue;
132 std::array<s16, 2> last_frame{}; 130 std::array<s16, 2> last_frame{};
133 std::atomic<bool> should_flush{}; 131 std::atomic<bool> should_flush{};
134 TimeStretcher time_stretch;
135 132
136 static long DataCallback(cubeb_stream* stream, void* user_data, const void* input_buffer, 133 static long DataCallback(cubeb_stream* stream, void* user_data, const void* input_buffer,
137 void* output_buffer, long num_frames); 134 void* output_buffer, long num_frames);
@@ -205,25 +202,7 @@ long CubebSinkStream::DataCallback([[maybe_unused]] cubeb_stream* stream, void*
205 202
206 const std::size_t num_channels = impl->GetNumChannels(); 203 const std::size_t num_channels = impl->GetNumChannels();
207 const std::size_t samples_to_write = num_channels * num_frames; 204 const std::size_t samples_to_write = num_channels * num_frames;
208 std::size_t samples_written; 205 const std::size_t samples_written = impl->queue.Pop(buffer, samples_to_write);
209
210 /*
211 if (Settings::values.enable_audio_stretching.GetValue()) {
212 const std::vector<s16> in{impl->queue.Pop()};
213 const std::size_t num_in{in.size() / num_channels};
214 s16* const out{reinterpret_cast<s16*>(buffer)};
215 const std::size_t out_frames =
216 impl->time_stretch.Process(in.data(), num_in, out, num_frames);
217 samples_written = out_frames * num_channels;
218
219 if (impl->should_flush) {
220 impl->time_stretch.Flush();
221 impl->should_flush = false;
222 }
223 } else {
224 samples_written = impl->queue.Pop(buffer, samples_to_write);
225 }*/
226 samples_written = impl->queue.Pop(buffer, samples_to_write);
227 206
228 if (samples_written >= num_channels) { 207 if (samples_written >= num_channels) {
229 std::memcpy(&impl->last_frame[0], buffer + (samples_written - num_channels) * sizeof(s16), 208 std::memcpy(&impl->last_frame[0], buffer + (samples_written - num_channels) * sizeof(s16),
diff --git a/src/audio_core/sdl2_sink.cpp b/src/audio_core/sdl2_sink.cpp
index 62d3716a6..2d14ce2cb 100644
--- a/src/audio_core/sdl2_sink.cpp
+++ b/src/audio_core/sdl2_sink.cpp
@@ -7,7 +7,6 @@
7#include <cstring> 7#include <cstring>
8#include "audio_core/sdl2_sink.h" 8#include "audio_core/sdl2_sink.h"
9#include "audio_core/stream.h" 9#include "audio_core/stream.h"
10#include "audio_core/time_stretch.h"
11#include "common/assert.h" 10#include "common/assert.h"
12#include "common/logging/log.h" 11#include "common/logging/log.h"
13//#include "common/settings.h" 12//#include "common/settings.h"
@@ -27,7 +26,7 @@ namespace AudioCore {
27class SDLSinkStream final : public SinkStream { 26class SDLSinkStream final : public SinkStream {
28public: 27public:
29 SDLSinkStream(u32 sample_rate, u32 num_channels_, const std::string& output_device) 28 SDLSinkStream(u32 sample_rate, u32 num_channels_, const std::string& output_device)
30 : num_channels{std::min(num_channels_, 6u)}, time_stretch{sample_rate, num_channels} { 29 : num_channels{std::min(num_channels_, 6u)} {
31 30
32 SDL_AudioSpec spec; 31 SDL_AudioSpec spec;
33 spec.freq = sample_rate; 32 spec.freq = sample_rate;
@@ -116,7 +115,6 @@ private:
116 SDL_AudioDeviceID dev = 0; 115 SDL_AudioDeviceID dev = 0;
117 u32 num_channels{}; 116 u32 num_channels{};
118 std::atomic<bool> should_flush{}; 117 std::atomic<bool> should_flush{};
119 TimeStretcher time_stretch;
120}; 118};
121 119
122SDLSink::SDLSink(std::string_view target_device_name) { 120SDLSink::SDLSink(std::string_view target_device_name) {
diff --git a/src/audio_core/time_stretch.cpp b/src/audio_core/time_stretch.cpp
deleted file mode 100644
index 726591fce..000000000
--- a/src/audio_core/time_stretch.cpp
+++ /dev/null
@@ -1,68 +0,0 @@
1// Copyright 2018 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <algorithm>
6#include <cmath>
7#include <cstddef>
8#include "audio_core/time_stretch.h"
9#include "common/logging/log.h"
10
11namespace AudioCore {
12
13TimeStretcher::TimeStretcher(u32 sample_rate, u32 channel_count) : m_sample_rate{sample_rate} {
14 m_sound_touch.setChannels(channel_count);
15 m_sound_touch.setSampleRate(sample_rate);
16 m_sound_touch.setPitch(1.0);
17 m_sound_touch.setTempo(1.0);
18}
19
20void TimeStretcher::Clear() {
21 m_sound_touch.clear();
22}
23
24void TimeStretcher::Flush() {
25 m_sound_touch.flush();
26}
27
28std::size_t TimeStretcher::Process(const s16* in, std::size_t num_in, s16* out,
29 std::size_t num_out) {
30 const double time_delta = static_cast<double>(num_out) / m_sample_rate; // seconds
31
32 // We were given actual_samples number of samples, and num_samples were requested from us.
33 double current_ratio = static_cast<double>(num_in) / static_cast<double>(num_out);
34
35 const double max_latency = 0.25; // seconds
36 const double max_backlog = m_sample_rate * max_latency;
37 const double backlog_fullness = m_sound_touch.numSamples() / max_backlog;
38 if (backlog_fullness > 4.0) {
39 // Too many samples in backlog: Don't push anymore on
40 num_in = 0;
41 }
42
43 // We ideally want the backlog to be about 50% full.
44 // This gives some headroom both ways to prevent underflow and overflow.
45 // We tweak current_ratio to encourage this.
46 constexpr double tweak_time_scale = 0.05; // seconds
47 const double tweak_correction = (backlog_fullness - 0.5) * (time_delta / tweak_time_scale);
48 current_ratio *= std::pow(1.0 + 2.0 * tweak_correction, tweak_correction < 0 ? 3.0 : 1.0);
49
50 // This low-pass filter smoothes out variance in the calculated stretch ratio.
51 // The time-scale determines how responsive this filter is.
52 constexpr double lpf_time_scale = 0.712; // seconds
53 const double lpf_gain = 1.0 - std::exp(-time_delta / lpf_time_scale);
54 m_stretch_ratio += lpf_gain * (current_ratio - m_stretch_ratio);
55
56 // Place a lower limit of 5% speed. When a game boots up, there will be
57 // many silence samples. These do not need to be timestretched.
58 m_stretch_ratio = std::max(m_stretch_ratio, 0.05);
59 m_sound_touch.setTempo(m_stretch_ratio);
60
61 LOG_TRACE(Audio, "{:5}/{:5} ratio:{:0.6f} backlog:{:0.6f}", num_in, num_out, m_stretch_ratio,
62 backlog_fullness);
63
64 m_sound_touch.putSamples(in, static_cast<u32>(num_in));
65 return m_sound_touch.receiveSamples(out, static_cast<u32>(num_out));
66}
67
68} // namespace AudioCore
diff --git a/src/audio_core/time_stretch.h b/src/audio_core/time_stretch.h
deleted file mode 100644
index bb2270b96..000000000
--- a/src/audio_core/time_stretch.h
+++ /dev/null
@@ -1,34 +0,0 @@
1// Copyright 2018 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <cstddef>
8#include <SoundTouch.h>
9#include "common/common_types.h"
10
11namespace AudioCore {
12
13class TimeStretcher {
14public:
15 TimeStretcher(u32 sample_rate, u32 channel_count);
16
17 /// @param in Input sample buffer
18 /// @param num_in Number of input frames in `in`
19 /// @param out Output sample buffer
20 /// @param num_out Desired number of output frames in `out`
21 /// @returns Actual number of frames written to `out`
22 std::size_t Process(const s16* in, std::size_t num_in, s16* out, std::size_t num_out);
23
24 void Clear();
25
26 void Flush();
27
28private:
29 u32 m_sample_rate;
30 soundtouch::SoundTouch m_sound_touch;
31 double m_stretch_ratio = 1.0;
32};
33
34} // namespace AudioCore
diff --git a/src/common/atomic_ops.h b/src/common/atomic_ops.h
index b94d73c7a..69fde8421 100644
--- a/src/common/atomic_ops.h
+++ b/src/common/atomic_ops.h
@@ -46,6 +46,50 @@ namespace Common {
46 reinterpret_cast<__int64*>(expected.data())) != 0; 46 reinterpret_cast<__int64*>(expected.data())) != 0;
47} 47}
48 48
49[[nodiscard]] inline bool AtomicCompareAndSwap(volatile u8* pointer, u8 value, u8 expected,
50 u8& actual) {
51 actual =
52 _InterlockedCompareExchange8(reinterpret_cast<volatile char*>(pointer), value, expected);
53 return actual == expected;
54}
55
56[[nodiscard]] inline bool AtomicCompareAndSwap(volatile u16* pointer, u16 value, u16 expected,
57 u16& actual) {
58 actual =
59 _InterlockedCompareExchange16(reinterpret_cast<volatile short*>(pointer), value, expected);
60 return actual == expected;
61}
62
63[[nodiscard]] inline bool AtomicCompareAndSwap(volatile u32* pointer, u32 value, u32 expected,
64 u32& actual) {
65 actual =
66 _InterlockedCompareExchange(reinterpret_cast<volatile long*>(pointer), value, expected);
67 return actual == expected;
68}
69
70[[nodiscard]] inline bool AtomicCompareAndSwap(volatile u64* pointer, u64 value, u64 expected,
71 u64& actual) {
72 actual = _InterlockedCompareExchange64(reinterpret_cast<volatile __int64*>(pointer), value,
73 expected);
74 return actual == expected;
75}
76
77[[nodiscard]] inline bool AtomicCompareAndSwap(volatile u64* pointer, u128 value, u128 expected,
78 u128& actual) {
79 const bool result =
80 _InterlockedCompareExchange128(reinterpret_cast<volatile __int64*>(pointer), value[1],
81 value[0], reinterpret_cast<__int64*>(expected.data())) != 0;
82 actual = expected;
83 return result;
84}
85
86[[nodiscard]] inline u128 AtomicLoad128(volatile u64* pointer) {
87 u128 result{};
88 _InterlockedCompareExchange128(reinterpret_cast<volatile __int64*>(pointer), result[1],
89 result[0], reinterpret_cast<__int64*>(result.data()));
90 return result;
91}
92
49#else 93#else
50 94
51[[nodiscard]] inline bool AtomicCompareAndSwap(volatile u8* pointer, u8 value, u8 expected) { 95[[nodiscard]] inline bool AtomicCompareAndSwap(volatile u8* pointer, u8 value, u8 expected) {
@@ -72,6 +116,52 @@ namespace Common {
72 return __sync_bool_compare_and_swap((unsigned __int128*)pointer, expected_a, value_a); 116 return __sync_bool_compare_and_swap((unsigned __int128*)pointer, expected_a, value_a);
73} 117}
74 118
119[[nodiscard]] inline bool AtomicCompareAndSwap(volatile u8* pointer, u8 value, u8 expected,
120 u8& actual) {
121 actual = __sync_val_compare_and_swap(pointer, expected, value);
122 return actual == expected;
123}
124
125[[nodiscard]] inline bool AtomicCompareAndSwap(volatile u16* pointer, u16 value, u16 expected,
126 u16& actual) {
127 actual = __sync_val_compare_and_swap(pointer, expected, value);
128 return actual == expected;
129}
130
131[[nodiscard]] inline bool AtomicCompareAndSwap(volatile u32* pointer, u32 value, u32 expected,
132 u32& actual) {
133 actual = __sync_val_compare_and_swap(pointer, expected, value);
134 return actual == expected;
135}
136
137[[nodiscard]] inline bool AtomicCompareAndSwap(volatile u64* pointer, u64 value, u64 expected,
138 u64& actual) {
139 actual = __sync_val_compare_and_swap(pointer, expected, value);
140 return actual == expected;
141}
142
143[[nodiscard]] inline bool AtomicCompareAndSwap(volatile u64* pointer, u128 value, u128 expected,
144 u128& actual) {
145 unsigned __int128 value_a;
146 unsigned __int128 expected_a;
147 unsigned __int128 actual_a;
148 std::memcpy(&value_a, value.data(), sizeof(u128));
149 std::memcpy(&expected_a, expected.data(), sizeof(u128));
150 actual_a = __sync_val_compare_and_swap((unsigned __int128*)pointer, expected_a, value_a);
151 std::memcpy(actual.data(), &actual_a, sizeof(u128));
152 return actual_a == expected_a;
153}
154
155[[nodiscard]] inline u128 AtomicLoad128(volatile u64* pointer) {
156 unsigned __int128 zeros_a = 0;
157 unsigned __int128 result_a =
158 __sync_val_compare_and_swap((unsigned __int128*)pointer, zeros_a, zeros_a);
159
160 u128 result;
161 std::memcpy(result.data(), &result_a, sizeof(u128));
162 return result;
163}
164
75#endif 165#endif
76 166
77} // namespace Common 167} // namespace Common
diff --git a/src/common/fiber.cpp b/src/common/fiber.cpp
index 81b212e4b..177a74deb 100644
--- a/src/common/fiber.cpp
+++ b/src/common/fiber.cpp
@@ -2,9 +2,10 @@
2// Licensed under GPLv2 or any later version 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <mutex>
6
5#include "common/assert.h" 7#include "common/assert.h"
6#include "common/fiber.h" 8#include "common/fiber.h"
7#include "common/spin_lock.h"
8#include "common/virtual_buffer.h" 9#include "common/virtual_buffer.h"
9 10
10#include <boost/context/detail/fcontext.hpp> 11#include <boost/context/detail/fcontext.hpp>
@@ -19,7 +20,7 @@ struct Fiber::FiberImpl {
19 VirtualBuffer<u8> stack; 20 VirtualBuffer<u8> stack;
20 VirtualBuffer<u8> rewind_stack; 21 VirtualBuffer<u8> rewind_stack;
21 22
22 SpinLock guard{}; 23 std::mutex guard;
23 std::function<void(void*)> entry_point; 24 std::function<void(void*)> entry_point;
24 std::function<void(void*)> rewind_point; 25 std::function<void(void*)> rewind_point;
25 void* rewind_parameter{}; 26 void* rewind_parameter{};
diff --git a/src/common/host_memory.cpp b/src/common/host_memory.cpp
index e829af1ac..802943eb7 100644
--- a/src/common/host_memory.cpp
+++ b/src/common/host_memory.cpp
@@ -149,7 +149,7 @@ public:
149 } 149 }
150 150
151 void Unmap(size_t virtual_offset, size_t length) { 151 void Unmap(size_t virtual_offset, size_t length) {
152 std::lock_guard lock{placeholder_mutex}; 152 std::scoped_lock lock{placeholder_mutex};
153 153
154 // Unmap until there are no more placeholders 154 // Unmap until there are no more placeholders
155 while (UnmapOnePlaceholder(virtual_offset, length)) { 155 while (UnmapOnePlaceholder(virtual_offset, length)) {
@@ -169,7 +169,7 @@ public:
169 } 169 }
170 const size_t virtual_end = virtual_offset + length; 170 const size_t virtual_end = virtual_offset + length;
171 171
172 std::lock_guard lock{placeholder_mutex}; 172 std::scoped_lock lock{placeholder_mutex};
173 auto [it, end] = placeholders.equal_range({virtual_offset, virtual_end}); 173 auto [it, end] = placeholders.equal_range({virtual_offset, virtual_end});
174 while (it != end) { 174 while (it != end) {
175 const size_t offset = std::max(it->lower(), virtual_offset); 175 const size_t offset = std::max(it->lower(), virtual_offset);
diff --git a/src/common/logging/filter.cpp b/src/common/logging/filter.cpp
index 4afc1369a..4acbff649 100644
--- a/src/common/logging/filter.cpp
+++ b/src/common/logging/filter.cpp
@@ -101,6 +101,7 @@ bool ParseFilterRule(Filter& instance, Iterator begin, Iterator end) {
101 SUB(Service, GRC) \ 101 SUB(Service, GRC) \
102 SUB(Service, HID) \ 102 SUB(Service, HID) \
103 SUB(Service, IRS) \ 103 SUB(Service, IRS) \
104 SUB(Service, JIT) \
104 SUB(Service, LBL) \ 105 SUB(Service, LBL) \
105 SUB(Service, LDN) \ 106 SUB(Service, LDN) \
106 SUB(Service, LDR) \ 107 SUB(Service, LDR) \
@@ -119,6 +120,7 @@ bool ParseFilterRule(Filter& instance, Iterator begin, Iterator end) {
119 SUB(Service, NPNS) \ 120 SUB(Service, NPNS) \
120 SUB(Service, NS) \ 121 SUB(Service, NS) \
121 SUB(Service, NVDRV) \ 122 SUB(Service, NVDRV) \
123 SUB(Service, NVFlinger) \
122 SUB(Service, OLSC) \ 124 SUB(Service, OLSC) \
123 SUB(Service, PCIE) \ 125 SUB(Service, PCIE) \
124 SUB(Service, PCTL) \ 126 SUB(Service, PCTL) \
diff --git a/src/common/logging/types.h b/src/common/logging/types.h
index 2b6e4daa7..99c15fa96 100644
--- a/src/common/logging/types.h
+++ b/src/common/logging/types.h
@@ -69,6 +69,7 @@ enum class Class : u8 {
69 Service_GRC, ///< The game recording service 69 Service_GRC, ///< The game recording service
70 Service_HID, ///< The HID (Human interface device) service 70 Service_HID, ///< The HID (Human interface device) service
71 Service_IRS, ///< The IRS service 71 Service_IRS, ///< The IRS service
72 Service_JIT, ///< The JIT service
72 Service_LBL, ///< The LBL (LCD backlight) service 73 Service_LBL, ///< The LBL (LCD backlight) service
73 Service_LDN, ///< The LDN (Local domain network) service 74 Service_LDN, ///< The LDN (Local domain network) service
74 Service_LDR, ///< The loader service 75 Service_LDR, ///< The loader service
@@ -87,6 +88,7 @@ enum class Class : u8 {
87 Service_NPNS, ///< The NPNS service 88 Service_NPNS, ///< The NPNS service
88 Service_NS, ///< The NS services 89 Service_NS, ///< The NS services
89 Service_NVDRV, ///< The NVDRV (Nvidia driver) service 90 Service_NVDRV, ///< The NVDRV (Nvidia driver) service
91 Service_NVFlinger, ///< The NVFlinger service
90 Service_OLSC, ///< The OLSC service 92 Service_OLSC, ///< The OLSC service
91 Service_PCIE, ///< The PCIe service 93 Service_PCIE, ///< The PCIe service
92 Service_PCTL, ///< The PCTL (Parental control) service 94 Service_PCTL, ///< The PCTL (Parental control) service
diff --git a/src/common/math_util.h b/src/common/math_util.h
index 510c4e56d..54485bf53 100644
--- a/src/common/math_util.h
+++ b/src/common/math_util.h
@@ -4,6 +4,7 @@
4 4
5#pragma once 5#pragma once
6 6
7#include <algorithm>
7#include <cstdlib> 8#include <cstdlib>
8#include <type_traits> 9#include <type_traits>
9 10
@@ -20,10 +21,32 @@ struct Rectangle {
20 21
21 constexpr Rectangle() = default; 22 constexpr Rectangle() = default;
22 23
24 constexpr Rectangle(T width, T height) : right(width), bottom(height) {}
25
23 constexpr Rectangle(T left_, T top_, T right_, T bottom_) 26 constexpr Rectangle(T left_, T top_, T right_, T bottom_)
24 : left(left_), top(top_), right(right_), bottom(bottom_) {} 27 : left(left_), top(top_), right(right_), bottom(bottom_) {}
25 28
26 [[nodiscard]] T GetWidth() const { 29 [[nodiscard]] constexpr T Left() const {
30 return left;
31 }
32
33 [[nodiscard]] constexpr T Top() const {
34 return top;
35 }
36
37 [[nodiscard]] constexpr T Right() const {
38 return right;
39 }
40
41 [[nodiscard]] constexpr T Bottom() const {
42 return bottom;
43 }
44
45 [[nodiscard]] constexpr bool IsEmpty() const {
46 return (GetWidth() <= 0) || (GetHeight() <= 0);
47 }
48
49 [[nodiscard]] constexpr T GetWidth() const {
27 if constexpr (std::is_floating_point_v<T>) { 50 if constexpr (std::is_floating_point_v<T>) {
28 return std::abs(right - left); 51 return std::abs(right - left);
29 } else { 52 } else {
@@ -31,7 +54,7 @@ struct Rectangle {
31 } 54 }
32 } 55 }
33 56
34 [[nodiscard]] T GetHeight() const { 57 [[nodiscard]] constexpr T GetHeight() const {
35 if constexpr (std::is_floating_point_v<T>) { 58 if constexpr (std::is_floating_point_v<T>) {
36 return std::abs(bottom - top); 59 return std::abs(bottom - top);
37 } else { 60 } else {
@@ -39,18 +62,35 @@ struct Rectangle {
39 } 62 }
40 } 63 }
41 64
42 [[nodiscard]] Rectangle<T> TranslateX(const T x) const { 65 [[nodiscard]] constexpr Rectangle<T> TranslateX(const T x) const {
43 return Rectangle{left + x, top, right + x, bottom}; 66 return Rectangle{left + x, top, right + x, bottom};
44 } 67 }
45 68
46 [[nodiscard]] Rectangle<T> TranslateY(const T y) const { 69 [[nodiscard]] constexpr Rectangle<T> TranslateY(const T y) const {
47 return Rectangle{left, top + y, right, bottom + y}; 70 return Rectangle{left, top + y, right, bottom + y};
48 } 71 }
49 72
50 [[nodiscard]] Rectangle<T> Scale(const float s) const { 73 [[nodiscard]] constexpr Rectangle<T> Scale(const float s) const {
51 return Rectangle{left, top, static_cast<T>(static_cast<float>(left + GetWidth()) * s), 74 return Rectangle{left, top, static_cast<T>(static_cast<float>(left + GetWidth()) * s),
52 static_cast<T>(static_cast<float>(top + GetHeight()) * s)}; 75 static_cast<T>(static_cast<float>(top + GetHeight()) * s)};
53 } 76 }
77
78 [[nodiscard]] constexpr bool operator==(const Rectangle<T>& rhs) const {
79 return (left == rhs.left) && (top == rhs.top) && (right == rhs.right) &&
80 (bottom == rhs.bottom);
81 }
82
83 [[nodiscard]] constexpr bool operator!=(const Rectangle<T>& rhs) const {
84 return !operator==(rhs);
85 }
86
87 [[nodiscard]] constexpr bool Intersect(const Rectangle<T>& with, Rectangle<T>* result) const {
88 result->left = std::max(left, with.left);
89 result->top = std::max(top, with.top);
90 result->right = std::min(right, with.right);
91 result->bottom = std::min(bottom, with.bottom);
92 return !result->IsEmpty();
93 }
54}; 94};
55 95
56template <typename T> 96template <typename T>
diff --git a/src/common/settings.h b/src/common/settings.h
index a37d83fb3..3b7be63b3 100644
--- a/src/common/settings.h
+++ b/src/common/settings.h
@@ -38,6 +38,7 @@ enum class CPUAccuracy : u32 {
38 Auto = 0, 38 Auto = 0,
39 Accurate = 1, 39 Accurate = 1,
40 Unsafe = 2, 40 Unsafe = 2,
41 Paranoid = 3,
41}; 42};
42 43
43enum class FullscreenMode : u32 { 44enum class FullscreenMode : u32 {
@@ -470,7 +471,7 @@ struct Values {
470 471
471 // Cpu 472 // Cpu
472 RangedSetting<CPUAccuracy> cpu_accuracy{CPUAccuracy::Auto, CPUAccuracy::Auto, 473 RangedSetting<CPUAccuracy> cpu_accuracy{CPUAccuracy::Auto, CPUAccuracy::Auto,
473 CPUAccuracy::Unsafe, "cpu_accuracy"}; 474 CPUAccuracy::Paranoid, "cpu_accuracy"};
474 // TODO: remove cpu_accuracy_first_time, migration setting added 8 July 2021 475 // TODO: remove cpu_accuracy_first_time, migration setting added 8 July 2021
475 BasicSetting<bool> cpu_accuracy_first_time{true, "cpu_accuracy_first_time"}; 476 BasicSetting<bool> cpu_accuracy_first_time{true, "cpu_accuracy_first_time"};
476 BasicSetting<bool> cpu_debug_mode{false, "cpu_debug_mode"}; 477 BasicSetting<bool> cpu_debug_mode{false, "cpu_debug_mode"};
@@ -589,6 +590,9 @@ struct Values {
589 BasicSetting<int> touch_from_button_map_index{0, "touch_from_button_map"}; 590 BasicSetting<int> touch_from_button_map_index{0, "touch_from_button_map"};
590 std::vector<TouchFromButtonMap> touch_from_button_maps; 591 std::vector<TouchFromButtonMap> touch_from_button_maps;
591 592
593 BasicSetting<bool> enable_ring_controller{true, "enable_ring_controller"};
594 RingconRaw ringcon_analogs;
595
592 // Data Storage 596 // Data Storage
593 BasicSetting<bool> use_virtual_sd{true, "use_virtual_sd"}; 597 BasicSetting<bool> use_virtual_sd{true, "use_virtual_sd"};
594 BasicSetting<bool> gamecard_inserted{false, "gamecard_inserted"}; 598 BasicSetting<bool> gamecard_inserted{false, "gamecard_inserted"};
diff --git a/src/common/settings_input.h b/src/common/settings_input.h
index 4ff37e186..6f42346bc 100644
--- a/src/common/settings_input.h
+++ b/src/common/settings_input.h
@@ -357,6 +357,7 @@ constexpr int NUM_KEYBOARD_MODS_HID = NumKeyboardMods;
357using AnalogsRaw = std::array<std::string, NativeAnalog::NumAnalogs>; 357using AnalogsRaw = std::array<std::string, NativeAnalog::NumAnalogs>;
358using ButtonsRaw = std::array<std::string, NativeButton::NumButtons>; 358using ButtonsRaw = std::array<std::string, NativeButton::NumButtons>;
359using MotionsRaw = std::array<std::string, NativeMotion::NumMotions>; 359using MotionsRaw = std::array<std::string, NativeMotion::NumMotions>;
360using RingconRaw = std::string;
360 361
361constexpr u32 JOYCON_BODY_NEON_RED = 0xFF3C28; 362constexpr u32 JOYCON_BODY_NEON_RED = 0xFF3C28;
362constexpr u32 JOYCON_BUTTONS_NEON_RED = 0x1E0A0A; 363constexpr u32 JOYCON_BUTTONS_NEON_RED = 0x1E0A0A;
diff --git a/src/common/thread.h b/src/common/thread.h
index a8c17c71a..626609372 100644
--- a/src/common/thread.h
+++ b/src/common/thread.h
@@ -17,7 +17,7 @@ namespace Common {
17class Event { 17class Event {
18public: 18public:
19 void Set() { 19 void Set() {
20 std::lock_guard lk{mutex}; 20 std::scoped_lock lk{mutex};
21 if (!is_set) { 21 if (!is_set) {
22 is_set = true; 22 is_set = true;
23 condvar.notify_one(); 23 condvar.notify_one();
diff --git a/src/common/threadsafe_queue.h b/src/common/threadsafe_queue.h
index 2c8c2b90e..7272ac6e8 100644
--- a/src/common/threadsafe_queue.h
+++ b/src/common/threadsafe_queue.h
@@ -52,7 +52,7 @@ public:
52 // line before cv.wait 52 // line before cv.wait
53 // TODO(bunnei): This can be replaced with C++20 waitable atomics when properly supported. 53 // TODO(bunnei): This can be replaced with C++20 waitable atomics when properly supported.
54 // See discussion on https://github.com/yuzu-emu/yuzu/pull/3173 for details. 54 // See discussion on https://github.com/yuzu-emu/yuzu/pull/3173 for details.
55 std::lock_guard lock{cv_mutex}; 55 std::scoped_lock lock{cv_mutex};
56 cv.notify_one(); 56 cv.notify_one();
57 } 57 }
58 58
@@ -159,7 +159,7 @@ public:
159 159
160 template <typename Arg> 160 template <typename Arg>
161 void Push(Arg&& t) { 161 void Push(Arg&& t) {
162 std::lock_guard lock{write_lock}; 162 std::scoped_lock lock{write_lock};
163 spsc_queue.Push(t); 163 spsc_queue.Push(t);
164 } 164 }
165 165
diff --git a/src/common/x64/native_clock.cpp b/src/common/x64/native_clock.cpp
index 347e41efc..7fd9d22f8 100644
--- a/src/common/x64/native_clock.cpp
+++ b/src/common/x64/native_clock.cpp
@@ -10,25 +10,49 @@
10#include "common/uint128.h" 10#include "common/uint128.h"
11#include "common/x64/native_clock.h" 11#include "common/x64/native_clock.h"
12 12
13#ifdef _MSC_VER
14#include <intrin.h>
15#endif
16
13namespace Common { 17namespace Common {
14 18
19#ifdef _MSC_VER
20__forceinline static u64 FencedRDTSC() {
21 _mm_lfence();
22 _ReadWriteBarrier();
23 const u64 result = __rdtsc();
24 _mm_lfence();
25 _ReadWriteBarrier();
26 return result;
27}
28#else
29static u64 FencedRDTSC() {
30 u64 result;
31 asm volatile("lfence\n\t"
32 "rdtsc\n\t"
33 "shl $32, %%rdx\n\t"
34 "or %%rdx, %0\n\t"
35 "lfence"
36 : "=a"(result)
37 :
38 : "rdx", "memory", "cc");
39 return result;
40}
41#endif
42
15u64 EstimateRDTSCFrequency() { 43u64 EstimateRDTSCFrequency() {
16 // Discard the first result measuring the rdtsc. 44 // Discard the first result measuring the rdtsc.
17 _mm_mfence(); 45 FencedRDTSC();
18 __rdtsc();
19 std::this_thread::sleep_for(std::chrono::milliseconds{1}); 46 std::this_thread::sleep_for(std::chrono::milliseconds{1});
20 _mm_mfence(); 47 FencedRDTSC();
21 __rdtsc();
22 48
23 // Get the current time. 49 // Get the current time.
24 const auto start_time = std::chrono::steady_clock::now(); 50 const auto start_time = std::chrono::steady_clock::now();
25 _mm_mfence(); 51 const u64 tsc_start = FencedRDTSC();
26 const u64 tsc_start = __rdtsc();
27 // Wait for 200 milliseconds. 52 // Wait for 200 milliseconds.
28 std::this_thread::sleep_for(std::chrono::milliseconds{200}); 53 std::this_thread::sleep_for(std::chrono::milliseconds{200});
29 const auto end_time = std::chrono::steady_clock::now(); 54 const auto end_time = std::chrono::steady_clock::now();
30 _mm_mfence(); 55 const u64 tsc_end = FencedRDTSC();
31 const u64 tsc_end = __rdtsc();
32 // Calculate differences. 56 // Calculate differences.
33 const u64 timer_diff = static_cast<u64>( 57 const u64 timer_diff = static_cast<u64>(
34 std::chrono::duration_cast<std::chrono::nanoseconds>(end_time - start_time).count()); 58 std::chrono::duration_cast<std::chrono::nanoseconds>(end_time - start_time).count());
@@ -42,8 +66,7 @@ NativeClock::NativeClock(u64 emulated_cpu_frequency_, u64 emulated_clock_frequen
42 u64 rtsc_frequency_) 66 u64 rtsc_frequency_)
43 : WallClock(emulated_cpu_frequency_, emulated_clock_frequency_, true), rtsc_frequency{ 67 : WallClock(emulated_cpu_frequency_, emulated_clock_frequency_, true), rtsc_frequency{
44 rtsc_frequency_} { 68 rtsc_frequency_} {
45 _mm_mfence(); 69 time_point.inner.last_measure = FencedRDTSC();
46 time_point.inner.last_measure = __rdtsc();
47 time_point.inner.accumulated_ticks = 0U; 70 time_point.inner.accumulated_ticks = 0U;
48 ns_rtsc_factor = GetFixedPoint64Factor(NS_RATIO, rtsc_frequency); 71 ns_rtsc_factor = GetFixedPoint64Factor(NS_RATIO, rtsc_frequency);
49 us_rtsc_factor = GetFixedPoint64Factor(US_RATIO, rtsc_frequency); 72 us_rtsc_factor = GetFixedPoint64Factor(US_RATIO, rtsc_frequency);
@@ -55,10 +78,10 @@ NativeClock::NativeClock(u64 emulated_cpu_frequency_, u64 emulated_clock_frequen
55u64 NativeClock::GetRTSC() { 78u64 NativeClock::GetRTSC() {
56 TimePoint new_time_point{}; 79 TimePoint new_time_point{};
57 TimePoint current_time_point{}; 80 TimePoint current_time_point{};
81
82 current_time_point.pack = Common::AtomicLoad128(time_point.pack.data());
58 do { 83 do {
59 current_time_point.pack = time_point.pack; 84 const u64 current_measure = FencedRDTSC();
60 _mm_mfence();
61 const u64 current_measure = __rdtsc();
62 u64 diff = current_measure - current_time_point.inner.last_measure; 85 u64 diff = current_measure - current_time_point.inner.last_measure;
63 diff = diff & ~static_cast<u64>(static_cast<s64>(diff) >> 63); // max(diff, 0) 86 diff = diff & ~static_cast<u64>(static_cast<s64>(diff) >> 63); // max(diff, 0)
64 new_time_point.inner.last_measure = current_measure > current_time_point.inner.last_measure 87 new_time_point.inner.last_measure = current_measure > current_time_point.inner.last_measure
@@ -66,7 +89,7 @@ u64 NativeClock::GetRTSC() {
66 : current_time_point.inner.last_measure; 89 : current_time_point.inner.last_measure;
67 new_time_point.inner.accumulated_ticks = current_time_point.inner.accumulated_ticks + diff; 90 new_time_point.inner.accumulated_ticks = current_time_point.inner.accumulated_ticks + diff;
68 } while (!Common::AtomicCompareAndSwap(time_point.pack.data(), new_time_point.pack, 91 } while (!Common::AtomicCompareAndSwap(time_point.pack.data(), new_time_point.pack,
69 current_time_point.pack)); 92 current_time_point.pack, current_time_point.pack));
70 /// The clock cannot be more precise than the guest timer, remove the lower bits 93 /// The clock cannot be more precise than the guest timer, remove the lower bits
71 return new_time_point.inner.accumulated_ticks & inaccuracy_mask; 94 return new_time_point.inner.accumulated_ticks & inaccuracy_mask;
72} 95}
@@ -75,13 +98,13 @@ void NativeClock::Pause(bool is_paused) {
75 if (!is_paused) { 98 if (!is_paused) {
76 TimePoint current_time_point{}; 99 TimePoint current_time_point{};
77 TimePoint new_time_point{}; 100 TimePoint new_time_point{};
101
102 current_time_point.pack = Common::AtomicLoad128(time_point.pack.data());
78 do { 103 do {
79 current_time_point.pack = time_point.pack;
80 new_time_point.pack = current_time_point.pack; 104 new_time_point.pack = current_time_point.pack;
81 _mm_mfence(); 105 new_time_point.inner.last_measure = FencedRDTSC();
82 new_time_point.inner.last_measure = __rdtsc();
83 } while (!Common::AtomicCompareAndSwap(time_point.pack.data(), new_time_point.pack, 106 } while (!Common::AtomicCompareAndSwap(time_point.pack.data(), new_time_point.pack,
84 current_time_point.pack)); 107 current_time_point.pack, current_time_point.pack));
85 } 108 }
86} 109}
87 110
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt
index 1d4e92edb..62230bae0 100644
--- a/src/core/CMakeLists.txt
+++ b/src/core/CMakeLists.txt
@@ -13,6 +13,8 @@ add_library(core STATIC
13 arm/dynarmic/arm_exclusive_monitor.h 13 arm/dynarmic/arm_exclusive_monitor.h
14 arm/exclusive_monitor.cpp 14 arm/exclusive_monitor.cpp
15 arm/exclusive_monitor.h 15 arm/exclusive_monitor.h
16 arm/symbols.cpp
17 arm/symbols.h
16 constants.cpp 18 constants.cpp
17 constants.h 19 constants.h
18 core.cpp 20 core.cpp
@@ -122,8 +124,8 @@ add_library(core STATIC
122 frontend/applets/error.h 124 frontend/applets/error.h
123 frontend/applets/general_frontend.cpp 125 frontend/applets/general_frontend.cpp
124 frontend/applets/general_frontend.h 126 frontend/applets/general_frontend.h
125 frontend/applets/mii.cpp 127 frontend/applets/mii_edit.cpp
126 frontend/applets/mii.h 128 frontend/applets/mii_edit.h
127 frontend/applets/profile_select.cpp 129 frontend/applets/profile_select.cpp
128 frontend/applets/profile_select.h 130 frontend/applets/profile_select.h
129 frontend/applets/software_keyboard.cpp 131 frontend/applets/software_keyboard.cpp
@@ -306,8 +308,9 @@ add_library(core STATIC
306 hle/service/am/applets/applet_error.h 308 hle/service/am/applets/applet_error.h
307 hle/service/am/applets/applet_general_backend.cpp 309 hle/service/am/applets/applet_general_backend.cpp
308 hle/service/am/applets/applet_general_backend.h 310 hle/service/am/applets/applet_general_backend.h
309 hle/service/am/applets/applet_mii.cpp 311 hle/service/am/applets/applet_mii_edit.cpp
310 hle/service/am/applets/applet_mii.h 312 hle/service/am/applets/applet_mii_edit.h
313 hle/service/am/applets/applet_mii_edit_types.h
311 hle/service/am/applets/applet_profile_select.cpp 314 hle/service/am/applets/applet_profile_select.cpp
312 hle/service/am/applets/applet_profile_select.h 315 hle/service/am/applets/applet_profile_select.h
313 hle/service/am/applets/applet_software_keyboard.cpp 316 hle/service/am/applets/applet_software_keyboard.cpp
@@ -431,6 +434,8 @@ add_library(core STATIC
431 hle/service/grc/grc.h 434 hle/service/grc/grc.h
432 hle/service/hid/hid.cpp 435 hle/service/hid/hid.cpp
433 hle/service/hid/hid.h 436 hle/service/hid/hid.h
437 hle/service/hid/hidbus.cpp
438 hle/service/hid/hidbus.h
434 hle/service/hid/irs.cpp 439 hle/service/hid/irs.cpp
435 hle/service/hid/irs.h 440 hle/service/hid/irs.h
436 hle/service/hid/ring_lifo.h 441 hle/service/hid/ring_lifo.h
@@ -457,6 +462,18 @@ add_library(core STATIC
457 hle/service/hid/controllers/touchscreen.h 462 hle/service/hid/controllers/touchscreen.h
458 hle/service/hid/controllers/xpad.cpp 463 hle/service/hid/controllers/xpad.cpp
459 hle/service/hid/controllers/xpad.h 464 hle/service/hid/controllers/xpad.h
465 hle/service/hid/hidbus/hidbus_base.cpp
466 hle/service/hid/hidbus/hidbus_base.h
467 hle/service/hid/hidbus/ringcon.cpp
468 hle/service/hid/hidbus/ringcon.h
469 hle/service/hid/hidbus/starlink.cpp
470 hle/service/hid/hidbus/starlink.h
471 hle/service/hid/hidbus/stubbed.cpp
472 hle/service/hid/hidbus/stubbed.h
473 hle/service/jit/jit_context.cpp
474 hle/service/jit/jit_context.h
475 hle/service/jit/jit.cpp
476 hle/service/jit/jit.h
460 hle/service/lbl/lbl.cpp 477 hle/service/lbl/lbl.cpp
461 hle/service/lbl/lbl.h 478 hle/service/lbl/lbl.h
462 hle/service/ldn/errors.h 479 hle/service/ldn/errors.h
@@ -534,10 +551,35 @@ add_library(core STATIC
534 hle/service/nvdrv/nvmemp.h 551 hle/service/nvdrv/nvmemp.h
535 hle/service/nvdrv/syncpoint_manager.cpp 552 hle/service/nvdrv/syncpoint_manager.cpp
536 hle/service/nvdrv/syncpoint_manager.h 553 hle/service/nvdrv/syncpoint_manager.h
537 hle/service/nvflinger/buffer_queue.cpp 554 hle/service/nvflinger/binder.h
538 hle/service/nvflinger/buffer_queue.h 555 hle/service/nvflinger/buffer_item.h
556 hle/service/nvflinger/buffer_item_consumer.cpp
557 hle/service/nvflinger/buffer_item_consumer.h
558 hle/service/nvflinger/buffer_queue_consumer.cpp
559 hle/service/nvflinger/buffer_queue_consumer.h
560 hle/service/nvflinger/buffer_queue_core.cpp
561 hle/service/nvflinger/buffer_queue_core.h
562 hle/service/nvflinger/buffer_queue_defs.h
563 hle/service/nvflinger/buffer_queue_producer.cpp
564 hle/service/nvflinger/buffer_queue_producer.h
565 hle/service/nvflinger/buffer_slot.h
566 hle/service/nvflinger/buffer_transform_flags.h
567 hle/service/nvflinger/consumer_base.cpp
568 hle/service/nvflinger/consumer_base.h
569 hle/service/nvflinger/consumer_listener.h
570 hle/service/nvflinger/graphic_buffer_producer.cpp
571 hle/service/nvflinger/graphic_buffer_producer.h
572 hle/service/nvflinger/hos_binder_driver_server.cpp
573 hle/service/nvflinger/hos_binder_driver_server.h
539 hle/service/nvflinger/nvflinger.cpp 574 hle/service/nvflinger/nvflinger.cpp
540 hle/service/nvflinger/nvflinger.h 575 hle/service/nvflinger/nvflinger.h
576 hle/service/nvflinger/parcel.h
577 hle/service/nvflinger/pixel_format.h
578 hle/service/nvflinger/producer_listener.h
579 hle/service/nvflinger/status.h
580 hle/service/nvflinger/ui/fence.h
581 hle/service/nvflinger/ui/graphic_buffer.h
582 hle/service/nvflinger/window.h
541 hle/service/olsc/olsc.cpp 583 hle/service/olsc/olsc.cpp
542 hle/service/olsc/olsc.h 584 hle/service/olsc/olsc.h
543 hle/service/pcie/pcie.cpp 585 hle/service/pcie/pcie.cpp
diff --git a/src/core/arm/arm_interface.cpp b/src/core/arm/arm_interface.cpp
index 0951e1976..2a9390e26 100644
--- a/src/core/arm/arm_interface.cpp
+++ b/src/core/arm/arm_interface.cpp
@@ -8,226 +8,41 @@
8#include "common/common_types.h" 8#include "common/common_types.h"
9#include "common/logging/log.h" 9#include "common/logging/log.h"
10#include "core/arm/arm_interface.h" 10#include "core/arm/arm_interface.h"
11#include "core/arm/symbols.h"
11#include "core/core.h" 12#include "core/core.h"
13#include "core/hle/kernel/k_process.h"
12#include "core/loader/loader.h" 14#include "core/loader/loader.h"
13#include "core/memory.h" 15#include "core/memory.h"
14 16
15namespace Core { 17#include "core/arm/dynarmic/arm_dynarmic_32.h"
16namespace { 18#include "core/arm/dynarmic/arm_dynarmic_64.h"
17
18constexpr u64 ELF_DYNAMIC_TAG_NULL = 0;
19constexpr u64 ELF_DYNAMIC_TAG_STRTAB = 5;
20constexpr u64 ELF_DYNAMIC_TAG_SYMTAB = 6;
21constexpr u64 ELF_DYNAMIC_TAG_SYMENT = 11;
22
23enum class ELFSymbolType : u8 {
24 None = 0,
25 Object = 1,
26 Function = 2,
27 Section = 3,
28 File = 4,
29 Common = 5,
30 TLS = 6,
31};
32
33enum class ELFSymbolBinding : u8 {
34 Local = 0,
35 Global = 1,
36 Weak = 2,
37};
38
39enum class ELFSymbolVisibility : u8 {
40 Default = 0,
41 Internal = 1,
42 Hidden = 2,
43 Protected = 3,
44};
45
46struct ELFSymbol {
47 u32 name_index;
48 union {
49 u8 info;
50
51 BitField<0, 4, ELFSymbolType> type;
52 BitField<4, 4, ELFSymbolBinding> binding;
53 };
54 ELFSymbolVisibility visibility;
55 u16 sh_index;
56 u64 value;
57 u64 size;
58};
59static_assert(sizeof(ELFSymbol) == 0x18, "ELFSymbol has incorrect size.");
60
61using Symbols = std::vector<std::pair<ELFSymbol, std::string>>;
62
63Symbols GetSymbols(VAddr text_offset, Core::Memory::Memory& memory) {
64 const auto mod_offset = text_offset + memory.Read32(text_offset + 4);
65
66 if (mod_offset < text_offset || (mod_offset & 0b11) != 0 ||
67 memory.Read32(mod_offset) != Common::MakeMagic('M', 'O', 'D', '0')) {
68 return {};
69 }
70
71 const auto dynamic_offset = memory.Read32(mod_offset + 0x4) + mod_offset;
72
73 VAddr string_table_offset{};
74 VAddr symbol_table_offset{};
75 u64 symbol_entry_size{};
76
77 VAddr dynamic_index = dynamic_offset;
78 while (true) {
79 const u64 tag = memory.Read64(dynamic_index);
80 const u64 value = memory.Read64(dynamic_index + 0x8);
81 dynamic_index += 0x10;
82
83 if (tag == ELF_DYNAMIC_TAG_NULL) {
84 break;
85 }
86
87 if (tag == ELF_DYNAMIC_TAG_STRTAB) {
88 string_table_offset = value;
89 } else if (tag == ELF_DYNAMIC_TAG_SYMTAB) {
90 symbol_table_offset = value;
91 } else if (tag == ELF_DYNAMIC_TAG_SYMENT) {
92 symbol_entry_size = value;
93 }
94 }
95
96 if (string_table_offset == 0 || symbol_table_offset == 0 || symbol_entry_size == 0) {
97 return {};
98 }
99 19
100 const auto string_table_address = text_offset + string_table_offset; 20namespace Core {
101 const auto symbol_table_address = text_offset + symbol_table_offset;
102
103 Symbols out;
104
105 VAddr symbol_index = symbol_table_address;
106 while (symbol_index < string_table_address) {
107 ELFSymbol symbol{};
108 memory.ReadBlock(symbol_index, &symbol, sizeof(ELFSymbol));
109
110 VAddr string_offset = string_table_address + symbol.name_index;
111 std::string name;
112 for (u8 c = memory.Read8(string_offset); c != 0; c = memory.Read8(++string_offset)) {
113 name += static_cast<char>(c);
114 }
115
116 symbol_index += symbol_entry_size;
117 out.push_back({symbol, name});
118 }
119
120 return out;
121}
122
123std::optional<std::string> GetSymbolName(const Symbols& symbols, VAddr func_address) {
124 const auto iter =
125 std::find_if(symbols.begin(), symbols.end(), [func_address](const auto& pair) {
126 const auto& symbol = pair.first;
127 const auto end_address = symbol.value + symbol.size;
128 return func_address >= symbol.value && func_address < end_address;
129 });
130
131 if (iter == symbols.end()) {
132 return std::nullopt;
133 }
134
135 return iter->second;
136}
137
138} // Anonymous namespace
139 21
140constexpr u64 SEGMENT_BASE = 0x7100000000ull; 22constexpr u64 SEGMENT_BASE = 0x7100000000ull;
141 23
142std::vector<ARM_Interface::BacktraceEntry> ARM_Interface::GetBacktraceFromContext( 24std::vector<ARM_Interface::BacktraceEntry> ARM_Interface::GetBacktraceFromContext(
143 System& system, const ThreadContext64& ctx) { 25 Core::System& system, const ARM_Interface::ThreadContext32& ctx) {
144 std::vector<BacktraceEntry> out; 26 return ARM_Dynarmic_32::GetBacktraceFromContext(system, ctx);
145 auto& memory = system.Memory();
146
147 auto fp = ctx.cpu_registers[29];
148 auto lr = ctx.cpu_registers[30];
149 while (true) {
150 out.push_back({
151 .module = "",
152 .address = 0,
153 .original_address = lr,
154 .offset = 0,
155 .name = {},
156 });
157
158 if (fp == 0) {
159 break;
160 }
161
162 lr = memory.Read64(fp + 8) - 4;
163 fp = memory.Read64(fp);
164 }
165
166 std::map<VAddr, std::string> modules;
167 auto& loader{system.GetAppLoader()};
168 if (loader.ReadNSOModules(modules) != Loader::ResultStatus::Success) {
169 return {};
170 }
171
172 std::map<std::string, Symbols> symbols;
173 for (const auto& module : modules) {
174 symbols.insert_or_assign(module.second, GetSymbols(module.first, memory));
175 }
176
177 for (auto& entry : out) {
178 VAddr base = 0;
179 for (auto iter = modules.rbegin(); iter != modules.rend(); ++iter) {
180 const auto& module{*iter};
181 if (entry.original_address >= module.first) {
182 entry.module = module.second;
183 base = module.first;
184 break;
185 }
186 }
187
188 entry.offset = entry.original_address - base;
189 entry.address = SEGMENT_BASE + entry.offset;
190
191 if (entry.module.empty())
192 entry.module = "unknown";
193
194 const auto symbol_set = symbols.find(entry.module);
195 if (symbol_set != symbols.end()) {
196 const auto symbol = GetSymbolName(symbol_set->second, entry.offset);
197 if (symbol.has_value()) {
198 // TODO(DarkLordZach): Add demangling of symbol names.
199 entry.name = *symbol;
200 }
201 }
202 }
203
204 return out;
205} 27}
206 28
207std::vector<ARM_Interface::BacktraceEntry> ARM_Interface::GetBacktrace() const { 29std::vector<ARM_Interface::BacktraceEntry> ARM_Interface::GetBacktraceFromContext(
208 std::vector<BacktraceEntry> out; 30 Core::System& system, const ARM_Interface::ThreadContext64& ctx) {
209 auto& memory = system.Memory(); 31 return ARM_Dynarmic_64::GetBacktraceFromContext(system, ctx);
210 32}
211 auto fp = GetReg(29);
212 auto lr = GetReg(30);
213 while (true) {
214 out.push_back({"", 0, lr, 0, ""});
215 if (!fp) {
216 break;
217 }
218 lr = memory.Read64(fp + 8) - 4;
219 fp = memory.Read64(fp);
220 }
221 33
34void ARM_Interface::SymbolicateBacktrace(Core::System& system, std::vector<BacktraceEntry>& out) {
222 std::map<VAddr, std::string> modules; 35 std::map<VAddr, std::string> modules;
223 auto& loader{system.GetAppLoader()}; 36 auto& loader{system.GetAppLoader()};
224 if (loader.ReadNSOModules(modules) != Loader::ResultStatus::Success) { 37 if (loader.ReadNSOModules(modules) != Loader::ResultStatus::Success) {
225 return {}; 38 return;
226 } 39 }
227 40
228 std::map<std::string, Symbols> symbols; 41 std::map<std::string, Symbols::Symbols> symbols;
229 for (const auto& module : modules) { 42 for (const auto& module : modules) {
230 symbols.insert_or_assign(module.second, GetSymbols(module.first, memory)); 43 symbols.insert_or_assign(module.second,
44 Symbols::GetSymbols(module.first, system.Memory(),
45 system.CurrentProcess()->Is64BitProcess()));
231 } 46 }
232 47
233 for (auto& entry : out) { 48 for (auto& entry : out) {
@@ -244,24 +59,23 @@ std::vector<ARM_Interface::BacktraceEntry> ARM_Interface::GetBacktrace() const {
244 entry.offset = entry.original_address - base; 59 entry.offset = entry.original_address - base;
245 entry.address = SEGMENT_BASE + entry.offset; 60 entry.address = SEGMENT_BASE + entry.offset;
246 61
247 if (entry.module.empty()) 62 if (entry.module.empty()) {
248 entry.module = "unknown"; 63 entry.module = "unknown";
64 }
249 65
250 const auto symbol_set = symbols.find(entry.module); 66 const auto symbol_set = symbols.find(entry.module);
251 if (symbol_set != symbols.end()) { 67 if (symbol_set != symbols.end()) {
252 const auto symbol = GetSymbolName(symbol_set->second, entry.offset); 68 const auto symbol = Symbols::GetSymbolName(symbol_set->second, entry.offset);
253 if (symbol.has_value()) { 69 if (symbol.has_value()) {
254 // TODO(DarkLordZach): Add demangling of symbol names. 70 // TODO(DarkLordZach): Add demangling of symbol names.
255 entry.name = *symbol; 71 entry.name = *symbol;
256 } 72 }
257 } 73 }
258 } 74 }
259
260 return out;
261} 75}
262 76
263void ARM_Interface::LogBacktrace() const { 77void ARM_Interface::LogBacktrace() const {
264 const VAddr sp = GetReg(13); 78 const VAddr sp = GetSP();
265 const VAddr pc = GetPC(); 79 const VAddr pc = GetPC();
266 LOG_ERROR(Core_ARM, "Backtrace, sp={:016X}, pc={:016X}", sp, pc); 80 LOG_ERROR(Core_ARM, "Backtrace, sp={:016X}, pc={:016X}", sp, pc);
267 LOG_ERROR(Core_ARM, "{:20}{:20}{:20}{:20}{}", "Module Name", "Address", "Original Address", 81 LOG_ERROR(Core_ARM, "{:20}{:20}{:20}{:20}{}", "Module Name", "Address", "Original Address",
diff --git a/src/core/arm/arm_interface.h b/src/core/arm/arm_interface.h
index c60322442..bcec4b3b8 100644
--- a/src/core/arm/arm_interface.h
+++ b/src/core/arm/arm_interface.h
@@ -101,6 +101,12 @@ public:
101 virtual u64 GetPC() const = 0; 101 virtual u64 GetPC() const = 0;
102 102
103 /** 103 /**
104 * Get the current Stack Pointer
105 * @return Returns current SP
106 */
107 virtual u64 GetSP() const = 0;
108
109 /**
104 * Get an ARM register 110 * Get an ARM register
105 * @param index Register index 111 * @param index Register index
106 * @return Returns the value in the register 112 * @return Returns the value in the register
@@ -171,6 +177,9 @@ public:
171 /// Prepare core for thread reschedule (if needed to correctly handle state) 177 /// Prepare core for thread reschedule (if needed to correctly handle state)
172 virtual void PrepareReschedule() = 0; 178 virtual void PrepareReschedule() = 0;
173 179
180 /// Signal an interrupt and ask the core to halt as soon as possible.
181 virtual void SignalInterrupt() = 0;
182
174 struct BacktraceEntry { 183 struct BacktraceEntry {
175 std::string module; 184 std::string module;
176 u64 address; 185 u64 address;
@@ -180,16 +189,12 @@ public:
180 }; 189 };
181 190
182 static std::vector<BacktraceEntry> GetBacktraceFromContext(System& system, 191 static std::vector<BacktraceEntry> GetBacktraceFromContext(System& system,
192 const ThreadContext32& ctx);
193 static std::vector<BacktraceEntry> GetBacktraceFromContext(System& system,
183 const ThreadContext64& ctx); 194 const ThreadContext64& ctx);
184 195
185 std::vector<BacktraceEntry> GetBacktrace() const; 196 virtual std::vector<BacktraceEntry> GetBacktrace() const = 0;
186 197
187 /// fp (= r29) points to the last frame record.
188 /// Note that this is the frame record for the *previous* frame, not the current one.
189 /// Note we need to subtract 4 from our last read to get the proper address
190 /// Frame records are two words long:
191 /// fp+0 : pointer to previous frame record
192 /// fp+8 : value of lr for frame
193 void LogBacktrace() const; 198 void LogBacktrace() const;
194 199
195protected: 200protected:
@@ -197,6 +202,8 @@ protected:
197 System& system; 202 System& system;
198 CPUInterrupts& interrupt_handlers; 203 CPUInterrupts& interrupt_handlers;
199 bool uses_wall_clock; 204 bool uses_wall_clock;
205
206 static void SymbolicateBacktrace(Core::System& system, std::vector<BacktraceEntry>& out);
200}; 207};
201 208
202} // namespace Core 209} // namespace Core
diff --git a/src/core/arm/dynarmic/arm_dynarmic_32.cpp b/src/core/arm/dynarmic/arm_dynarmic_32.cpp
index c1c843b8f..da5659046 100644
--- a/src/core/arm/dynarmic/arm_dynarmic_32.cpp
+++ b/src/core/arm/dynarmic/arm_dynarmic_32.cpp
@@ -25,6 +25,9 @@ namespace Core {
25 25
26using namespace Common::Literals; 26using namespace Common::Literals;
27 27
28constexpr Dynarmic::HaltReason break_loop = Dynarmic::HaltReason::UserDefined2;
29constexpr Dynarmic::HaltReason svc_call = Dynarmic::HaltReason::UserDefined3;
30
28class DynarmicCallbacks32 : public Dynarmic::A32::UserCallbacks { 31class DynarmicCallbacks32 : public Dynarmic::A32::UserCallbacks {
29public: 32public:
30 explicit DynarmicCallbacks32(ARM_Dynarmic_32& parent_) 33 explicit DynarmicCallbacks32(ARM_Dynarmic_32& parent_)
@@ -70,11 +73,13 @@ public:
70 } 73 }
71 74
72 void InterpreterFallback(u32 pc, std::size_t num_instructions) override { 75 void InterpreterFallback(u32 pc, std::size_t num_instructions) override {
76 parent.LogBacktrace();
73 UNIMPLEMENTED_MSG("This should never happen, pc = {:08X}, code = {:08X}", pc, 77 UNIMPLEMENTED_MSG("This should never happen, pc = {:08X}, code = {:08X}", pc,
74 MemoryReadCode(pc)); 78 MemoryReadCode(pc));
75 } 79 }
76 80
77 void ExceptionRaised(u32 pc, Dynarmic::A32::Exception exception) override { 81 void ExceptionRaised(u32 pc, Dynarmic::A32::Exception exception) override {
82 parent.LogBacktrace();
78 LOG_CRITICAL(Core_ARM, 83 LOG_CRITICAL(Core_ARM,
79 "ExceptionRaised(exception = {}, pc = {:08X}, code = {:08X}, thumb = {})", 84 "ExceptionRaised(exception = {}, pc = {:08X}, code = {:08X}, thumb = {})",
80 exception, pc, MemoryReadCode(pc), parent.IsInThumbMode()); 85 exception, pc, MemoryReadCode(pc), parent.IsInThumbMode());
@@ -82,15 +87,13 @@ public:
82 } 87 }
83 88
84 void CallSVC(u32 swi) override { 89 void CallSVC(u32 swi) override {
85 parent.svc_called = true;
86 parent.svc_swi = swi; 90 parent.svc_swi = swi;
87 parent.jit->HaltExecution(); 91 parent.jit.load()->HaltExecution(svc_call);
88 } 92 }
89 93
90 void AddTicks(u64 ticks) override { 94 void AddTicks(u64 ticks) override {
91 if (parent.uses_wall_clock) { 95 ASSERT_MSG(!parent.uses_wall_clock, "This should never happen - dynarmic ticking disabled");
92 return; 96
93 }
94 // Divide the number of ticks by the amount of CPU cores. TODO(Subv): This yields only a 97 // Divide the number of ticks by the amount of CPU cores. TODO(Subv): This yields only a
95 // rough approximation of the amount of executed ticks in the system, it may be thrown off 98 // rough approximation of the amount of executed ticks in the system, it may be thrown off
96 // if not all cores are doing a similar amount of work. Instead of doing this, we should 99 // if not all cores are doing a similar amount of work. Instead of doing this, we should
@@ -106,12 +109,8 @@ public:
106 } 109 }
107 110
108 u64 GetTicksRemaining() override { 111 u64 GetTicksRemaining() override {
109 if (parent.uses_wall_clock) { 112 ASSERT_MSG(!parent.uses_wall_clock, "This should never happen - dynarmic ticking disabled");
110 if (!parent.interrupt_handlers[parent.core_index].IsInterrupted()) { 113
111 return minimum_run_cycles;
112 }
113 return 0U;
114 }
115 return std::max<s64>(parent.system.CoreTiming().GetDowncount(), 0); 114 return std::max<s64>(parent.system.CoreTiming().GetDowncount(), 0);
116 } 115 }
117 116
@@ -146,10 +145,18 @@ std::shared_ptr<Dynarmic::A32::Jit> ARM_Dynarmic_32::MakeJit(Common::PageTable*
146 145
147 // Timing 146 // Timing
148 config.wall_clock_cntpct = uses_wall_clock; 147 config.wall_clock_cntpct = uses_wall_clock;
148 config.enable_cycle_counting = !uses_wall_clock;
149 149
150 // Code cache size 150 // Code cache size
151 config.code_cache_size = 128_MiB; 151 config.code_cache_size = 512_MiB;
152 config.far_code_offset = 100_MiB; 152 config.far_code_offset = 400_MiB;
153
154 // null_jit
155 if (!page_table) {
156 // Don't waste too much memory on null_jit
157 config.code_cache_size = 8_MiB;
158 config.far_code_offset = 4_MiB;
159 }
153 160
154 // Safe optimizations 161 // Safe optimizations
155 if (Settings::values.cpu_debug_mode) { 162 if (Settings::values.cpu_debug_mode) {
@@ -186,35 +193,41 @@ std::shared_ptr<Dynarmic::A32::Jit> ARM_Dynarmic_32::MakeJit(Common::PageTable*
186 if (!Settings::values.cpuopt_recompile_exclusives) { 193 if (!Settings::values.cpuopt_recompile_exclusives) {
187 config.recompile_on_exclusive_fastmem_failure = false; 194 config.recompile_on_exclusive_fastmem_failure = false;
188 } 195 }
189 } 196 } else {
197 // Unsafe optimizations
198 if (Settings::values.cpu_accuracy.GetValue() == Settings::CPUAccuracy::Unsafe) {
199 config.unsafe_optimizations = true;
200 if (Settings::values.cpuopt_unsafe_unfuse_fma) {
201 config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_UnfuseFMA;
202 }
203 if (Settings::values.cpuopt_unsafe_reduce_fp_error) {
204 config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_ReducedErrorFP;
205 }
206 if (Settings::values.cpuopt_unsafe_ignore_standard_fpcr) {
207 config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_IgnoreStandardFPCRValue;
208 }
209 if (Settings::values.cpuopt_unsafe_inaccurate_nan) {
210 config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_InaccurateNaN;
211 }
212 if (Settings::values.cpuopt_unsafe_ignore_global_monitor) {
213 config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_IgnoreGlobalMonitor;
214 }
215 }
190 216
191 // Unsafe optimizations 217 // Curated optimizations
192 if (Settings::values.cpu_accuracy.GetValue() == Settings::CPUAccuracy::Unsafe) { 218 if (Settings::values.cpu_accuracy.GetValue() == Settings::CPUAccuracy::Auto) {
193 config.unsafe_optimizations = true; 219 config.unsafe_optimizations = true;
194 if (Settings::values.cpuopt_unsafe_unfuse_fma) {
195 config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_UnfuseFMA; 220 config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_UnfuseFMA;
196 }
197 if (Settings::values.cpuopt_unsafe_reduce_fp_error) {
198 config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_ReducedErrorFP;
199 }
200 if (Settings::values.cpuopt_unsafe_ignore_standard_fpcr) {
201 config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_IgnoreStandardFPCRValue; 221 config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_IgnoreStandardFPCRValue;
202 }
203 if (Settings::values.cpuopt_unsafe_inaccurate_nan) {
204 config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_InaccurateNaN; 222 config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_InaccurateNaN;
205 }
206 if (Settings::values.cpuopt_unsafe_ignore_global_monitor) {
207 config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_IgnoreGlobalMonitor; 223 config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_IgnoreGlobalMonitor;
208 } 224 }
209 }
210 225
211 // Curated optimizations 226 // Paranoia mode for debugging optimizations
212 if (Settings::values.cpu_accuracy.GetValue() == Settings::CPUAccuracy::Auto) { 227 if (Settings::values.cpu_accuracy.GetValue() == Settings::CPUAccuracy::Paranoid) {
213 config.unsafe_optimizations = true; 228 config.unsafe_optimizations = false;
214 config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_UnfuseFMA; 229 config.optimizations = Dynarmic::no_optimizations;
215 config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_IgnoreStandardFPCRValue; 230 }
216 config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_InaccurateNaN;
217 config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_IgnoreGlobalMonitor;
218 } 231 }
219 232
220 return std::make_unique<Dynarmic::A32::Jit>(config); 233 return std::make_unique<Dynarmic::A32::Jit>(config);
@@ -222,20 +235,18 @@ std::shared_ptr<Dynarmic::A32::Jit> ARM_Dynarmic_32::MakeJit(Common::PageTable*
222 235
223void ARM_Dynarmic_32::Run() { 236void ARM_Dynarmic_32::Run() {
224 while (true) { 237 while (true) {
225 jit->Run(); 238 const auto hr = jit.load()->Run();
226 if (!svc_called) { 239 if (Has(hr, svc_call)) {
227 break; 240 Kernel::Svc::Call(system, svc_swi);
228 } 241 }
229 svc_called = false; 242 if (Has(hr, break_loop) || !uses_wall_clock) {
230 Kernel::Svc::Call(system, svc_swi);
231 if (shutdown) {
232 break; 243 break;
233 } 244 }
234 } 245 }
235} 246}
236 247
237void ARM_Dynarmic_32::Step() { 248void ARM_Dynarmic_32::Step() {
238 jit->Step(); 249 jit.load()->Step();
239} 250}
240 251
241ARM_Dynarmic_32::ARM_Dynarmic_32(System& system_, CPUInterrupts& interrupt_handlers_, 252ARM_Dynarmic_32::ARM_Dynarmic_32(System& system_, CPUInterrupts& interrupt_handlers_,
@@ -245,24 +256,28 @@ ARM_Dynarmic_32::ARM_Dynarmic_32(System& system_, CPUInterrupts& interrupt_handl
245 cb(std::make_unique<DynarmicCallbacks32>(*this)), 256 cb(std::make_unique<DynarmicCallbacks32>(*this)),
246 cp15(std::make_shared<DynarmicCP15>(*this)), core_index{core_index_}, 257 cp15(std::make_shared<DynarmicCP15>(*this)), core_index{core_index_},
247 exclusive_monitor{dynamic_cast<DynarmicExclusiveMonitor&>(exclusive_monitor_)}, 258 exclusive_monitor{dynamic_cast<DynarmicExclusiveMonitor&>(exclusive_monitor_)},
248 jit(MakeJit(nullptr)) {} 259 null_jit{MakeJit(nullptr)}, jit{null_jit.get()} {}
249 260
250ARM_Dynarmic_32::~ARM_Dynarmic_32() = default; 261ARM_Dynarmic_32::~ARM_Dynarmic_32() = default;
251 262
252void ARM_Dynarmic_32::SetPC(u64 pc) { 263void ARM_Dynarmic_32::SetPC(u64 pc) {
253 jit->Regs()[15] = static_cast<u32>(pc); 264 jit.load()->Regs()[15] = static_cast<u32>(pc);
254} 265}
255 266
256u64 ARM_Dynarmic_32::GetPC() const { 267u64 ARM_Dynarmic_32::GetPC() const {
257 return jit->Regs()[15]; 268 return jit.load()->Regs()[15];
269}
270
271u64 ARM_Dynarmic_32::GetSP() const {
272 return jit.load()->Regs()[13];
258} 273}
259 274
260u64 ARM_Dynarmic_32::GetReg(int index) const { 275u64 ARM_Dynarmic_32::GetReg(int index) const {
261 return jit->Regs()[index]; 276 return jit.load()->Regs()[index];
262} 277}
263 278
264void ARM_Dynarmic_32::SetReg(int index, u64 value) { 279void ARM_Dynarmic_32::SetReg(int index, u64 value) {
265 jit->Regs()[index] = static_cast<u32>(value); 280 jit.load()->Regs()[index] = static_cast<u32>(value);
266} 281}
267 282
268u128 ARM_Dynarmic_32::GetVectorReg(int index) const { 283u128 ARM_Dynarmic_32::GetVectorReg(int index) const {
@@ -272,11 +287,11 @@ u128 ARM_Dynarmic_32::GetVectorReg(int index) const {
272void ARM_Dynarmic_32::SetVectorReg(int index, u128 value) {} 287void ARM_Dynarmic_32::SetVectorReg(int index, u128 value) {}
273 288
274u32 ARM_Dynarmic_32::GetPSTATE() const { 289u32 ARM_Dynarmic_32::GetPSTATE() const {
275 return jit->Cpsr(); 290 return jit.load()->Cpsr();
276} 291}
277 292
278void ARM_Dynarmic_32::SetPSTATE(u32 cpsr) { 293void ARM_Dynarmic_32::SetPSTATE(u32 cpsr) {
279 jit->SetCpsr(cpsr); 294 jit.load()->SetCpsr(cpsr);
280} 295}
281 296
282u64 ARM_Dynarmic_32::GetTlsAddress() const { 297u64 ARM_Dynarmic_32::GetTlsAddress() const {
@@ -297,7 +312,7 @@ void ARM_Dynarmic_32::SetTPIDR_EL0(u64 value) {
297 312
298void ARM_Dynarmic_32::SaveContext(ThreadContext32& ctx) { 313void ARM_Dynarmic_32::SaveContext(ThreadContext32& ctx) {
299 Dynarmic::A32::Context context; 314 Dynarmic::A32::Context context;
300 jit->SaveContext(context); 315 jit.load()->SaveContext(context);
301 ctx.cpu_registers = context.Regs(); 316 ctx.cpu_registers = context.Regs();
302 ctx.extension_registers = context.ExtRegs(); 317 ctx.extension_registers = context.ExtRegs();
303 ctx.cpsr = context.Cpsr(); 318 ctx.cpsr = context.Cpsr();
@@ -310,24 +325,27 @@ void ARM_Dynarmic_32::LoadContext(const ThreadContext32& ctx) {
310 context.ExtRegs() = ctx.extension_registers; 325 context.ExtRegs() = ctx.extension_registers;
311 context.SetCpsr(ctx.cpsr); 326 context.SetCpsr(ctx.cpsr);
312 context.SetFpscr(ctx.fpscr); 327 context.SetFpscr(ctx.fpscr);
313 jit->LoadContext(context); 328 jit.load()->LoadContext(context);
314} 329}
315 330
316void ARM_Dynarmic_32::PrepareReschedule() { 331void ARM_Dynarmic_32::PrepareReschedule() {
317 jit->HaltExecution(); 332 jit.load()->HaltExecution(break_loop);
318 shutdown = true; 333}
334
335void ARM_Dynarmic_32::SignalInterrupt() {
336 jit.load()->HaltExecution(break_loop);
319} 337}
320 338
321void ARM_Dynarmic_32::ClearInstructionCache() { 339void ARM_Dynarmic_32::ClearInstructionCache() {
322 jit->ClearCache(); 340 jit.load()->ClearCache();
323} 341}
324 342
325void ARM_Dynarmic_32::InvalidateCacheRange(VAddr addr, std::size_t size) { 343void ARM_Dynarmic_32::InvalidateCacheRange(VAddr addr, std::size_t size) {
326 jit->InvalidateCacheRange(static_cast<u32>(addr), size); 344 jit.load()->InvalidateCacheRange(static_cast<u32>(addr), size);
327} 345}
328 346
329void ARM_Dynarmic_32::ClearExclusiveState() { 347void ARM_Dynarmic_32::ClearExclusiveState() {
330 jit->ClearExclusiveState(); 348 jit.load()->ClearExclusiveState();
331} 349}
332 350
333void ARM_Dynarmic_32::PageTableChanged(Common::PageTable& page_table, 351void ARM_Dynarmic_32::PageTableChanged(Common::PageTable& page_table,
@@ -338,13 +356,29 @@ void ARM_Dynarmic_32::PageTableChanged(Common::PageTable& page_table,
338 auto key = std::make_pair(&page_table, new_address_space_size_in_bits); 356 auto key = std::make_pair(&page_table, new_address_space_size_in_bits);
339 auto iter = jit_cache.find(key); 357 auto iter = jit_cache.find(key);
340 if (iter != jit_cache.end()) { 358 if (iter != jit_cache.end()) {
341 jit = iter->second; 359 jit.store(iter->second.get());
342 LoadContext(ctx); 360 LoadContext(ctx);
343 return; 361 return;
344 } 362 }
345 jit = MakeJit(&page_table); 363 std::shared_ptr new_jit = MakeJit(&page_table);
364 jit.store(new_jit.get());
346 LoadContext(ctx); 365 LoadContext(ctx);
347 jit_cache.emplace(key, jit); 366 jit_cache.emplace(key, std::move(new_jit));
367}
368
369std::vector<ARM_Interface::BacktraceEntry> ARM_Dynarmic_32::GetBacktrace(Core::System& system,
370 u64 sp, u64 lr) {
371 // No way to get accurate stack traces in A32 yet
372 return {};
373}
374
375std::vector<ARM_Interface::BacktraceEntry> ARM_Dynarmic_32::GetBacktraceFromContext(
376 System& system, const ThreadContext32& ctx) {
377 return GetBacktrace(system, ctx.cpu_registers[13], ctx.cpu_registers[14]);
378}
379
380std::vector<ARM_Interface::BacktraceEntry> ARM_Dynarmic_32::GetBacktrace() const {
381 return GetBacktrace(system, GetReg(13), GetReg(14));
348} 382}
349 383
350} // namespace Core 384} // namespace Core
diff --git a/src/core/arm/dynarmic/arm_dynarmic_32.h b/src/core/arm/dynarmic/arm_dynarmic_32.h
index 5d47b600d..1b628f94d 100644
--- a/src/core/arm/dynarmic/arm_dynarmic_32.h
+++ b/src/core/arm/dynarmic/arm_dynarmic_32.h
@@ -4,6 +4,7 @@
4 4
5#pragma once 5#pragma once
6 6
7#include <atomic>
7#include <memory> 8#include <memory>
8#include <unordered_map> 9#include <unordered_map>
9 10
@@ -34,6 +35,7 @@ public:
34 35
35 void SetPC(u64 pc) override; 36 void SetPC(u64 pc) override;
36 u64 GetPC() const override; 37 u64 GetPC() const override;
38 u64 GetSP() const override;
37 u64 GetReg(int index) const override; 39 u64 GetReg(int index) const override;
38 void SetReg(int index, u64 value) override; 40 void SetReg(int index, u64 value) override;
39 u128 GetVectorReg(int index) const override; 41 u128 GetVectorReg(int index) const override;
@@ -57,6 +59,7 @@ public:
57 void LoadContext(const ThreadContext64& ctx) override {} 59 void LoadContext(const ThreadContext64& ctx) override {}
58 60
59 void PrepareReschedule() override; 61 void PrepareReschedule() override;
62 void SignalInterrupt() override;
60 void ClearExclusiveState() override; 63 void ClearExclusiveState() override;
61 64
62 void ClearInstructionCache() override; 65 void ClearInstructionCache() override;
@@ -64,9 +67,16 @@ public:
64 void PageTableChanged(Common::PageTable& new_page_table, 67 void PageTableChanged(Common::PageTable& new_page_table,
65 std::size_t new_address_space_size_in_bits) override; 68 std::size_t new_address_space_size_in_bits) override;
66 69
70 static std::vector<BacktraceEntry> GetBacktraceFromContext(System& system,
71 const ThreadContext32& ctx);
72
73 std::vector<BacktraceEntry> GetBacktrace() const override;
74
67private: 75private:
68 std::shared_ptr<Dynarmic::A32::Jit> MakeJit(Common::PageTable* page_table) const; 76 std::shared_ptr<Dynarmic::A32::Jit> MakeJit(Common::PageTable* page_table) const;
69 77
78 static std::vector<BacktraceEntry> GetBacktrace(Core::System& system, u64 sp, u64 lr);
79
70 using JitCacheKey = std::pair<Common::PageTable*, std::size_t>; 80 using JitCacheKey = std::pair<Common::PageTable*, std::size_t>;
71 using JitCacheType = 81 using JitCacheType =
72 std::unordered_map<JitCacheKey, std::shared_ptr<Dynarmic::A32::Jit>, Common::PairHash>; 82 std::unordered_map<JitCacheKey, std::shared_ptr<Dynarmic::A32::Jit>, Common::PairHash>;
@@ -79,13 +89,14 @@ private:
79 std::shared_ptr<DynarmicCP15> cp15; 89 std::shared_ptr<DynarmicCP15> cp15;
80 std::size_t core_index; 90 std::size_t core_index;
81 DynarmicExclusiveMonitor& exclusive_monitor; 91 DynarmicExclusiveMonitor& exclusive_monitor;
82 std::shared_ptr<Dynarmic::A32::Jit> jit; 92
93 std::shared_ptr<Dynarmic::A32::Jit> null_jit;
94
95 // A raw pointer here is fine; we never delete Jit instances.
96 std::atomic<Dynarmic::A32::Jit*> jit;
83 97
84 // SVC callback 98 // SVC callback
85 u32 svc_swi{}; 99 u32 svc_swi{};
86 bool svc_called{};
87
88 bool shutdown{};
89}; 100};
90 101
91} // namespace Core 102} // namespace Core
diff --git a/src/core/arm/dynarmic/arm_dynarmic_64.cpp b/src/core/arm/dynarmic/arm_dynarmic_64.cpp
index aa74fce4d..871d9d10e 100644
--- a/src/core/arm/dynarmic/arm_dynarmic_64.cpp
+++ b/src/core/arm/dynarmic/arm_dynarmic_64.cpp
@@ -26,6 +26,9 @@ namespace Core {
26using Vector = Dynarmic::A64::Vector; 26using Vector = Dynarmic::A64::Vector;
27using namespace Common::Literals; 27using namespace Common::Literals;
28 28
29constexpr Dynarmic::HaltReason break_loop = Dynarmic::HaltReason::UserDefined2;
30constexpr Dynarmic::HaltReason svc_call = Dynarmic::HaltReason::UserDefined3;
31
29class DynarmicCallbacks64 : public Dynarmic::A64::UserCallbacks { 32class DynarmicCallbacks64 : public Dynarmic::A64::UserCallbacks {
30public: 33public:
31 explicit DynarmicCallbacks64(ARM_Dynarmic_64& parent_) 34 explicit DynarmicCallbacks64(ARM_Dynarmic_64& parent_)
@@ -81,6 +84,7 @@ public:
81 } 84 }
82 85
83 void InterpreterFallback(u64 pc, std::size_t num_instructions) override { 86 void InterpreterFallback(u64 pc, std::size_t num_instructions) override {
87 parent.LogBacktrace();
84 LOG_ERROR(Core_ARM, 88 LOG_ERROR(Core_ARM,
85 "Unimplemented instruction @ 0x{:X} for {} instructions (instr = {:08X})", pc, 89 "Unimplemented instruction @ 0x{:X} for {} instructions (instr = {:08X})", pc,
86 num_instructions, MemoryReadCode(pc)); 90 num_instructions, MemoryReadCode(pc));
@@ -93,17 +97,19 @@ public:
93 static constexpr u64 ICACHE_LINE_SIZE = 64; 97 static constexpr u64 ICACHE_LINE_SIZE = 64;
94 98
95 const u64 cache_line_start = value & ~(ICACHE_LINE_SIZE - 1); 99 const u64 cache_line_start = value & ~(ICACHE_LINE_SIZE - 1);
96 parent.InvalidateCacheRange(cache_line_start, ICACHE_LINE_SIZE); 100 parent.system.InvalidateCpuInstructionCacheRange(cache_line_start, ICACHE_LINE_SIZE);
97 break; 101 break;
98 } 102 }
99 case Dynarmic::A64::InstructionCacheOperation::InvalidateAllToPoU: 103 case Dynarmic::A64::InstructionCacheOperation::InvalidateAllToPoU:
100 parent.ClearInstructionCache(); 104 parent.system.InvalidateCpuInstructionCaches();
101 break; 105 break;
102 case Dynarmic::A64::InstructionCacheOperation::InvalidateAllToPoUInnerSharable: 106 case Dynarmic::A64::InstructionCacheOperation::InvalidateAllToPoUInnerSharable:
103 default: 107 default:
104 LOG_DEBUG(Core_ARM, "Unprocesseed instruction cache operation: {}", op); 108 LOG_DEBUG(Core_ARM, "Unprocesseed instruction cache operation: {}", op);
105 break; 109 break;
106 } 110 }
111
112 parent.jit.load()->HaltExecution(Dynarmic::HaltReason::CacheInvalidation);
107 } 113 }
108 114
109 void ExceptionRaised(u64 pc, Dynarmic::A64::Exception exception) override { 115 void ExceptionRaised(u64 pc, Dynarmic::A64::Exception exception) override {
@@ -116,21 +122,19 @@ public:
116 return; 122 return;
117 case Dynarmic::A64::Exception::Breakpoint: 123 case Dynarmic::A64::Exception::Breakpoint:
118 default: 124 default:
125 parent.LogBacktrace();
119 ASSERT_MSG(false, "ExceptionRaised(exception = {}, pc = {:08X}, code = {:08X})", 126 ASSERT_MSG(false, "ExceptionRaised(exception = {}, pc = {:08X}, code = {:08X})",
120 static_cast<std::size_t>(exception), pc, MemoryReadCode(pc)); 127 static_cast<std::size_t>(exception), pc, MemoryReadCode(pc));
121 } 128 }
122 } 129 }
123 130
124 void CallSVC(u32 swi) override { 131 void CallSVC(u32 swi) override {
125 parent.svc_called = true;
126 parent.svc_swi = swi; 132 parent.svc_swi = swi;
127 parent.jit->HaltExecution(); 133 parent.jit.load()->HaltExecution(svc_call);
128 } 134 }
129 135
130 void AddTicks(u64 ticks) override { 136 void AddTicks(u64 ticks) override {
131 if (parent.uses_wall_clock) { 137 ASSERT_MSG(!parent.uses_wall_clock, "This should never happen - dynarmic ticking disabled");
132 return;
133 }
134 138
135 // Divide the number of ticks by the amount of CPU cores. TODO(Subv): This yields only a 139 // Divide the number of ticks by the amount of CPU cores. TODO(Subv): This yields only a
136 // rough approximation of the amount of executed ticks in the system, it may be thrown off 140 // rough approximation of the amount of executed ticks in the system, it may be thrown off
@@ -145,12 +149,8 @@ public:
145 } 149 }
146 150
147 u64 GetTicksRemaining() override { 151 u64 GetTicksRemaining() override {
148 if (parent.uses_wall_clock) { 152 ASSERT_MSG(!parent.uses_wall_clock, "This should never happen - dynarmic ticking disabled");
149 if (!parent.interrupt_handlers[parent.core_index].IsInterrupted()) { 153
150 return minimum_run_cycles;
151 }
152 return 0U;
153 }
154 return std::max<s64>(parent.system.CoreTiming().GetDowncount(), 0); 154 return std::max<s64>(parent.system.CoreTiming().GetDowncount(), 0);
155 } 155 }
156 156
@@ -206,10 +206,18 @@ std::shared_ptr<Dynarmic::A64::Jit> ARM_Dynarmic_64::MakeJit(Common::PageTable*
206 206
207 // Timing 207 // Timing
208 config.wall_clock_cntpct = uses_wall_clock; 208 config.wall_clock_cntpct = uses_wall_clock;
209 config.enable_cycle_counting = !uses_wall_clock;
209 210
210 // Code cache size 211 // Code cache size
211 config.code_cache_size = 128_MiB; 212 config.code_cache_size = 512_MiB;
212 config.far_code_offset = 100_MiB; 213 config.far_code_offset = 400_MiB;
214
215 // null_jit
216 if (!page_table) {
217 // Don't waste too much memory on null_jit
218 config.code_cache_size = 8_MiB;
219 config.far_code_offset = 4_MiB;
220 }
213 221
214 // Safe optimizations 222 // Safe optimizations
215 if (Settings::values.cpu_debug_mode) { 223 if (Settings::values.cpu_debug_mode) {
@@ -246,35 +254,41 @@ std::shared_ptr<Dynarmic::A64::Jit> ARM_Dynarmic_64::MakeJit(Common::PageTable*
246 if (!Settings::values.cpuopt_recompile_exclusives) { 254 if (!Settings::values.cpuopt_recompile_exclusives) {
247 config.recompile_on_exclusive_fastmem_failure = false; 255 config.recompile_on_exclusive_fastmem_failure = false;
248 } 256 }
249 } 257 } else {
258 // Unsafe optimizations
259 if (Settings::values.cpu_accuracy.GetValue() == Settings::CPUAccuracy::Unsafe) {
260 config.unsafe_optimizations = true;
261 if (Settings::values.cpuopt_unsafe_unfuse_fma) {
262 config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_UnfuseFMA;
263 }
264 if (Settings::values.cpuopt_unsafe_reduce_fp_error) {
265 config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_ReducedErrorFP;
266 }
267 if (Settings::values.cpuopt_unsafe_inaccurate_nan) {
268 config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_InaccurateNaN;
269 }
270 if (Settings::values.cpuopt_unsafe_fastmem_check) {
271 config.fastmem_address_space_bits = 64;
272 }
273 if (Settings::values.cpuopt_unsafe_ignore_global_monitor) {
274 config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_IgnoreGlobalMonitor;
275 }
276 }
250 277
251 // Unsafe optimizations 278 // Curated optimizations
252 if (Settings::values.cpu_accuracy.GetValue() == Settings::CPUAccuracy::Unsafe) { 279 if (Settings::values.cpu_accuracy.GetValue() == Settings::CPUAccuracy::Auto) {
253 config.unsafe_optimizations = true; 280 config.unsafe_optimizations = true;
254 if (Settings::values.cpuopt_unsafe_unfuse_fma) {
255 config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_UnfuseFMA; 281 config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_UnfuseFMA;
256 }
257 if (Settings::values.cpuopt_unsafe_reduce_fp_error) {
258 config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_ReducedErrorFP;
259 }
260 if (Settings::values.cpuopt_unsafe_inaccurate_nan) {
261 config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_InaccurateNaN; 282 config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_InaccurateNaN;
262 }
263 if (Settings::values.cpuopt_unsafe_fastmem_check) {
264 config.fastmem_address_space_bits = 64; 283 config.fastmem_address_space_bits = 64;
265 }
266 if (Settings::values.cpuopt_unsafe_ignore_global_monitor) {
267 config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_IgnoreGlobalMonitor; 284 config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_IgnoreGlobalMonitor;
268 } 285 }
269 }
270 286
271 // Curated optimizations 287 // Paranoia mode for debugging optimizations
272 if (Settings::values.cpu_accuracy.GetValue() == Settings::CPUAccuracy::Auto) { 288 if (Settings::values.cpu_accuracy.GetValue() == Settings::CPUAccuracy::Paranoid) {
273 config.unsafe_optimizations = true; 289 config.unsafe_optimizations = false;
274 config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_UnfuseFMA; 290 config.optimizations = Dynarmic::no_optimizations;
275 config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_InaccurateNaN; 291 }
276 config.fastmem_address_space_bits = 64;
277 config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_IgnoreGlobalMonitor;
278 } 292 }
279 293
280 return std::make_shared<Dynarmic::A64::Jit>(config); 294 return std::make_shared<Dynarmic::A64::Jit>(config);
@@ -282,20 +296,18 @@ std::shared_ptr<Dynarmic::A64::Jit> ARM_Dynarmic_64::MakeJit(Common::PageTable*
282 296
283void ARM_Dynarmic_64::Run() { 297void ARM_Dynarmic_64::Run() {
284 while (true) { 298 while (true) {
285 jit->Run(); 299 const auto hr = jit.load()->Run();
286 if (!svc_called) { 300 if (Has(hr, svc_call)) {
287 break; 301 Kernel::Svc::Call(system, svc_swi);
288 } 302 }
289 svc_called = false; 303 if (Has(hr, break_loop) || !uses_wall_clock) {
290 Kernel::Svc::Call(system, svc_swi);
291 if (shutdown) {
292 break; 304 break;
293 } 305 }
294 } 306 }
295} 307}
296 308
297void ARM_Dynarmic_64::Step() { 309void ARM_Dynarmic_64::Step() {
298 jit->Step(); 310 jit.load()->Step();
299} 311}
300 312
301ARM_Dynarmic_64::ARM_Dynarmic_64(System& system_, CPUInterrupts& interrupt_handlers_, 313ARM_Dynarmic_64::ARM_Dynarmic_64(System& system_, CPUInterrupts& interrupt_handlers_,
@@ -304,40 +316,44 @@ ARM_Dynarmic_64::ARM_Dynarmic_64(System& system_, CPUInterrupts& interrupt_handl
304 : ARM_Interface{system_, interrupt_handlers_, uses_wall_clock_}, 316 : ARM_Interface{system_, interrupt_handlers_, uses_wall_clock_},
305 cb(std::make_unique<DynarmicCallbacks64>(*this)), core_index{core_index_}, 317 cb(std::make_unique<DynarmicCallbacks64>(*this)), core_index{core_index_},
306 exclusive_monitor{dynamic_cast<DynarmicExclusiveMonitor&>(exclusive_monitor_)}, 318 exclusive_monitor{dynamic_cast<DynarmicExclusiveMonitor&>(exclusive_monitor_)},
307 jit(MakeJit(nullptr, 48)) {} 319 null_jit{MakeJit(nullptr, 48)}, jit{null_jit.get()} {}
308 320
309ARM_Dynarmic_64::~ARM_Dynarmic_64() = default; 321ARM_Dynarmic_64::~ARM_Dynarmic_64() = default;
310 322
311void ARM_Dynarmic_64::SetPC(u64 pc) { 323void ARM_Dynarmic_64::SetPC(u64 pc) {
312 jit->SetPC(pc); 324 jit.load()->SetPC(pc);
313} 325}
314 326
315u64 ARM_Dynarmic_64::GetPC() const { 327u64 ARM_Dynarmic_64::GetPC() const {
316 return jit->GetPC(); 328 return jit.load()->GetPC();
329}
330
331u64 ARM_Dynarmic_64::GetSP() const {
332 return jit.load()->GetSP();
317} 333}
318 334
319u64 ARM_Dynarmic_64::GetReg(int index) const { 335u64 ARM_Dynarmic_64::GetReg(int index) const {
320 return jit->GetRegister(index); 336 return jit.load()->GetRegister(index);
321} 337}
322 338
323void ARM_Dynarmic_64::SetReg(int index, u64 value) { 339void ARM_Dynarmic_64::SetReg(int index, u64 value) {
324 jit->SetRegister(index, value); 340 jit.load()->SetRegister(index, value);
325} 341}
326 342
327u128 ARM_Dynarmic_64::GetVectorReg(int index) const { 343u128 ARM_Dynarmic_64::GetVectorReg(int index) const {
328 return jit->GetVector(index); 344 return jit.load()->GetVector(index);
329} 345}
330 346
331void ARM_Dynarmic_64::SetVectorReg(int index, u128 value) { 347void ARM_Dynarmic_64::SetVectorReg(int index, u128 value) {
332 jit->SetVector(index, value); 348 jit.load()->SetVector(index, value);
333} 349}
334 350
335u32 ARM_Dynarmic_64::GetPSTATE() const { 351u32 ARM_Dynarmic_64::GetPSTATE() const {
336 return jit->GetPstate(); 352 return jit.load()->GetPstate();
337} 353}
338 354
339void ARM_Dynarmic_64::SetPSTATE(u32 pstate) { 355void ARM_Dynarmic_64::SetPSTATE(u32 pstate) {
340 jit->SetPstate(pstate); 356 jit.load()->SetPstate(pstate);
341} 357}
342 358
343u64 ARM_Dynarmic_64::GetTlsAddress() const { 359u64 ARM_Dynarmic_64::GetTlsAddress() const {
@@ -357,42 +373,47 @@ void ARM_Dynarmic_64::SetTPIDR_EL0(u64 value) {
357} 373}
358 374
359void ARM_Dynarmic_64::SaveContext(ThreadContext64& ctx) { 375void ARM_Dynarmic_64::SaveContext(ThreadContext64& ctx) {
360 ctx.cpu_registers = jit->GetRegisters(); 376 Dynarmic::A64::Jit* j = jit.load();
361 ctx.sp = jit->GetSP(); 377 ctx.cpu_registers = j->GetRegisters();
362 ctx.pc = jit->GetPC(); 378 ctx.sp = j->GetSP();
363 ctx.pstate = jit->GetPstate(); 379 ctx.pc = j->GetPC();
364 ctx.vector_registers = jit->GetVectors(); 380 ctx.pstate = j->GetPstate();
365 ctx.fpcr = jit->GetFpcr(); 381 ctx.vector_registers = j->GetVectors();
366 ctx.fpsr = jit->GetFpsr(); 382 ctx.fpcr = j->GetFpcr();
383 ctx.fpsr = j->GetFpsr();
367 ctx.tpidr = cb->tpidr_el0; 384 ctx.tpidr = cb->tpidr_el0;
368} 385}
369 386
370void ARM_Dynarmic_64::LoadContext(const ThreadContext64& ctx) { 387void ARM_Dynarmic_64::LoadContext(const ThreadContext64& ctx) {
371 jit->SetRegisters(ctx.cpu_registers); 388 Dynarmic::A64::Jit* j = jit.load();
372 jit->SetSP(ctx.sp); 389 j->SetRegisters(ctx.cpu_registers);
373 jit->SetPC(ctx.pc); 390 j->SetSP(ctx.sp);
374 jit->SetPstate(ctx.pstate); 391 j->SetPC(ctx.pc);
375 jit->SetVectors(ctx.vector_registers); 392 j->SetPstate(ctx.pstate);
376 jit->SetFpcr(ctx.fpcr); 393 j->SetVectors(ctx.vector_registers);
377 jit->SetFpsr(ctx.fpsr); 394 j->SetFpcr(ctx.fpcr);
395 j->SetFpsr(ctx.fpsr);
378 SetTPIDR_EL0(ctx.tpidr); 396 SetTPIDR_EL0(ctx.tpidr);
379} 397}
380 398
381void ARM_Dynarmic_64::PrepareReschedule() { 399void ARM_Dynarmic_64::PrepareReschedule() {
382 jit->HaltExecution(); 400 jit.load()->HaltExecution(break_loop);
383 shutdown = true; 401}
402
403void ARM_Dynarmic_64::SignalInterrupt() {
404 jit.load()->HaltExecution(break_loop);
384} 405}
385 406
386void ARM_Dynarmic_64::ClearInstructionCache() { 407void ARM_Dynarmic_64::ClearInstructionCache() {
387 jit->ClearCache(); 408 jit.load()->ClearCache();
388} 409}
389 410
390void ARM_Dynarmic_64::InvalidateCacheRange(VAddr addr, std::size_t size) { 411void ARM_Dynarmic_64::InvalidateCacheRange(VAddr addr, std::size_t size) {
391 jit->InvalidateCacheRange(addr, size); 412 jit.load()->InvalidateCacheRange(addr, size);
392} 413}
393 414
394void ARM_Dynarmic_64::ClearExclusiveState() { 415void ARM_Dynarmic_64::ClearExclusiveState() {
395 jit->ClearExclusiveState(); 416 jit.load()->ClearExclusiveState();
396} 417}
397 418
398void ARM_Dynarmic_64::PageTableChanged(Common::PageTable& page_table, 419void ARM_Dynarmic_64::PageTableChanged(Common::PageTable& page_table,
@@ -403,13 +424,48 @@ void ARM_Dynarmic_64::PageTableChanged(Common::PageTable& page_table,
403 auto key = std::make_pair(&page_table, new_address_space_size_in_bits); 424 auto key = std::make_pair(&page_table, new_address_space_size_in_bits);
404 auto iter = jit_cache.find(key); 425 auto iter = jit_cache.find(key);
405 if (iter != jit_cache.end()) { 426 if (iter != jit_cache.end()) {
406 jit = iter->second; 427 jit.store(iter->second.get());
407 LoadContext(ctx); 428 LoadContext(ctx);
408 return; 429 return;
409 } 430 }
410 jit = MakeJit(&page_table, new_address_space_size_in_bits); 431 std::shared_ptr new_jit = MakeJit(&page_table, new_address_space_size_in_bits);
432 jit.store(new_jit.get());
411 LoadContext(ctx); 433 LoadContext(ctx);
412 jit_cache.emplace(key, jit); 434 jit_cache.emplace(key, std::move(new_jit));
435}
436
437std::vector<ARM_Interface::BacktraceEntry> ARM_Dynarmic_64::GetBacktrace(Core::System& system,
438 u64 fp, u64 lr) {
439 std::vector<BacktraceEntry> out;
440 auto& memory = system.Memory();
441
442 // fp (= r29) points to the last frame record.
443 // Note that this is the frame record for the *previous* frame, not the current one.
444 // Note we need to subtract 4 from our last read to get the proper address
445 // Frame records are two words long:
446 // fp+0 : pointer to previous frame record
447 // fp+8 : value of lr for frame
448 while (true) {
449 out.push_back({"", 0, lr, 0, ""});
450 if (!fp) {
451 break;
452 }
453 lr = memory.Read64(fp + 8) - 4;
454 fp = memory.Read64(fp);
455 }
456
457 SymbolicateBacktrace(system, out);
458
459 return out;
460}
461
462std::vector<ARM_Interface::BacktraceEntry> ARM_Dynarmic_64::GetBacktraceFromContext(
463 System& system, const ThreadContext64& ctx) {
464 return GetBacktrace(system, ctx.cpu_registers[29], ctx.cpu_registers[30]);
465}
466
467std::vector<ARM_Interface::BacktraceEntry> ARM_Dynarmic_64::GetBacktrace() const {
468 return GetBacktrace(system, GetReg(29), GetReg(30));
413} 469}
414 470
415} // namespace Core 471} // namespace Core
diff --git a/src/core/arm/dynarmic/arm_dynarmic_64.h b/src/core/arm/dynarmic/arm_dynarmic_64.h
index 0c4e46c64..78773e293 100644
--- a/src/core/arm/dynarmic/arm_dynarmic_64.h
+++ b/src/core/arm/dynarmic/arm_dynarmic_64.h
@@ -4,6 +4,7 @@
4 4
5#pragma once 5#pragma once
6 6
7#include <atomic>
7#include <memory> 8#include <memory>
8#include <unordered_map> 9#include <unordered_map>
9 10
@@ -32,6 +33,7 @@ public:
32 33
33 void SetPC(u64 pc) override; 34 void SetPC(u64 pc) override;
34 u64 GetPC() const override; 35 u64 GetPC() const override;
36 u64 GetSP() const override;
35 u64 GetReg(int index) const override; 37 u64 GetReg(int index) const override;
36 void SetReg(int index, u64 value) override; 38 void SetReg(int index, u64 value) override;
37 u128 GetVectorReg(int index) const override; 39 u128 GetVectorReg(int index) const override;
@@ -51,6 +53,7 @@ public:
51 void LoadContext(const ThreadContext64& ctx) override; 53 void LoadContext(const ThreadContext64& ctx) override;
52 54
53 void PrepareReschedule() override; 55 void PrepareReschedule() override;
56 void SignalInterrupt() override;
54 void ClearExclusiveState() override; 57 void ClearExclusiveState() override;
55 58
56 void ClearInstructionCache() override; 59 void ClearInstructionCache() override;
@@ -58,10 +61,17 @@ public:
58 void PageTableChanged(Common::PageTable& new_page_table, 61 void PageTableChanged(Common::PageTable& new_page_table,
59 std::size_t new_address_space_size_in_bits) override; 62 std::size_t new_address_space_size_in_bits) override;
60 63
64 static std::vector<BacktraceEntry> GetBacktraceFromContext(System& system,
65 const ThreadContext64& ctx);
66
67 std::vector<BacktraceEntry> GetBacktrace() const override;
68
61private: 69private:
62 std::shared_ptr<Dynarmic::A64::Jit> MakeJit(Common::PageTable* page_table, 70 std::shared_ptr<Dynarmic::A64::Jit> MakeJit(Common::PageTable* page_table,
63 std::size_t address_space_bits) const; 71 std::size_t address_space_bits) const;
64 72
73 static std::vector<BacktraceEntry> GetBacktrace(Core::System& system, u64 fp, u64 lr);
74
65 using JitCacheKey = std::pair<Common::PageTable*, std::size_t>; 75 using JitCacheKey = std::pair<Common::PageTable*, std::size_t>;
66 using JitCacheType = 76 using JitCacheType =
67 std::unordered_map<JitCacheKey, std::shared_ptr<Dynarmic::A64::Jit>, Common::PairHash>; 77 std::unordered_map<JitCacheKey, std::shared_ptr<Dynarmic::A64::Jit>, Common::PairHash>;
@@ -73,13 +83,13 @@ private:
73 std::size_t core_index; 83 std::size_t core_index;
74 DynarmicExclusiveMonitor& exclusive_monitor; 84 DynarmicExclusiveMonitor& exclusive_monitor;
75 85
76 std::shared_ptr<Dynarmic::A64::Jit> jit; 86 std::shared_ptr<Dynarmic::A64::Jit> null_jit;
87
88 // A raw pointer here is fine; we never delete Jit instances.
89 std::atomic<Dynarmic::A64::Jit*> jit;
77 90
78 // SVC callback 91 // SVC callback
79 u32 svc_swi{}; 92 u32 svc_swi{};
80 bool svc_called{};
81
82 bool shutdown{};
83}; 93};
84 94
85} // namespace Core 95} // namespace Core
diff --git a/src/core/arm/symbols.cpp b/src/core/arm/symbols.cpp
new file mode 100644
index 000000000..26c44f0c7
--- /dev/null
+++ b/src/core/arm/symbols.cpp
@@ -0,0 +1,190 @@
1// Copyright 2022 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include "common/bit_field.h"
6#include "common/common_funcs.h"
7#include "core/arm/symbols.h"
8#include "core/core.h"
9#include "core/memory.h"
10
11namespace Core {
12namespace {
13
14constexpr u64 ELF_DYNAMIC_TAG_NULL = 0;
15constexpr u64 ELF_DYNAMIC_TAG_STRTAB = 5;
16constexpr u64 ELF_DYNAMIC_TAG_SYMTAB = 6;
17constexpr u64 ELF_DYNAMIC_TAG_SYMENT = 11;
18
19enum class ELFSymbolType : u8 {
20 None = 0,
21 Object = 1,
22 Function = 2,
23 Section = 3,
24 File = 4,
25 Common = 5,
26 TLS = 6,
27};
28
29enum class ELFSymbolBinding : u8 {
30 Local = 0,
31 Global = 1,
32 Weak = 2,
33};
34
35enum class ELFSymbolVisibility : u8 {
36 Default = 0,
37 Internal = 1,
38 Hidden = 2,
39 Protected = 3,
40};
41
42struct ELF64Symbol {
43 u32 name_index;
44 union {
45 u8 info;
46
47 BitField<0, 4, ELFSymbolType> type;
48 BitField<4, 4, ELFSymbolBinding> binding;
49 };
50 ELFSymbolVisibility visibility;
51 u16 sh_index;
52 u64 value;
53 u64 size;
54};
55static_assert(sizeof(ELF64Symbol) == 0x18, "ELF64Symbol has incorrect size.");
56
57struct ELF32Symbol {
58 u32 name_index;
59 u32 value;
60 u32 size;
61 union {
62 u8 info;
63
64 BitField<0, 4, ELFSymbolType> type;
65 BitField<4, 4, ELFSymbolBinding> binding;
66 };
67 ELFSymbolVisibility visibility;
68 u16 sh_index;
69};
70static_assert(sizeof(ELF32Symbol) == 0x10, "ELF32Symbol has incorrect size.");
71
72} // Anonymous namespace
73
74namespace Symbols {
75
76template <typename Word, typename ELFSymbol, typename ByteReader>
77static Symbols GetSymbols(ByteReader ReadBytes) {
78 const auto Read8{[&](u64 index) {
79 u8 ret;
80 ReadBytes(&ret, index, sizeof(u8));
81 return ret;
82 }};
83
84 const auto Read32{[&](u64 index) {
85 u32 ret;
86 ReadBytes(&ret, index, sizeof(u32));
87 return ret;
88 }};
89
90 const auto ReadWord{[&](u64 index) {
91 Word ret;
92 ReadBytes(&ret, index, sizeof(Word));
93 return ret;
94 }};
95
96 const u32 mod_offset = Read32(4);
97
98 if (Read32(mod_offset) != Common::MakeMagic('M', 'O', 'D', '0')) {
99 return {};
100 }
101
102 VAddr string_table_offset{};
103 VAddr symbol_table_offset{};
104 u64 symbol_entry_size{};
105
106 const auto dynamic_offset = Read32(mod_offset + 0x4) + mod_offset;
107
108 VAddr dynamic_index = dynamic_offset;
109 while (true) {
110 const Word tag = ReadWord(dynamic_index);
111 const Word value = ReadWord(dynamic_index + sizeof(Word));
112 dynamic_index += 2 * sizeof(Word);
113
114 if (tag == ELF_DYNAMIC_TAG_NULL) {
115 break;
116 }
117
118 if (tag == ELF_DYNAMIC_TAG_STRTAB) {
119 string_table_offset = value;
120 } else if (tag == ELF_DYNAMIC_TAG_SYMTAB) {
121 symbol_table_offset = value;
122 } else if (tag == ELF_DYNAMIC_TAG_SYMENT) {
123 symbol_entry_size = value;
124 }
125 }
126
127 if (string_table_offset == 0 || symbol_table_offset == 0 || symbol_entry_size == 0) {
128 return {};
129 }
130
131 Symbols out;
132
133 VAddr symbol_index = symbol_table_offset;
134 while (symbol_index < string_table_offset) {
135 ELFSymbol symbol{};
136 ReadBytes(&symbol, symbol_index, sizeof(ELFSymbol));
137
138 VAddr string_offset = string_table_offset + symbol.name_index;
139 std::string name;
140 for (u8 c = Read8(string_offset); c != 0; c = Read8(++string_offset)) {
141 name += static_cast<char>(c);
142 }
143
144 symbol_index += symbol_entry_size;
145 out[name] = std::make_pair(symbol.value, symbol.size);
146 }
147
148 return out;
149}
150
151Symbols GetSymbols(VAddr base, Core::Memory::Memory& memory, bool is_64) {
152 const auto ReadBytes{
153 [&](void* ptr, size_t offset, size_t size) { memory.ReadBlock(base + offset, ptr, size); }};
154
155 if (is_64) {
156 return GetSymbols<u64, ELF64Symbol>(ReadBytes);
157 } else {
158 return GetSymbols<u32, ELF32Symbol>(ReadBytes);
159 }
160}
161
162Symbols GetSymbols(std::span<const u8> data, bool is_64) {
163 const auto ReadBytes{[&](void* ptr, size_t offset, size_t size) {
164 std::memcpy(ptr, data.data() + offset, size);
165 }};
166
167 if (is_64) {
168 return GetSymbols<u64, ELF64Symbol>(ReadBytes);
169 } else {
170 return GetSymbols<u32, ELF32Symbol>(ReadBytes);
171 }
172}
173
174std::optional<std::string> GetSymbolName(const Symbols& symbols, VAddr addr) {
175 const auto iter = std::find_if(symbols.cbegin(), symbols.cend(), [addr](const auto& pair) {
176 const auto& [name, sym_info] = pair;
177 const auto& [start_address, size] = sym_info;
178 const auto end_address = start_address + size;
179 return addr >= start_address && addr < end_address;
180 });
181
182 if (iter == symbols.cend()) {
183 return std::nullopt;
184 }
185
186 return iter->first;
187}
188
189} // namespace Symbols
190} // namespace Core
diff --git a/src/core/arm/symbols.h b/src/core/arm/symbols.h
new file mode 100644
index 000000000..99e6a9f8e
--- /dev/null
+++ b/src/core/arm/symbols.h
@@ -0,0 +1,27 @@
1// Copyright 2022 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <map>
8#include <optional>
9#include <span>
10#include <string>
11#include <utility>
12
13#include "common/common_types.h"
14
15namespace Core::Memory {
16class Memory;
17} // namespace Core::Memory
18
19namespace Core::Symbols {
20
21using Symbols = std::map<std::string, std::pair<VAddr, std::size_t>, std::less<>>;
22
23Symbols GetSymbols(VAddr base, Core::Memory::Memory& memory, bool is_64 = true);
24Symbols GetSymbols(std::span<const u8> data, bool is_64 = true);
25std::optional<std::string> GetSymbolName(const Symbols& symbols, VAddr addr);
26
27} // namespace Core::Symbols
diff --git a/src/core/core_timing.h b/src/core/core_timing.h
index 888828fd0..28b63be43 100644
--- a/src/core/core_timing.h
+++ b/src/core/core_timing.h
@@ -8,13 +8,13 @@
8#include <chrono> 8#include <chrono>
9#include <functional> 9#include <functional>
10#include <memory> 10#include <memory>
11#include <mutex>
11#include <optional> 12#include <optional>
12#include <string> 13#include <string>
13#include <thread> 14#include <thread>
14#include <vector> 15#include <vector>
15 16
16#include "common/common_types.h" 17#include "common/common_types.h"
17#include "common/spin_lock.h"
18#include "common/thread.h" 18#include "common/thread.h"
19#include "common/wall_clock.h" 19#include "common/wall_clock.h"
20 20
@@ -149,8 +149,8 @@ private:
149 std::shared_ptr<EventType> ev_lost; 149 std::shared_ptr<EventType> ev_lost;
150 Common::Event event{}; 150 Common::Event event{};
151 Common::Event pause_event{}; 151 Common::Event pause_event{};
152 Common::SpinLock basic_lock{}; 152 std::mutex basic_lock;
153 Common::SpinLock advance_lock{}; 153 std::mutex advance_lock;
154 std::unique_ptr<std::thread> timer_thread; 154 std::unique_ptr<std::thread> timer_thread;
155 std::atomic<bool> paused{}; 155 std::atomic<bool> paused{};
156 std::atomic<bool> paused_set{}; 156 std::atomic<bool> paused_set{};
diff --git a/src/core/file_sys/patch_manager.cpp b/src/core/file_sys/patch_manager.cpp
index c4e185757..73e724f3d 100644
--- a/src/core/file_sys/patch_manager.cpp
+++ b/src/core/file_sys/patch_manager.cpp
@@ -148,29 +148,33 @@ VirtualDir PatchManager::PatchExeFS(VirtualDir exefs) const {
148 148
149 // LayeredExeFS 149 // LayeredExeFS
150 const auto load_dir = fs_controller.GetModificationLoadRoot(title_id); 150 const auto load_dir = fs_controller.GetModificationLoadRoot(title_id);
151 const auto sdmc_load_dir = fs_controller.GetSDMCModificationLoadRoot(title_id);
152
153 std::vector<VirtualDir> patch_dirs = {sdmc_load_dir};
151 if (load_dir != nullptr && load_dir->GetSize() > 0) { 154 if (load_dir != nullptr && load_dir->GetSize() > 0) {
152 auto patch_dirs = load_dir->GetSubdirectories(); 155 const auto load_patch_dirs = load_dir->GetSubdirectories();
153 std::sort( 156 patch_dirs.insert(patch_dirs.end(), load_patch_dirs.begin(), load_patch_dirs.end());
154 patch_dirs.begin(), patch_dirs.end(), 157 }
155 [](const VirtualDir& l, const VirtualDir& r) { return l->GetName() < r->GetName(); });
156
157 std::vector<VirtualDir> layers;
158 layers.reserve(patch_dirs.size() + 1);
159 for (const auto& subdir : patch_dirs) {
160 if (std::find(disabled.begin(), disabled.end(), subdir->GetName()) != disabled.end())
161 continue;
162 158
163 auto exefs_dir = FindSubdirectoryCaseless(subdir, "exefs"); 159 std::sort(patch_dirs.begin(), patch_dirs.end(),
164 if (exefs_dir != nullptr) 160 [](const VirtualDir& l, const VirtualDir& r) { return l->GetName() < r->GetName(); });
165 layers.push_back(std::move(exefs_dir));
166 }
167 layers.push_back(exefs);
168 161
169 auto layered = LayeredVfsDirectory::MakeLayeredDirectory(std::move(layers)); 162 std::vector<VirtualDir> layers;
170 if (layered != nullptr) { 163 layers.reserve(patch_dirs.size() + 1);
171 LOG_INFO(Loader, " ExeFS: LayeredExeFS patches applied successfully"); 164 for (const auto& subdir : patch_dirs) {
172 exefs = std::move(layered); 165 if (std::find(disabled.begin(), disabled.end(), subdir->GetName()) != disabled.end())
173 } 166 continue;
167
168 auto exefs_dir = FindSubdirectoryCaseless(subdir, "exefs");
169 if (exefs_dir != nullptr)
170 layers.push_back(std::move(exefs_dir));
171 }
172 layers.push_back(exefs);
173
174 auto layered = LayeredVfsDirectory::MakeLayeredDirectory(std::move(layers));
175 if (layered != nullptr) {
176 LOG_INFO(Loader, " ExeFS: LayeredExeFS patches applied successfully");
177 exefs = std::move(layered);
174 } 178 }
175 179
176 if (Settings::values.dump_exefs) { 180 if (Settings::values.dump_exefs) {
@@ -536,11 +540,20 @@ PatchManager::PatchVersionNames PatchManager::GetPatchVersionNames(VirtualFile u
536 540
537 // SDMC mod directory (RomFS LayeredFS) 541 // SDMC mod directory (RomFS LayeredFS)
538 const auto sdmc_mod_dir = fs_controller.GetSDMCModificationLoadRoot(title_id); 542 const auto sdmc_mod_dir = fs_controller.GetSDMCModificationLoadRoot(title_id);
539 if (sdmc_mod_dir != nullptr && sdmc_mod_dir->GetSize() > 0 && 543 if (sdmc_mod_dir != nullptr && sdmc_mod_dir->GetSize() > 0) {
540 IsDirValidAndNonEmpty(FindSubdirectoryCaseless(sdmc_mod_dir, "romfs"))) { 544 std::string types;
541 const auto mod_disabled = 545 if (IsDirValidAndNonEmpty(FindSubdirectoryCaseless(sdmc_mod_dir, "exefs"))) {
542 std::find(disabled.begin(), disabled.end(), "SDMC") != disabled.end(); 546 AppendCommaIfNotEmpty(types, "LayeredExeFS");
543 out.insert_or_assign(mod_disabled ? "[D] SDMC" : "SDMC", "LayeredFS"); 547 }
548 if (IsDirValidAndNonEmpty(FindSubdirectoryCaseless(sdmc_mod_dir, "romfs"))) {
549 AppendCommaIfNotEmpty(types, "LayeredFS");
550 }
551
552 if (!types.empty()) {
553 const auto mod_disabled =
554 std::find(disabled.begin(), disabled.end(), "SDMC") != disabled.end();
555 out.insert_or_assign(mod_disabled ? "[D] SDMC" : "SDMC", types);
556 }
544 } 557 }
545 558
546 // DLC 559 // DLC
diff --git a/src/core/file_sys/registered_cache.cpp b/src/core/file_sys/registered_cache.cpp
index 7a646b5f1..4ada4a69b 100644
--- a/src/core/file_sys/registered_cache.cpp
+++ b/src/core/file_sys/registered_cache.cpp
@@ -387,15 +387,17 @@ std::vector<NcaID> RegisteredCache::AccumulateFiles() const {
387 continue; 387 continue;
388 388
389 for (const auto& nca_dir : d2_dir->GetSubdirectories()) { 389 for (const auto& nca_dir : d2_dir->GetSubdirectories()) {
390 if (!FollowsNcaIdFormat(nca_dir->GetName())) 390 if (nca_dir == nullptr || !FollowsNcaIdFormat(nca_dir->GetName())) {
391 continue; 391 continue;
392 }
392 393
393 ids.push_back(Common::HexStringToArray<0x10, true>(nca_dir->GetName().substr(0, 0x20))); 394 ids.push_back(Common::HexStringToArray<0x10, true>(nca_dir->GetName().substr(0, 0x20)));
394 } 395 }
395 396
396 for (const auto& nca_file : d2_dir->GetFiles()) { 397 for (const auto& nca_file : d2_dir->GetFiles()) {
397 if (!FollowsNcaIdFormat(nca_file->GetName())) 398 if (nca_file == nullptr || !FollowsNcaIdFormat(nca_file->GetName())) {
398 continue; 399 continue;
400 }
399 401
400 ids.push_back( 402 ids.push_back(
401 Common::HexStringToArray<0x10, true>(nca_file->GetName().substr(0, 0x20))); 403 Common::HexStringToArray<0x10, true>(nca_file->GetName().substr(0, 0x20)));
diff --git a/src/core/frontend/applets/mii.cpp b/src/core/frontend/applets/mii.cpp
deleted file mode 100644
index 1c05ff412..000000000
--- a/src/core/frontend/applets/mii.cpp
+++ /dev/null
@@ -1,19 +0,0 @@
1// Copyright 2022 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include "common/logging/log.h"
6#include "core/frontend/applets/mii.h"
7
8namespace Core::Frontend {
9
10MiiApplet::~MiiApplet() = default;
11
12void DefaultMiiApplet::ShowMii(
13 const MiiParameters& parameters,
14 const std::function<void(const Core::Frontend::MiiParameters& parameters)> callback) const {
15 LOG_INFO(Service_HID, "(STUBBED) called");
16 callback(parameters);
17}
18
19} // namespace Core::Frontend
diff --git a/src/core/frontend/applets/mii.h b/src/core/frontend/applets/mii.h
deleted file mode 100644
index 11ba8a359..000000000
--- a/src/core/frontend/applets/mii.h
+++ /dev/null
@@ -1,34 +0,0 @@
1// Copyright 2022 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <functional>
8
9#include "core/hle/service/mii/mii_manager.h"
10
11namespace Core::Frontend {
12
13struct MiiParameters {
14 bool is_editable;
15 Service::Mii::MiiInfo mii_data{};
16};
17
18class MiiApplet {
19public:
20 virtual ~MiiApplet();
21
22 virtual void ShowMii(const MiiParameters& parameters,
23 const std::function<void(const Core::Frontend::MiiParameters& parameters)>
24 callback) const = 0;
25};
26
27class DefaultMiiApplet final : public MiiApplet {
28public:
29 void ShowMii(const MiiParameters& parameters,
30 const std::function<void(const Core::Frontend::MiiParameters& parameters)>
31 callback) const override;
32};
33
34} // namespace Core::Frontend
diff --git a/src/core/frontend/applets/mii_edit.cpp b/src/core/frontend/applets/mii_edit.cpp
new file mode 100644
index 000000000..fadb5fb15
--- /dev/null
+++ b/src/core/frontend/applets/mii_edit.cpp
@@ -0,0 +1,18 @@
1// Copyright 2022 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include "common/logging/log.h"
6#include "core/frontend/applets/mii_edit.h"
7
8namespace Core::Frontend {
9
10MiiEditApplet::~MiiEditApplet() = default;
11
12void DefaultMiiEditApplet::ShowMiiEdit(const std::function<void()>& callback) const {
13 LOG_WARNING(Service_AM, "(STUBBED) called");
14
15 callback();
16}
17
18} // namespace Core::Frontend
diff --git a/src/core/frontend/applets/mii_edit.h b/src/core/frontend/applets/mii_edit.h
new file mode 100644
index 000000000..cca0e931d
--- /dev/null
+++ b/src/core/frontend/applets/mii_edit.h
@@ -0,0 +1,23 @@
1// Copyright 2022 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <functional>
8
9namespace Core::Frontend {
10
11class MiiEditApplet {
12public:
13 virtual ~MiiEditApplet();
14
15 virtual void ShowMiiEdit(const std::function<void()>& callback) const = 0;
16};
17
18class DefaultMiiEditApplet final : public MiiEditApplet {
19public:
20 void ShowMiiEdit(const std::function<void()>& callback) const override;
21};
22
23} // namespace Core::Frontend
diff --git a/src/core/hid/emulated_console.cpp b/src/core/hid/emulated_console.cpp
index eef0ff493..de565048b 100644
--- a/src/core/hid/emulated_console.cpp
+++ b/src/core/hid/emulated_console.cpp
@@ -132,7 +132,7 @@ void EmulatedConsole::SetMotionParam(Common::ParamPackage param) {
132} 132}
133 133
134void EmulatedConsole::SetMotion(const Common::Input::CallbackStatus& callback) { 134void EmulatedConsole::SetMotion(const Common::Input::CallbackStatus& callback) {
135 std::lock_guard lock{mutex}; 135 std::unique_lock lock{mutex};
136 auto& raw_status = console.motion_values.raw_status; 136 auto& raw_status = console.motion_values.raw_status;
137 auto& emulated = console.motion_values.emulated; 137 auto& emulated = console.motion_values.emulated;
138 138
@@ -151,6 +151,7 @@ void EmulatedConsole::SetMotion(const Common::Input::CallbackStatus& callback) {
151 emulated.UpdateOrientation(raw_status.delta_timestamp); 151 emulated.UpdateOrientation(raw_status.delta_timestamp);
152 152
153 if (is_configuring) { 153 if (is_configuring) {
154 lock.unlock();
154 TriggerOnChange(ConsoleTriggerType::Motion); 155 TriggerOnChange(ConsoleTriggerType::Motion);
155 return; 156 return;
156 } 157 }
@@ -166,6 +167,7 @@ void EmulatedConsole::SetMotion(const Common::Input::CallbackStatus& callback) {
166 // Find what is this value 167 // Find what is this value
167 motion.verticalization_error = 0.0f; 168 motion.verticalization_error = 0.0f;
168 169
170 lock.unlock();
169 TriggerOnChange(ConsoleTriggerType::Motion); 171 TriggerOnChange(ConsoleTriggerType::Motion);
170} 172}
171 173
@@ -173,11 +175,12 @@ void EmulatedConsole::SetTouch(const Common::Input::CallbackStatus& callback, st
173 if (index >= console.touch_values.size()) { 175 if (index >= console.touch_values.size()) {
174 return; 176 return;
175 } 177 }
176 std::lock_guard lock{mutex}; 178 std::unique_lock lock{mutex};
177 179
178 console.touch_values[index] = TransformToTouch(callback); 180 console.touch_values[index] = TransformToTouch(callback);
179 181
180 if (is_configuring) { 182 if (is_configuring) {
183 lock.unlock();
181 TriggerOnChange(ConsoleTriggerType::Touch); 184 TriggerOnChange(ConsoleTriggerType::Touch);
182 return; 185 return;
183 } 186 }
@@ -189,26 +192,32 @@ void EmulatedConsole::SetTouch(const Common::Input::CallbackStatus& callback, st
189 .pressed = console.touch_values[index].pressed.value, 192 .pressed = console.touch_values[index].pressed.value,
190 }; 193 };
191 194
195 lock.unlock();
192 TriggerOnChange(ConsoleTriggerType::Touch); 196 TriggerOnChange(ConsoleTriggerType::Touch);
193} 197}
194 198
195ConsoleMotionValues EmulatedConsole::GetMotionValues() const { 199ConsoleMotionValues EmulatedConsole::GetMotionValues() const {
200 std::scoped_lock lock{mutex};
196 return console.motion_values; 201 return console.motion_values;
197} 202}
198 203
199TouchValues EmulatedConsole::GetTouchValues() const { 204TouchValues EmulatedConsole::GetTouchValues() const {
205 std::scoped_lock lock{mutex};
200 return console.touch_values; 206 return console.touch_values;
201} 207}
202 208
203ConsoleMotion EmulatedConsole::GetMotion() const { 209ConsoleMotion EmulatedConsole::GetMotion() const {
210 std::scoped_lock lock{mutex};
204 return console.motion_state; 211 return console.motion_state;
205} 212}
206 213
207TouchFingerState EmulatedConsole::GetTouch() const { 214TouchFingerState EmulatedConsole::GetTouch() const {
215 std::scoped_lock lock{mutex};
208 return console.touch_state; 216 return console.touch_state;
209} 217}
210 218
211void EmulatedConsole::TriggerOnChange(ConsoleTriggerType type) { 219void EmulatedConsole::TriggerOnChange(ConsoleTriggerType type) {
220 std::scoped_lock lock{callback_mutex};
212 for (const auto& poller_pair : callback_list) { 221 for (const auto& poller_pair : callback_list) {
213 const ConsoleUpdateCallback& poller = poller_pair.second; 222 const ConsoleUpdateCallback& poller = poller_pair.second;
214 if (poller.on_change) { 223 if (poller.on_change) {
@@ -218,13 +227,13 @@ void EmulatedConsole::TriggerOnChange(ConsoleTriggerType type) {
218} 227}
219 228
220int EmulatedConsole::SetCallback(ConsoleUpdateCallback update_callback) { 229int EmulatedConsole::SetCallback(ConsoleUpdateCallback update_callback) {
221 std::lock_guard lock{mutex}; 230 std::scoped_lock lock{callback_mutex};
222 callback_list.insert_or_assign(last_callback_key, update_callback); 231 callback_list.insert_or_assign(last_callback_key, update_callback);
223 return last_callback_key++; 232 return last_callback_key++;
224} 233}
225 234
226void EmulatedConsole::DeleteCallback(int key) { 235void EmulatedConsole::DeleteCallback(int key) {
227 std::lock_guard lock{mutex}; 236 std::scoped_lock lock{callback_mutex};
228 const auto& iterator = callback_list.find(key); 237 const auto& iterator = callback_list.find(key);
229 if (iterator == callback_list.end()) { 238 if (iterator == callback_list.end()) {
230 LOG_ERROR(Input, "Tried to delete non-existent callback {}", key); 239 LOG_ERROR(Input, "Tried to delete non-existent callback {}", key);
diff --git a/src/core/hid/emulated_console.h b/src/core/hid/emulated_console.h
index 5eb170823..53677bdc5 100644
--- a/src/core/hid/emulated_console.h
+++ b/src/core/hid/emulated_console.h
@@ -183,6 +183,7 @@ private:
183 TouchDevices touch_devices; 183 TouchDevices touch_devices;
184 184
185 mutable std::mutex mutex; 185 mutable std::mutex mutex;
186 mutable std::mutex callback_mutex;
186 std::unordered_map<int, ConsoleUpdateCallback> callback_list; 187 std::unordered_map<int, ConsoleUpdateCallback> callback_list;
187 int last_callback_key = 0; 188 int last_callback_key = 0;
188 189
diff --git a/src/core/hid/emulated_controller.cpp b/src/core/hid/emulated_controller.cpp
index 7e05666d6..c3f21066c 100644
--- a/src/core/hid/emulated_controller.cpp
+++ b/src/core/hid/emulated_controller.cpp
@@ -353,14 +353,17 @@ void EmulatedController::DisableConfiguration() {
353} 353}
354 354
355void EmulatedController::EnableSystemButtons() { 355void EmulatedController::EnableSystemButtons() {
356 std::scoped_lock lock{mutex};
356 system_buttons_enabled = true; 357 system_buttons_enabled = true;
357} 358}
358 359
359void EmulatedController::DisableSystemButtons() { 360void EmulatedController::DisableSystemButtons() {
361 std::scoped_lock lock{mutex};
360 system_buttons_enabled = false; 362 system_buttons_enabled = false;
361} 363}
362 364
363void EmulatedController::ResetSystemButtons() { 365void EmulatedController::ResetSystemButtons() {
366 std::scoped_lock lock{mutex};
364 controller.home_button_state.home.Assign(false); 367 controller.home_button_state.home.Assign(false);
365 controller.capture_button_state.capture.Assign(false); 368 controller.capture_button_state.capture.Assign(false);
366} 369}
@@ -494,139 +497,141 @@ void EmulatedController::SetButton(const Common::Input::CallbackStatus& callback
494 if (index >= controller.button_values.size()) { 497 if (index >= controller.button_values.size()) {
495 return; 498 return;
496 } 499 }
497 { 500 std::unique_lock lock{mutex};
498 std::lock_guard lock{mutex}; 501 bool value_changed = false;
499 bool value_changed = false; 502 const auto new_status = TransformToButton(callback);
500 const auto new_status = TransformToButton(callback); 503 auto& current_status = controller.button_values[index];
501 auto& current_status = controller.button_values[index];
502 504
503 // Only read button values that have the same uuid or are pressed once 505 // Only read button values that have the same uuid or are pressed once
504 if (current_status.uuid != uuid) { 506 if (current_status.uuid != uuid) {
505 if (!new_status.value) { 507 if (!new_status.value) {
506 return; 508 return;
507 }
508 } 509 }
510 }
509 511
510 current_status.toggle = new_status.toggle; 512 current_status.toggle = new_status.toggle;
511 current_status.uuid = uuid; 513 current_status.uuid = uuid;
512
513 // Update button status with current
514 if (!current_status.toggle) {
515 current_status.locked = false;
516 if (current_status.value != new_status.value) {
517 current_status.value = new_status.value;
518 value_changed = true;
519 }
520 } else {
521 // Toggle button and lock status
522 if (new_status.value && !current_status.locked) {
523 current_status.locked = true;
524 current_status.value = !current_status.value;
525 value_changed = true;
526 }
527 514
528 // Unlock button ready for next press 515 // Update button status with current
529 if (!new_status.value && current_status.locked) { 516 if (!current_status.toggle) {
530 current_status.locked = false; 517 current_status.locked = false;
531 } 518 if (current_status.value != new_status.value) {
519 current_status.value = new_status.value;
520 value_changed = true;
532 } 521 }
533 522 } else {
534 if (!value_changed) { 523 // Toggle button and lock status
535 return; 524 if (new_status.value && !current_status.locked) {
525 current_status.locked = true;
526 current_status.value = !current_status.value;
527 value_changed = true;
536 } 528 }
537 529
538 if (is_configuring) { 530 // Unlock button ready for next press
539 controller.npad_button_state.raw = NpadButton::None; 531 if (!new_status.value && current_status.locked) {
540 controller.debug_pad_button_state.raw = 0; 532 current_status.locked = false;
541 TriggerOnChange(ControllerTriggerType::Button, false);
542 return;
543 } 533 }
534 }
544 535
545 switch (index) { 536 if (!value_changed) {
546 case Settings::NativeButton::A: 537 return;
547 controller.npad_button_state.a.Assign(current_status.value); 538 }
548 controller.debug_pad_button_state.a.Assign(current_status.value); 539
549 break; 540 if (is_configuring) {
550 case Settings::NativeButton::B: 541 controller.npad_button_state.raw = NpadButton::None;
551 controller.npad_button_state.b.Assign(current_status.value); 542 controller.debug_pad_button_state.raw = 0;
552 controller.debug_pad_button_state.b.Assign(current_status.value); 543 lock.unlock();
553 break; 544 TriggerOnChange(ControllerTriggerType::Button, false);
554 case Settings::NativeButton::X: 545 return;
555 controller.npad_button_state.x.Assign(current_status.value); 546 }
556 controller.debug_pad_button_state.x.Assign(current_status.value); 547
557 break; 548 switch (index) {
558 case Settings::NativeButton::Y: 549 case Settings::NativeButton::A:
559 controller.npad_button_state.y.Assign(current_status.value); 550 controller.npad_button_state.a.Assign(current_status.value);
560 controller.debug_pad_button_state.y.Assign(current_status.value); 551 controller.debug_pad_button_state.a.Assign(current_status.value);
561 break; 552 break;
562 case Settings::NativeButton::LStick: 553 case Settings::NativeButton::B:
563 controller.npad_button_state.stick_l.Assign(current_status.value); 554 controller.npad_button_state.b.Assign(current_status.value);
564 break; 555 controller.debug_pad_button_state.b.Assign(current_status.value);
565 case Settings::NativeButton::RStick: 556 break;
566 controller.npad_button_state.stick_r.Assign(current_status.value); 557 case Settings::NativeButton::X:
567 break; 558 controller.npad_button_state.x.Assign(current_status.value);
568 case Settings::NativeButton::L: 559 controller.debug_pad_button_state.x.Assign(current_status.value);
569 controller.npad_button_state.l.Assign(current_status.value); 560 break;
570 controller.debug_pad_button_state.l.Assign(current_status.value); 561 case Settings::NativeButton::Y:
571 break; 562 controller.npad_button_state.y.Assign(current_status.value);
572 case Settings::NativeButton::R: 563 controller.debug_pad_button_state.y.Assign(current_status.value);
573 controller.npad_button_state.r.Assign(current_status.value); 564 break;
574 controller.debug_pad_button_state.r.Assign(current_status.value); 565 case Settings::NativeButton::LStick:
575 break; 566 controller.npad_button_state.stick_l.Assign(current_status.value);
576 case Settings::NativeButton::ZL: 567 break;
577 controller.npad_button_state.zl.Assign(current_status.value); 568 case Settings::NativeButton::RStick:
578 controller.debug_pad_button_state.zl.Assign(current_status.value); 569 controller.npad_button_state.stick_r.Assign(current_status.value);
579 break; 570 break;
580 case Settings::NativeButton::ZR: 571 case Settings::NativeButton::L:
581 controller.npad_button_state.zr.Assign(current_status.value); 572 controller.npad_button_state.l.Assign(current_status.value);
582 controller.debug_pad_button_state.zr.Assign(current_status.value); 573 controller.debug_pad_button_state.l.Assign(current_status.value);
583 break; 574 break;
584 case Settings::NativeButton::Plus: 575 case Settings::NativeButton::R:
585 controller.npad_button_state.plus.Assign(current_status.value); 576 controller.npad_button_state.r.Assign(current_status.value);
586 controller.debug_pad_button_state.plus.Assign(current_status.value); 577 controller.debug_pad_button_state.r.Assign(current_status.value);
587 break; 578 break;
588 case Settings::NativeButton::Minus: 579 case Settings::NativeButton::ZL:
589 controller.npad_button_state.minus.Assign(current_status.value); 580 controller.npad_button_state.zl.Assign(current_status.value);
590 controller.debug_pad_button_state.minus.Assign(current_status.value); 581 controller.debug_pad_button_state.zl.Assign(current_status.value);
591 break; 582 break;
592 case Settings::NativeButton::DLeft: 583 case Settings::NativeButton::ZR:
593 controller.npad_button_state.left.Assign(current_status.value); 584 controller.npad_button_state.zr.Assign(current_status.value);
594 controller.debug_pad_button_state.d_left.Assign(current_status.value); 585 controller.debug_pad_button_state.zr.Assign(current_status.value);
595 break; 586 break;
596 case Settings::NativeButton::DUp: 587 case Settings::NativeButton::Plus:
597 controller.npad_button_state.up.Assign(current_status.value); 588 controller.npad_button_state.plus.Assign(current_status.value);
598 controller.debug_pad_button_state.d_up.Assign(current_status.value); 589 controller.debug_pad_button_state.plus.Assign(current_status.value);
599 break; 590 break;
600 case Settings::NativeButton::DRight: 591 case Settings::NativeButton::Minus:
601 controller.npad_button_state.right.Assign(current_status.value); 592 controller.npad_button_state.minus.Assign(current_status.value);
602 controller.debug_pad_button_state.d_right.Assign(current_status.value); 593 controller.debug_pad_button_state.minus.Assign(current_status.value);
603 break; 594 break;
604 case Settings::NativeButton::DDown: 595 case Settings::NativeButton::DLeft:
605 controller.npad_button_state.down.Assign(current_status.value); 596 controller.npad_button_state.left.Assign(current_status.value);
606 controller.debug_pad_button_state.d_down.Assign(current_status.value); 597 controller.debug_pad_button_state.d_left.Assign(current_status.value);
607 break; 598 break;
608 case Settings::NativeButton::SL: 599 case Settings::NativeButton::DUp:
609 controller.npad_button_state.left_sl.Assign(current_status.value); 600 controller.npad_button_state.up.Assign(current_status.value);
610 controller.npad_button_state.right_sl.Assign(current_status.value); 601 controller.debug_pad_button_state.d_up.Assign(current_status.value);
611 break; 602 break;
612 case Settings::NativeButton::SR: 603 case Settings::NativeButton::DRight:
613 controller.npad_button_state.left_sr.Assign(current_status.value); 604 controller.npad_button_state.right.Assign(current_status.value);
614 controller.npad_button_state.right_sr.Assign(current_status.value); 605 controller.debug_pad_button_state.d_right.Assign(current_status.value);
615 break; 606 break;
616 case Settings::NativeButton::Home: 607 case Settings::NativeButton::DDown:
617 if (!system_buttons_enabled) { 608 controller.npad_button_state.down.Assign(current_status.value);
618 break; 609 controller.debug_pad_button_state.d_down.Assign(current_status.value);
619 } 610 break;
620 controller.home_button_state.home.Assign(current_status.value); 611 case Settings::NativeButton::SL:
612 controller.npad_button_state.left_sl.Assign(current_status.value);
613 controller.npad_button_state.right_sl.Assign(current_status.value);
614 break;
615 case Settings::NativeButton::SR:
616 controller.npad_button_state.left_sr.Assign(current_status.value);
617 controller.npad_button_state.right_sr.Assign(current_status.value);
618 break;
619 case Settings::NativeButton::Home:
620 if (!system_buttons_enabled) {
621 break; 621 break;
622 case Settings::NativeButton::Screenshot: 622 }
623 if (!system_buttons_enabled) { 623 controller.home_button_state.home.Assign(current_status.value);
624 break; 624 break;
625 } 625 case Settings::NativeButton::Screenshot:
626 controller.capture_button_state.capture.Assign(current_status.value); 626 if (!system_buttons_enabled) {
627 break; 627 break;
628 } 628 }
629 controller.capture_button_state.capture.Assign(current_status.value);
630 break;
629 } 631 }
632
633 lock.unlock();
634
630 if (!is_connected) { 635 if (!is_connected) {
631 if (npad_id_type == NpadIdType::Player1 && npad_type != NpadStyleIndex::Handheld) { 636 if (npad_id_type == NpadIdType::Player1 && npad_type != NpadStyleIndex::Handheld) {
632 Connect(); 637 Connect();
@@ -643,7 +648,7 @@ void EmulatedController::SetStick(const Common::Input::CallbackStatus& callback,
643 if (index >= controller.stick_values.size()) { 648 if (index >= controller.stick_values.size()) {
644 return; 649 return;
645 } 650 }
646 std::lock_guard lock{mutex}; 651 std::unique_lock lock{mutex};
647 const auto stick_value = TransformToStick(callback); 652 const auto stick_value = TransformToStick(callback);
648 653
649 // Only read stick values that have the same uuid or are over the threshold to avoid flapping 654 // Only read stick values that have the same uuid or are over the threshold to avoid flapping
@@ -659,6 +664,7 @@ void EmulatedController::SetStick(const Common::Input::CallbackStatus& callback,
659 if (is_configuring) { 664 if (is_configuring) {
660 controller.analog_stick_state.left = {}; 665 controller.analog_stick_state.left = {};
661 controller.analog_stick_state.right = {}; 666 controller.analog_stick_state.right = {};
667 lock.unlock();
662 TriggerOnChange(ControllerTriggerType::Stick, false); 668 TriggerOnChange(ControllerTriggerType::Stick, false);
663 return; 669 return;
664 } 670 }
@@ -685,6 +691,7 @@ void EmulatedController::SetStick(const Common::Input::CallbackStatus& callback,
685 break; 691 break;
686 } 692 }
687 693
694 lock.unlock();
688 TriggerOnChange(ControllerTriggerType::Stick, true); 695 TriggerOnChange(ControllerTriggerType::Stick, true);
689} 696}
690 697
@@ -693,7 +700,7 @@ void EmulatedController::SetTrigger(const Common::Input::CallbackStatus& callbac
693 if (index >= controller.trigger_values.size()) { 700 if (index >= controller.trigger_values.size()) {
694 return; 701 return;
695 } 702 }
696 std::lock_guard lock{mutex}; 703 std::unique_lock lock{mutex};
697 const auto trigger_value = TransformToTrigger(callback); 704 const auto trigger_value = TransformToTrigger(callback);
698 705
699 // Only read trigger values that have the same uuid or are pressed once 706 // Only read trigger values that have the same uuid or are pressed once
@@ -709,6 +716,7 @@ void EmulatedController::SetTrigger(const Common::Input::CallbackStatus& callbac
709 if (is_configuring) { 716 if (is_configuring) {
710 controller.gc_trigger_state.left = 0; 717 controller.gc_trigger_state.left = 0;
711 controller.gc_trigger_state.right = 0; 718 controller.gc_trigger_state.right = 0;
719 lock.unlock();
712 TriggerOnChange(ControllerTriggerType::Trigger, false); 720 TriggerOnChange(ControllerTriggerType::Trigger, false);
713 return; 721 return;
714 } 722 }
@@ -727,6 +735,7 @@ void EmulatedController::SetTrigger(const Common::Input::CallbackStatus& callbac
727 break; 735 break;
728 } 736 }
729 737
738 lock.unlock();
730 TriggerOnChange(ControllerTriggerType::Trigger, true); 739 TriggerOnChange(ControllerTriggerType::Trigger, true);
731} 740}
732 741
@@ -735,7 +744,7 @@ void EmulatedController::SetMotion(const Common::Input::CallbackStatus& callback
735 if (index >= controller.motion_values.size()) { 744 if (index >= controller.motion_values.size()) {
736 return; 745 return;
737 } 746 }
738 std::lock_guard lock{mutex}; 747 std::unique_lock lock{mutex};
739 auto& raw_status = controller.motion_values[index].raw_status; 748 auto& raw_status = controller.motion_values[index].raw_status;
740 auto& emulated = controller.motion_values[index].emulated; 749 auto& emulated = controller.motion_values[index].emulated;
741 750
@@ -756,6 +765,7 @@ void EmulatedController::SetMotion(const Common::Input::CallbackStatus& callback
756 force_update_motion = raw_status.force_update; 765 force_update_motion = raw_status.force_update;
757 766
758 if (is_configuring) { 767 if (is_configuring) {
768 lock.unlock();
759 TriggerOnChange(ControllerTriggerType::Motion, false); 769 TriggerOnChange(ControllerTriggerType::Motion, false);
760 return; 770 return;
761 } 771 }
@@ -767,6 +777,7 @@ void EmulatedController::SetMotion(const Common::Input::CallbackStatus& callback
767 motion.orientation = emulated.GetOrientation(); 777 motion.orientation = emulated.GetOrientation();
768 motion.is_at_rest = !emulated.IsMoving(motion_sensitivity); 778 motion.is_at_rest = !emulated.IsMoving(motion_sensitivity);
769 779
780 lock.unlock();
770 TriggerOnChange(ControllerTriggerType::Motion, true); 781 TriggerOnChange(ControllerTriggerType::Motion, true);
771} 782}
772 783
@@ -775,10 +786,11 @@ void EmulatedController::SetBattery(const Common::Input::CallbackStatus& callbac
775 if (index >= controller.battery_values.size()) { 786 if (index >= controller.battery_values.size()) {
776 return; 787 return;
777 } 788 }
778 std::lock_guard lock{mutex}; 789 std::unique_lock lock{mutex};
779 controller.battery_values[index] = TransformToBattery(callback); 790 controller.battery_values[index] = TransformToBattery(callback);
780 791
781 if (is_configuring) { 792 if (is_configuring) {
793 lock.unlock();
782 TriggerOnChange(ControllerTriggerType::Battery, false); 794 TriggerOnChange(ControllerTriggerType::Battery, false);
783 return; 795 return;
784 } 796 }
@@ -835,6 +847,8 @@ void EmulatedController::SetBattery(const Common::Input::CallbackStatus& callbac
835 }; 847 };
836 break; 848 break;
837 } 849 }
850
851 lock.unlock();
838 TriggerOnChange(ControllerTriggerType::Battery, true); 852 TriggerOnChange(ControllerTriggerType::Battery, true);
839} 853}
840 854
@@ -932,6 +946,7 @@ void EmulatedController::SetSupportedNpadStyleTag(NpadStyleTag supported_styles)
932} 946}
933 947
934bool EmulatedController::IsControllerFullkey(bool use_temporary_value) const { 948bool EmulatedController::IsControllerFullkey(bool use_temporary_value) const {
949 std::scoped_lock lock{mutex};
935 const auto type = is_configuring && use_temporary_value ? tmp_npad_type : npad_type; 950 const auto type = is_configuring && use_temporary_value ? tmp_npad_type : npad_type;
936 switch (type) { 951 switch (type) {
937 case NpadStyleIndex::ProController: 952 case NpadStyleIndex::ProController:
@@ -947,6 +962,7 @@ bool EmulatedController::IsControllerFullkey(bool use_temporary_value) const {
947} 962}
948 963
949bool EmulatedController::IsControllerSupported(bool use_temporary_value) const { 964bool EmulatedController::IsControllerSupported(bool use_temporary_value) const {
965 std::scoped_lock lock{mutex};
950 const auto type = is_configuring && use_temporary_value ? tmp_npad_type : npad_type; 966 const auto type = is_configuring && use_temporary_value ? tmp_npad_type : npad_type;
951 switch (type) { 967 switch (type) {
952 case NpadStyleIndex::ProController: 968 case NpadStyleIndex::ProController:
@@ -982,40 +998,44 @@ void EmulatedController::Connect(bool use_temporary_value) {
982 LOG_ERROR(Service_HID, "Controller type {} is not supported", type); 998 LOG_ERROR(Service_HID, "Controller type {} is not supported", type);
983 return; 999 return;
984 } 1000 }
985 {
986 std::lock_guard lock{mutex};
987 if (is_configuring) {
988 tmp_is_connected = true;
989 TriggerOnChange(ControllerTriggerType::Connected, false);
990 return;
991 }
992 1001
993 if (is_connected) { 1002 std::unique_lock lock{mutex};
994 return; 1003 if (is_configuring) {
995 } 1004 tmp_is_connected = true;
996 is_connected = true; 1005 lock.unlock();
1006 TriggerOnChange(ControllerTriggerType::Connected, false);
1007 return;
1008 }
1009
1010 if (is_connected) {
1011 return;
997 } 1012 }
1013 is_connected = true;
1014
1015 lock.unlock();
998 TriggerOnChange(ControllerTriggerType::Connected, true); 1016 TriggerOnChange(ControllerTriggerType::Connected, true);
999} 1017}
1000 1018
1001void EmulatedController::Disconnect() { 1019void EmulatedController::Disconnect() {
1002 { 1020 std::unique_lock lock{mutex};
1003 std::lock_guard lock{mutex}; 1021 if (is_configuring) {
1004 if (is_configuring) { 1022 tmp_is_connected = false;
1005 tmp_is_connected = false; 1023 lock.unlock();
1006 TriggerOnChange(ControllerTriggerType::Disconnected, false); 1024 TriggerOnChange(ControllerTriggerType::Disconnected, false);
1007 return; 1025 return;
1008 } 1026 }
1009 1027
1010 if (!is_connected) { 1028 if (!is_connected) {
1011 return; 1029 return;
1012 }
1013 is_connected = false;
1014 } 1030 }
1031 is_connected = false;
1032
1033 lock.unlock();
1015 TriggerOnChange(ControllerTriggerType::Disconnected, true); 1034 TriggerOnChange(ControllerTriggerType::Disconnected, true);
1016} 1035}
1017 1036
1018bool EmulatedController::IsConnected(bool get_temporary_value) const { 1037bool EmulatedController::IsConnected(bool get_temporary_value) const {
1038 std::scoped_lock lock{mutex};
1019 if (get_temporary_value && is_configuring) { 1039 if (get_temporary_value && is_configuring) {
1020 return tmp_is_connected; 1040 return tmp_is_connected;
1021 } 1041 }
@@ -1029,10 +1049,12 @@ bool EmulatedController::IsVibrationEnabled() const {
1029} 1049}
1030 1050
1031NpadIdType EmulatedController::GetNpadIdType() const { 1051NpadIdType EmulatedController::GetNpadIdType() const {
1052 std::scoped_lock lock{mutex};
1032 return npad_id_type; 1053 return npad_id_type;
1033} 1054}
1034 1055
1035NpadStyleIndex EmulatedController::GetNpadStyleIndex(bool get_temporary_value) const { 1056NpadStyleIndex EmulatedController::GetNpadStyleIndex(bool get_temporary_value) const {
1057 std::scoped_lock lock{mutex};
1036 if (get_temporary_value && is_configuring) { 1058 if (get_temporary_value && is_configuring) {
1037 return tmp_npad_type; 1059 return tmp_npad_type;
1038 } 1060 }
@@ -1040,27 +1062,28 @@ NpadStyleIndex EmulatedController::GetNpadStyleIndex(bool get_temporary_value) c
1040} 1062}
1041 1063
1042void EmulatedController::SetNpadStyleIndex(NpadStyleIndex npad_type_) { 1064void EmulatedController::SetNpadStyleIndex(NpadStyleIndex npad_type_) {
1043 { 1065 std::unique_lock lock{mutex};
1044 std::lock_guard lock{mutex};
1045 1066
1046 if (is_configuring) { 1067 if (is_configuring) {
1047 if (tmp_npad_type == npad_type_) { 1068 if (tmp_npad_type == npad_type_) {
1048 return;
1049 }
1050 tmp_npad_type = npad_type_;
1051 TriggerOnChange(ControllerTriggerType::Type, false);
1052 return; 1069 return;
1053 } 1070 }
1071 tmp_npad_type = npad_type_;
1072 lock.unlock();
1073 TriggerOnChange(ControllerTriggerType::Type, false);
1074 return;
1075 }
1054 1076
1055 if (npad_type == npad_type_) { 1077 if (npad_type == npad_type_) {
1056 return; 1078 return;
1057 } 1079 }
1058 if (is_connected) { 1080 if (is_connected) {
1059 LOG_WARNING(Service_HID, "Controller {} type changed while it's connected", 1081 LOG_WARNING(Service_HID, "Controller {} type changed while it's connected",
1060 NpadIdTypeToIndex(npad_id_type)); 1082 NpadIdTypeToIndex(npad_id_type));
1061 }
1062 npad_type = npad_type_;
1063 } 1083 }
1084 npad_type = npad_type_;
1085
1086 lock.unlock();
1064 TriggerOnChange(ControllerTriggerType::Type, true); 1087 TriggerOnChange(ControllerTriggerType::Type, true);
1065} 1088}
1066 1089
@@ -1088,30 +1111,37 @@ LedPattern EmulatedController::GetLedPattern() const {
1088} 1111}
1089 1112
1090ButtonValues EmulatedController::GetButtonsValues() const { 1113ButtonValues EmulatedController::GetButtonsValues() const {
1114 std::scoped_lock lock{mutex};
1091 return controller.button_values; 1115 return controller.button_values;
1092} 1116}
1093 1117
1094SticksValues EmulatedController::GetSticksValues() const { 1118SticksValues EmulatedController::GetSticksValues() const {
1119 std::scoped_lock lock{mutex};
1095 return controller.stick_values; 1120 return controller.stick_values;
1096} 1121}
1097 1122
1098TriggerValues EmulatedController::GetTriggersValues() const { 1123TriggerValues EmulatedController::GetTriggersValues() const {
1124 std::scoped_lock lock{mutex};
1099 return controller.trigger_values; 1125 return controller.trigger_values;
1100} 1126}
1101 1127
1102ControllerMotionValues EmulatedController::GetMotionValues() const { 1128ControllerMotionValues EmulatedController::GetMotionValues() const {
1129 std::scoped_lock lock{mutex};
1103 return controller.motion_values; 1130 return controller.motion_values;
1104} 1131}
1105 1132
1106ColorValues EmulatedController::GetColorsValues() const { 1133ColorValues EmulatedController::GetColorsValues() const {
1134 std::scoped_lock lock{mutex};
1107 return controller.color_values; 1135 return controller.color_values;
1108} 1136}
1109 1137
1110BatteryValues EmulatedController::GetBatteryValues() const { 1138BatteryValues EmulatedController::GetBatteryValues() const {
1139 std::scoped_lock lock{mutex};
1111 return controller.battery_values; 1140 return controller.battery_values;
1112} 1141}
1113 1142
1114HomeButtonState EmulatedController::GetHomeButtons() const { 1143HomeButtonState EmulatedController::GetHomeButtons() const {
1144 std::scoped_lock lock{mutex};
1115 if (is_configuring) { 1145 if (is_configuring) {
1116 return {}; 1146 return {};
1117 } 1147 }
@@ -1119,6 +1149,7 @@ HomeButtonState EmulatedController::GetHomeButtons() const {
1119} 1149}
1120 1150
1121CaptureButtonState EmulatedController::GetCaptureButtons() const { 1151CaptureButtonState EmulatedController::GetCaptureButtons() const {
1152 std::scoped_lock lock{mutex};
1122 if (is_configuring) { 1153 if (is_configuring) {
1123 return {}; 1154 return {};
1124 } 1155 }
@@ -1126,6 +1157,7 @@ CaptureButtonState EmulatedController::GetCaptureButtons() const {
1126} 1157}
1127 1158
1128NpadButtonState EmulatedController::GetNpadButtons() const { 1159NpadButtonState EmulatedController::GetNpadButtons() const {
1160 std::scoped_lock lock{mutex};
1129 if (is_configuring) { 1161 if (is_configuring) {
1130 return {}; 1162 return {};
1131 } 1163 }
@@ -1133,6 +1165,7 @@ NpadButtonState EmulatedController::GetNpadButtons() const {
1133} 1165}
1134 1166
1135DebugPadButton EmulatedController::GetDebugPadButtons() const { 1167DebugPadButton EmulatedController::GetDebugPadButtons() const {
1168 std::scoped_lock lock{mutex};
1136 if (is_configuring) { 1169 if (is_configuring) {
1137 return {}; 1170 return {};
1138 } 1171 }
@@ -1140,20 +1173,27 @@ DebugPadButton EmulatedController::GetDebugPadButtons() const {
1140} 1173}
1141 1174
1142AnalogSticks EmulatedController::GetSticks() const { 1175AnalogSticks EmulatedController::GetSticks() const {
1176 std::unique_lock lock{mutex};
1177
1143 if (is_configuring) { 1178 if (is_configuring) {
1144 return {}; 1179 return {};
1145 } 1180 }
1181
1146 // Some drivers like stick from buttons need constant refreshing 1182 // Some drivers like stick from buttons need constant refreshing
1147 for (auto& device : stick_devices) { 1183 for (auto& device : stick_devices) {
1148 if (!device) { 1184 if (!device) {
1149 continue; 1185 continue;
1150 } 1186 }
1187 lock.unlock();
1151 device->SoftUpdate(); 1188 device->SoftUpdate();
1189 lock.lock();
1152 } 1190 }
1191
1153 return controller.analog_stick_state; 1192 return controller.analog_stick_state;
1154} 1193}
1155 1194
1156NpadGcTriggerState EmulatedController::GetTriggers() const { 1195NpadGcTriggerState EmulatedController::GetTriggers() const {
1196 std::scoped_lock lock{mutex};
1157 if (is_configuring) { 1197 if (is_configuring) {
1158 return {}; 1198 return {};
1159 } 1199 }
@@ -1161,26 +1201,35 @@ NpadGcTriggerState EmulatedController::GetTriggers() const {
1161} 1201}
1162 1202
1163MotionState EmulatedController::GetMotions() const { 1203MotionState EmulatedController::GetMotions() const {
1204 std::unique_lock lock{mutex};
1205
1206 // Some drivers like mouse motion need constant refreshing
1164 if (force_update_motion) { 1207 if (force_update_motion) {
1165 for (auto& device : motion_devices) { 1208 for (auto& device : motion_devices) {
1166 if (!device) { 1209 if (!device) {
1167 continue; 1210 continue;
1168 } 1211 }
1212 lock.unlock();
1169 device->ForceUpdate(); 1213 device->ForceUpdate();
1214 lock.lock();
1170 } 1215 }
1171 } 1216 }
1217
1172 return controller.motion_state; 1218 return controller.motion_state;
1173} 1219}
1174 1220
1175ControllerColors EmulatedController::GetColors() const { 1221ControllerColors EmulatedController::GetColors() const {
1222 std::scoped_lock lock{mutex};
1176 return controller.colors_state; 1223 return controller.colors_state;
1177} 1224}
1178 1225
1179BatteryLevelState EmulatedController::GetBattery() const { 1226BatteryLevelState EmulatedController::GetBattery() const {
1227 std::scoped_lock lock{mutex};
1180 return controller.battery_state; 1228 return controller.battery_state;
1181} 1229}
1182 1230
1183void EmulatedController::TriggerOnChange(ControllerTriggerType type, bool is_npad_service_update) { 1231void EmulatedController::TriggerOnChange(ControllerTriggerType type, bool is_npad_service_update) {
1232 std::scoped_lock lock{callback_mutex};
1184 for (const auto& poller_pair : callback_list) { 1233 for (const auto& poller_pair : callback_list) {
1185 const ControllerUpdateCallback& poller = poller_pair.second; 1234 const ControllerUpdateCallback& poller = poller_pair.second;
1186 if (!is_npad_service_update && poller.is_npad_service) { 1235 if (!is_npad_service_update && poller.is_npad_service) {
@@ -1193,13 +1242,13 @@ void EmulatedController::TriggerOnChange(ControllerTriggerType type, bool is_npa
1193} 1242}
1194 1243
1195int EmulatedController::SetCallback(ControllerUpdateCallback update_callback) { 1244int EmulatedController::SetCallback(ControllerUpdateCallback update_callback) {
1196 std::lock_guard lock{mutex}; 1245 std::scoped_lock lock{callback_mutex};
1197 callback_list.insert_or_assign(last_callback_key, std::move(update_callback)); 1246 callback_list.insert_or_assign(last_callback_key, std::move(update_callback));
1198 return last_callback_key++; 1247 return last_callback_key++;
1199} 1248}
1200 1249
1201void EmulatedController::DeleteCallback(int key) { 1250void EmulatedController::DeleteCallback(int key) {
1202 std::lock_guard lock{mutex}; 1251 std::scoped_lock lock{callback_mutex};
1203 const auto& iterator = callback_list.find(key); 1252 const auto& iterator = callback_list.find(key);
1204 if (iterator == callback_list.end()) { 1253 if (iterator == callback_list.end()) {
1205 LOG_ERROR(Input, "Tried to delete non-existent callback {}", key); 1254 LOG_ERROR(Input, "Tried to delete non-existent callback {}", key);
diff --git a/src/core/hid/emulated_controller.h b/src/core/hid/emulated_controller.h
index aa52f9572..1e224685d 100644
--- a/src/core/hid/emulated_controller.h
+++ b/src/core/hid/emulated_controller.h
@@ -400,7 +400,7 @@ private:
400 */ 400 */
401 void TriggerOnChange(ControllerTriggerType type, bool is_service_update); 401 void TriggerOnChange(ControllerTriggerType type, bool is_service_update);
402 402
403 NpadIdType npad_id_type; 403 const NpadIdType npad_id_type;
404 NpadStyleIndex npad_type{NpadStyleIndex::None}; 404 NpadStyleIndex npad_type{NpadStyleIndex::None};
405 NpadStyleTag supported_style_tag{NpadStyleSet::All}; 405 NpadStyleTag supported_style_tag{NpadStyleSet::All};
406 bool is_connected{false}; 406 bool is_connected{false};
@@ -434,6 +434,7 @@ private:
434 StickDevices tas_stick_devices; 434 StickDevices tas_stick_devices;
435 435
436 mutable std::mutex mutex; 436 mutable std::mutex mutex;
437 mutable std::mutex callback_mutex;
437 std::unordered_map<int, ControllerUpdateCallback> callback_list; 438 std::unordered_map<int, ControllerUpdateCallback> callback_list;
438 int last_callback_key = 0; 439 int last_callback_key = 0;
439 440
diff --git a/src/core/hid/emulated_devices.cpp b/src/core/hid/emulated_devices.cpp
index 708480f2d..2f84d2b52 100644
--- a/src/core/hid/emulated_devices.cpp
+++ b/src/core/hid/emulated_devices.cpp
@@ -15,6 +15,7 @@ EmulatedDevices::EmulatedDevices() = default;
15EmulatedDevices::~EmulatedDevices() = default; 15EmulatedDevices::~EmulatedDevices() = default;
16 16
17void EmulatedDevices::ReloadFromSettings() { 17void EmulatedDevices::ReloadFromSettings() {
18 ring_params = Common::ParamPackage(Settings::values.ringcon_analogs);
18 ReloadInput(); 19 ReloadInput();
19} 20}
20 21
@@ -66,6 +67,8 @@ void EmulatedDevices::ReloadInput() {
66 key_index++; 67 key_index++;
67 } 68 }
68 69
70 ring_analog_device = Common::Input::CreateDevice<Common::Input::InputDevice>(ring_params);
71
69 for (std::size_t index = 0; index < mouse_button_devices.size(); ++index) { 72 for (std::size_t index = 0; index < mouse_button_devices.size(); ++index) {
70 if (!mouse_button_devices[index]) { 73 if (!mouse_button_devices[index]) {
71 continue; 74 continue;
@@ -120,6 +123,13 @@ void EmulatedDevices::ReloadInput() {
120 }, 123 },
121 }); 124 });
122 } 125 }
126
127 if (ring_analog_device) {
128 ring_analog_device->SetCallback({
129 .on_change =
130 [this](const Common::Input::CallbackStatus& callback) { SetRingAnalog(callback); },
131 });
132 }
123} 133}
124 134
125void EmulatedDevices::UnloadInput() { 135void EmulatedDevices::UnloadInput() {
@@ -155,6 +165,7 @@ void EmulatedDevices::SaveCurrentConfig() {
155 if (!is_configuring) { 165 if (!is_configuring) {
156 return; 166 return;
157 } 167 }
168 Settings::values.ringcon_analogs = ring_params.Serialize();
158} 169}
159 170
160void EmulatedDevices::RestoreConfig() { 171void EmulatedDevices::RestoreConfig() {
@@ -164,12 +175,21 @@ void EmulatedDevices::RestoreConfig() {
164 ReloadFromSettings(); 175 ReloadFromSettings();
165} 176}
166 177
178Common::ParamPackage EmulatedDevices::GetRingParam() const {
179 return ring_params;
180}
181
182void EmulatedDevices::SetRingParam(Common::ParamPackage param) {
183 ring_params = std::move(param);
184 ReloadInput();
185}
186
167void EmulatedDevices::SetKeyboardButton(const Common::Input::CallbackStatus& callback, 187void EmulatedDevices::SetKeyboardButton(const Common::Input::CallbackStatus& callback,
168 std::size_t index) { 188 std::size_t index) {
169 if (index >= device_status.keyboard_values.size()) { 189 if (index >= device_status.keyboard_values.size()) {
170 return; 190 return;
171 } 191 }
172 std::lock_guard lock{mutex}; 192 std::unique_lock lock{mutex};
173 bool value_changed = false; 193 bool value_changed = false;
174 const auto new_status = TransformToButton(callback); 194 const auto new_status = TransformToButton(callback);
175 auto& current_status = device_status.keyboard_values[index]; 195 auto& current_status = device_status.keyboard_values[index];
@@ -201,6 +221,7 @@ void EmulatedDevices::SetKeyboardButton(const Common::Input::CallbackStatus& cal
201 } 221 }
202 222
203 if (is_configuring) { 223 if (is_configuring) {
224 lock.unlock();
204 TriggerOnChange(DeviceTriggerType::Keyboard); 225 TriggerOnChange(DeviceTriggerType::Keyboard);
205 return; 226 return;
206 } 227 }
@@ -208,6 +229,7 @@ void EmulatedDevices::SetKeyboardButton(const Common::Input::CallbackStatus& cal
208 // Index should be converted from NativeKeyboard to KeyboardKeyIndex 229 // Index should be converted from NativeKeyboard to KeyboardKeyIndex
209 UpdateKey(index, current_status.value); 230 UpdateKey(index, current_status.value);
210 231
232 lock.unlock();
211 TriggerOnChange(DeviceTriggerType::Keyboard); 233 TriggerOnChange(DeviceTriggerType::Keyboard);
212} 234}
213 235
@@ -227,7 +249,7 @@ void EmulatedDevices::SetKeyboardModifier(const Common::Input::CallbackStatus& c
227 if (index >= device_status.keyboard_moddifier_values.size()) { 249 if (index >= device_status.keyboard_moddifier_values.size()) {
228 return; 250 return;
229 } 251 }
230 std::lock_guard lock{mutex}; 252 std::unique_lock lock{mutex};
231 bool value_changed = false; 253 bool value_changed = false;
232 const auto new_status = TransformToButton(callback); 254 const auto new_status = TransformToButton(callback);
233 auto& current_status = device_status.keyboard_moddifier_values[index]; 255 auto& current_status = device_status.keyboard_moddifier_values[index];
@@ -259,6 +281,7 @@ void EmulatedDevices::SetKeyboardModifier(const Common::Input::CallbackStatus& c
259 } 281 }
260 282
261 if (is_configuring) { 283 if (is_configuring) {
284 lock.unlock();
262 TriggerOnChange(DeviceTriggerType::KeyboardModdifier); 285 TriggerOnChange(DeviceTriggerType::KeyboardModdifier);
263 return; 286 return;
264 } 287 }
@@ -289,6 +312,7 @@ void EmulatedDevices::SetKeyboardModifier(const Common::Input::CallbackStatus& c
289 break; 312 break;
290 } 313 }
291 314
315 lock.unlock();
292 TriggerOnChange(DeviceTriggerType::KeyboardModdifier); 316 TriggerOnChange(DeviceTriggerType::KeyboardModdifier);
293} 317}
294 318
@@ -297,7 +321,7 @@ void EmulatedDevices::SetMouseButton(const Common::Input::CallbackStatus& callba
297 if (index >= device_status.mouse_button_values.size()) { 321 if (index >= device_status.mouse_button_values.size()) {
298 return; 322 return;
299 } 323 }
300 std::lock_guard lock{mutex}; 324 std::unique_lock lock{mutex};
301 bool value_changed = false; 325 bool value_changed = false;
302 const auto new_status = TransformToButton(callback); 326 const auto new_status = TransformToButton(callback);
303 auto& current_status = device_status.mouse_button_values[index]; 327 auto& current_status = device_status.mouse_button_values[index];
@@ -329,6 +353,7 @@ void EmulatedDevices::SetMouseButton(const Common::Input::CallbackStatus& callba
329 } 353 }
330 354
331 if (is_configuring) { 355 if (is_configuring) {
356 lock.unlock();
332 TriggerOnChange(DeviceTriggerType::Mouse); 357 TriggerOnChange(DeviceTriggerType::Mouse);
333 return; 358 return;
334 } 359 }
@@ -351,6 +376,7 @@ void EmulatedDevices::SetMouseButton(const Common::Input::CallbackStatus& callba
351 break; 376 break;
352 } 377 }
353 378
379 lock.unlock();
354 TriggerOnChange(DeviceTriggerType::Mouse); 380 TriggerOnChange(DeviceTriggerType::Mouse);
355} 381}
356 382
@@ -359,13 +385,14 @@ void EmulatedDevices::SetMouseAnalog(const Common::Input::CallbackStatus& callba
359 if (index >= device_status.mouse_analog_values.size()) { 385 if (index >= device_status.mouse_analog_values.size()) {
360 return; 386 return;
361 } 387 }
362 std::lock_guard lock{mutex}; 388 std::unique_lock lock{mutex};
363 const auto analog_value = TransformToAnalog(callback); 389 const auto analog_value = TransformToAnalog(callback);
364 390
365 device_status.mouse_analog_values[index] = analog_value; 391 device_status.mouse_analog_values[index] = analog_value;
366 392
367 if (is_configuring) { 393 if (is_configuring) {
368 device_status.mouse_position_state = {}; 394 device_status.mouse_position_state = {};
395 lock.unlock();
369 TriggerOnChange(DeviceTriggerType::Mouse); 396 TriggerOnChange(DeviceTriggerType::Mouse);
370 return; 397 return;
371 } 398 }
@@ -379,17 +406,19 @@ void EmulatedDevices::SetMouseAnalog(const Common::Input::CallbackStatus& callba
379 break; 406 break;
380 } 407 }
381 408
409 lock.unlock();
382 TriggerOnChange(DeviceTriggerType::Mouse); 410 TriggerOnChange(DeviceTriggerType::Mouse);
383} 411}
384 412
385void EmulatedDevices::SetMouseStick(const Common::Input::CallbackStatus& callback) { 413void EmulatedDevices::SetMouseStick(const Common::Input::CallbackStatus& callback) {
386 std::lock_guard lock{mutex}; 414 std::unique_lock lock{mutex};
387 const auto touch_value = TransformToTouch(callback); 415 const auto touch_value = TransformToTouch(callback);
388 416
389 device_status.mouse_stick_value = touch_value; 417 device_status.mouse_stick_value = touch_value;
390 418
391 if (is_configuring) { 419 if (is_configuring) {
392 device_status.mouse_position_state = {}; 420 device_status.mouse_position_state = {};
421 lock.unlock();
393 TriggerOnChange(DeviceTriggerType::Mouse); 422 TriggerOnChange(DeviceTriggerType::Mouse);
394 return; 423 return;
395 } 424 }
@@ -397,42 +426,77 @@ void EmulatedDevices::SetMouseStick(const Common::Input::CallbackStatus& callbac
397 device_status.mouse_position_state.x = touch_value.x.value; 426 device_status.mouse_position_state.x = touch_value.x.value;
398 device_status.mouse_position_state.y = touch_value.y.value; 427 device_status.mouse_position_state.y = touch_value.y.value;
399 428
429 lock.unlock();
400 TriggerOnChange(DeviceTriggerType::Mouse); 430 TriggerOnChange(DeviceTriggerType::Mouse);
401} 431}
402 432
433void EmulatedDevices::SetRingAnalog(const Common::Input::CallbackStatus& callback) {
434 std::lock_guard lock{mutex};
435 const auto force_value = TransformToStick(callback);
436
437 device_status.ring_analog_value = force_value.x;
438
439 if (is_configuring) {
440 device_status.ring_analog_value = {};
441 TriggerOnChange(DeviceTriggerType::RingController);
442 return;
443 }
444
445 device_status.ring_analog_state.force = force_value.x.value;
446
447 TriggerOnChange(DeviceTriggerType::RingController);
448}
449
403KeyboardValues EmulatedDevices::GetKeyboardValues() const { 450KeyboardValues EmulatedDevices::GetKeyboardValues() const {
451 std::scoped_lock lock{mutex};
404 return device_status.keyboard_values; 452 return device_status.keyboard_values;
405} 453}
406 454
407KeyboardModifierValues EmulatedDevices::GetKeyboardModdifierValues() const { 455KeyboardModifierValues EmulatedDevices::GetKeyboardModdifierValues() const {
456 std::scoped_lock lock{mutex};
408 return device_status.keyboard_moddifier_values; 457 return device_status.keyboard_moddifier_values;
409} 458}
410 459
411MouseButtonValues EmulatedDevices::GetMouseButtonsValues() const { 460MouseButtonValues EmulatedDevices::GetMouseButtonsValues() const {
461 std::scoped_lock lock{mutex};
412 return device_status.mouse_button_values; 462 return device_status.mouse_button_values;
413} 463}
414 464
465RingAnalogValue EmulatedDevices::GetRingSensorValues() const {
466 return device_status.ring_analog_value;
467}
468
415KeyboardKey EmulatedDevices::GetKeyboard() const { 469KeyboardKey EmulatedDevices::GetKeyboard() const {
470 std::scoped_lock lock{mutex};
416 return device_status.keyboard_state; 471 return device_status.keyboard_state;
417} 472}
418 473
419KeyboardModifier EmulatedDevices::GetKeyboardModifier() const { 474KeyboardModifier EmulatedDevices::GetKeyboardModifier() const {
475 std::scoped_lock lock{mutex};
420 return device_status.keyboard_moddifier_state; 476 return device_status.keyboard_moddifier_state;
421} 477}
422 478
423MouseButton EmulatedDevices::GetMouseButtons() const { 479MouseButton EmulatedDevices::GetMouseButtons() const {
480 std::scoped_lock lock{mutex};
424 return device_status.mouse_button_state; 481 return device_status.mouse_button_state;
425} 482}
426 483
427MousePosition EmulatedDevices::GetMousePosition() const { 484MousePosition EmulatedDevices::GetMousePosition() const {
485 std::scoped_lock lock{mutex};
428 return device_status.mouse_position_state; 486 return device_status.mouse_position_state;
429} 487}
430 488
431AnalogStickState EmulatedDevices::GetMouseWheel() const { 489AnalogStickState EmulatedDevices::GetMouseWheel() const {
490 std::scoped_lock lock{mutex};
432 return device_status.mouse_wheel_state; 491 return device_status.mouse_wheel_state;
433} 492}
434 493
494RingSensorForce EmulatedDevices::GetRingSensorForce() const {
495 return device_status.ring_analog_state;
496}
497
435void EmulatedDevices::TriggerOnChange(DeviceTriggerType type) { 498void EmulatedDevices::TriggerOnChange(DeviceTriggerType type) {
499 std::scoped_lock lock{callback_mutex};
436 for (const auto& poller_pair : callback_list) { 500 for (const auto& poller_pair : callback_list) {
437 const InterfaceUpdateCallback& poller = poller_pair.second; 501 const InterfaceUpdateCallback& poller = poller_pair.second;
438 if (poller.on_change) { 502 if (poller.on_change) {
@@ -442,13 +506,13 @@ void EmulatedDevices::TriggerOnChange(DeviceTriggerType type) {
442} 506}
443 507
444int EmulatedDevices::SetCallback(InterfaceUpdateCallback update_callback) { 508int EmulatedDevices::SetCallback(InterfaceUpdateCallback update_callback) {
445 std::lock_guard lock{mutex}; 509 std::scoped_lock lock{callback_mutex};
446 callback_list.insert_or_assign(last_callback_key, std::move(update_callback)); 510 callback_list.insert_or_assign(last_callback_key, std::move(update_callback));
447 return last_callback_key++; 511 return last_callback_key++;
448} 512}
449 513
450void EmulatedDevices::DeleteCallback(int key) { 514void EmulatedDevices::DeleteCallback(int key) {
451 std::lock_guard lock{mutex}; 515 std::scoped_lock lock{callback_mutex};
452 const auto& iterator = callback_list.find(key); 516 const auto& iterator = callback_list.find(key);
453 if (iterator == callback_list.end()) { 517 if (iterator == callback_list.end()) {
454 LOG_ERROR(Input, "Tried to delete non-existent callback {}", key); 518 LOG_ERROR(Input, "Tried to delete non-existent callback {}", key);
diff --git a/src/core/hid/emulated_devices.h b/src/core/hid/emulated_devices.h
index 790d3b411..fb6451e7a 100644
--- a/src/core/hid/emulated_devices.h
+++ b/src/core/hid/emulated_devices.h
@@ -26,9 +26,11 @@ using MouseButtonDevices = std::array<std::unique_ptr<Common::Input::InputDevice
26using MouseAnalogDevices = std::array<std::unique_ptr<Common::Input::InputDevice>, 26using MouseAnalogDevices = std::array<std::unique_ptr<Common::Input::InputDevice>,
27 Settings::NativeMouseWheel::NumMouseWheels>; 27 Settings::NativeMouseWheel::NumMouseWheels>;
28using MouseStickDevice = std::unique_ptr<Common::Input::InputDevice>; 28using MouseStickDevice = std::unique_ptr<Common::Input::InputDevice>;
29using RingAnalogDevice = std::unique_ptr<Common::Input::InputDevice>;
29 30
30using MouseButtonParams = 31using MouseButtonParams =
31 std::array<Common::ParamPackage, Settings::NativeMouseButton::NumMouseButtons>; 32 std::array<Common::ParamPackage, Settings::NativeMouseButton::NumMouseButtons>;
33using RingAnalogParams = Common::ParamPackage;
32 34
33using KeyboardValues = 35using KeyboardValues =
34 std::array<Common::Input::ButtonStatus, Settings::NativeKeyboard::NumKeyboardKeys>; 36 std::array<Common::Input::ButtonStatus, Settings::NativeKeyboard::NumKeyboardKeys>;
@@ -39,12 +41,17 @@ using MouseButtonValues =
39using MouseAnalogValues = 41using MouseAnalogValues =
40 std::array<Common::Input::AnalogStatus, Settings::NativeMouseWheel::NumMouseWheels>; 42 std::array<Common::Input::AnalogStatus, Settings::NativeMouseWheel::NumMouseWheels>;
41using MouseStickValue = Common::Input::TouchStatus; 43using MouseStickValue = Common::Input::TouchStatus;
44using RingAnalogValue = Common::Input::AnalogStatus;
42 45
43struct MousePosition { 46struct MousePosition {
44 f32 x; 47 f32 x;
45 f32 y; 48 f32 y;
46}; 49};
47 50
51struct RingSensorForce {
52 f32 force;
53};
54
48struct DeviceStatus { 55struct DeviceStatus {
49 // Data from input_common 56 // Data from input_common
50 KeyboardValues keyboard_values{}; 57 KeyboardValues keyboard_values{};
@@ -52,6 +59,7 @@ struct DeviceStatus {
52 MouseButtonValues mouse_button_values{}; 59 MouseButtonValues mouse_button_values{};
53 MouseAnalogValues mouse_analog_values{}; 60 MouseAnalogValues mouse_analog_values{};
54 MouseStickValue mouse_stick_value{}; 61 MouseStickValue mouse_stick_value{};
62 RingAnalogValue ring_analog_value{};
55 63
56 // Data for HID serices 64 // Data for HID serices
57 KeyboardKey keyboard_state{}; 65 KeyboardKey keyboard_state{};
@@ -59,12 +67,14 @@ struct DeviceStatus {
59 MouseButton mouse_button_state{}; 67 MouseButton mouse_button_state{};
60 MousePosition mouse_position_state{}; 68 MousePosition mouse_position_state{};
61 AnalogStickState mouse_wheel_state{}; 69 AnalogStickState mouse_wheel_state{};
70 RingSensorForce ring_analog_state{};
62}; 71};
63 72
64enum class DeviceTriggerType { 73enum class DeviceTriggerType {
65 Keyboard, 74 Keyboard,
66 KeyboardModdifier, 75 KeyboardModdifier,
67 Mouse, 76 Mouse,
77 RingController,
68}; 78};
69 79
70struct InterfaceUpdateCallback { 80struct InterfaceUpdateCallback {
@@ -110,6 +120,15 @@ public:
110 /// Reverts any mapped changes made that weren't saved 120 /// Reverts any mapped changes made that weren't saved
111 void RestoreConfig(); 121 void RestoreConfig();
112 122
123 // Returns the current mapped ring device
124 Common::ParamPackage GetRingParam() const;
125
126 /**
127 * Updates the current mapped ring device
128 * @param param ParamPackage with ring sensor data to be mapped
129 */
130 void SetRingParam(Common::ParamPackage param);
131
113 /// Returns the latest status of button input from the keyboard with parameters 132 /// Returns the latest status of button input from the keyboard with parameters
114 KeyboardValues GetKeyboardValues() const; 133 KeyboardValues GetKeyboardValues() const;
115 134
@@ -119,6 +138,9 @@ public:
119 /// Returns the latest status of button input from the mouse with parameters 138 /// Returns the latest status of button input from the mouse with parameters
120 MouseButtonValues GetMouseButtonsValues() const; 139 MouseButtonValues GetMouseButtonsValues() const;
121 140
141 /// Returns the latest status of analog input from the ring sensor with parameters
142 RingAnalogValue GetRingSensorValues() const;
143
122 /// Returns the latest status of button input from the keyboard 144 /// Returns the latest status of button input from the keyboard
123 KeyboardKey GetKeyboard() const; 145 KeyboardKey GetKeyboard() const;
124 146
@@ -134,6 +156,9 @@ public:
134 /// Returns the latest mouse wheel change 156 /// Returns the latest mouse wheel change
135 AnalogStickState GetMouseWheel() const; 157 AnalogStickState GetMouseWheel() const;
136 158
159 /// Returns the latest ringcon force sensor value
160 RingSensorForce GetRingSensorForce() const;
161
137 /** 162 /**
138 * Adds a callback to the list of events 163 * Adds a callback to the list of events
139 * @param update_callback InterfaceUpdateCallback that will be triggered 164 * @param update_callback InterfaceUpdateCallback that will be triggered
@@ -186,6 +211,12 @@ private:
186 void SetMouseStick(const Common::Input::CallbackStatus& callback); 211 void SetMouseStick(const Common::Input::CallbackStatus& callback);
187 212
188 /** 213 /**
214 * Updates the ring analog sensor status of the ring controller
215 * @param callback A CallbackStatus containing the force status
216 */
217 void SetRingAnalog(const Common::Input::CallbackStatus& callback);
218
219 /**
189 * Triggers a callback that something has changed on the device status 220 * Triggers a callback that something has changed on the device status
190 * @param type Input type of the event to trigger 221 * @param type Input type of the event to trigger
191 */ 222 */
@@ -193,13 +224,17 @@ private:
193 224
194 bool is_configuring{false}; 225 bool is_configuring{false};
195 226
227 RingAnalogParams ring_params;
228
196 KeyboardDevices keyboard_devices; 229 KeyboardDevices keyboard_devices;
197 KeyboardModifierDevices keyboard_modifier_devices; 230 KeyboardModifierDevices keyboard_modifier_devices;
198 MouseButtonDevices mouse_button_devices; 231 MouseButtonDevices mouse_button_devices;
199 MouseAnalogDevices mouse_analog_devices; 232 MouseAnalogDevices mouse_analog_devices;
200 MouseStickDevice mouse_stick_device; 233 MouseStickDevice mouse_stick_device;
234 RingAnalogDevice ring_analog_device;
201 235
202 mutable std::mutex mutex; 236 mutable std::mutex mutex;
237 mutable std::mutex callback_mutex;
203 std::unordered_map<int, InterfaceUpdateCallback> callback_list; 238 std::unordered_map<int, InterfaceUpdateCallback> callback_list;
204 int last_callback_key = 0; 239 int last_callback_key = 0;
205 240
diff --git a/src/core/hid/hid_types.h b/src/core/hid/hid_types.h
index 778b328b9..422a9af33 100644
--- a/src/core/hid/hid_types.h
+++ b/src/core/hid/hid_types.h
@@ -491,9 +491,10 @@ struct SixAxisSensorHandle {
491}; 491};
492static_assert(sizeof(SixAxisSensorHandle) == 4, "SixAxisSensorHandle is an invalid size"); 492static_assert(sizeof(SixAxisSensorHandle) == 4, "SixAxisSensorHandle is an invalid size");
493 493
494// These parameters seem related to how much gyro/accelerometer is used
494struct SixAxisSensorFusionParameters { 495struct SixAxisSensorFusionParameters {
495 f32 parameter1; 496 f32 parameter1{0.03f}; // Range 0.0 to 1.0, default 0.03
496 f32 parameter2; 497 f32 parameter2{0.4f}; // Default 0.4
497}; 498};
498static_assert(sizeof(SixAxisSensorFusionParameters) == 8, 499static_assert(sizeof(SixAxisSensorFusionParameters) == 8,
499 "SixAxisSensorFusionParameters is an invalid size"); 500 "SixAxisSensorFusionParameters is an invalid size");
diff --git a/src/core/hle/kernel/board/nintendo/nx/k_system_control.cpp b/src/core/hle/kernel/board/nintendo/nx/k_system_control.cpp
index 8027bec00..7765e7848 100644
--- a/src/core/hle/kernel/board/nintendo/nx/k_system_control.cpp
+++ b/src/core/hle/kernel/board/nintendo/nx/k_system_control.cpp
@@ -148,9 +148,9 @@ u64 GenerateUniformRange(u64 min, u64 max, F f) {
148} // Anonymous namespace 148} // Anonymous namespace
149 149
150u64 KSystemControl::GenerateRandomU64() { 150u64 KSystemControl::GenerateRandomU64() {
151 static std::random_device device; 151 std::random_device device;
152 static std::mt19937 gen(device()); 152 std::mt19937 gen(device());
153 static std::uniform_int_distribution<u64> distribution(1, std::numeric_limits<u64>::max()); 153 std::uniform_int_distribution<u64> distribution(1, std::numeric_limits<u64>::max());
154 return distribution(gen); 154 return distribution(gen);
155} 155}
156 156
diff --git a/src/core/hle/kernel/global_scheduler_context.h b/src/core/hle/kernel/global_scheduler_context.h
index 6f44b534f..47425a3a1 100644
--- a/src/core/hle/kernel/global_scheduler_context.h
+++ b/src/core/hle/kernel/global_scheduler_context.h
@@ -8,7 +8,6 @@
8#include <vector> 8#include <vector>
9 9
10#include "common/common_types.h" 10#include "common/common_types.h"
11#include "common/spin_lock.h"
12#include "core/hardware_properties.h" 11#include "core/hardware_properties.h"
13#include "core/hle/kernel/k_priority_queue.h" 12#include "core/hle/kernel/k_priority_queue.h"
14#include "core/hle/kernel/k_scheduler_lock.h" 13#include "core/hle/kernel/k_scheduler_lock.h"
@@ -80,7 +79,7 @@ private:
80 79
81 /// Lists all thread ids that aren't deleted/etc. 80 /// Lists all thread ids that aren't deleted/etc.
82 std::vector<KThread*> thread_list; 81 std::vector<KThread*> thread_list;
83 Common::SpinLock global_list_guard{}; 82 std::mutex global_list_guard;
84}; 83};
85 84
86} // namespace Kernel 85} // namespace Kernel
diff --git a/src/core/hle/kernel/hle_ipc.cpp b/src/core/hle/kernel/hle_ipc.cpp
index 42d1b0e31..5828ac923 100644
--- a/src/core/hle/kernel/hle_ipc.cpp
+++ b/src/core/hle/kernel/hle_ipc.cpp
@@ -24,8 +24,15 @@
24 24
25namespace Kernel { 25namespace Kernel {
26 26
27SessionRequestHandler::SessionRequestHandler(KernelCore& kernel_, const char* service_name_) 27SessionRequestHandler::SessionRequestHandler(KernelCore& kernel_, const char* service_name_,
28 : kernel{kernel_}, service_thread{kernel.CreateServiceThread(service_name_)} {} 28 ServiceThreadType thread_type)
29 : kernel{kernel_} {
30 if (thread_type == ServiceThreadType::CreateNew) {
31 service_thread = kernel.CreateServiceThread(service_name_);
32 } else {
33 service_thread = kernel.GetDefaultServiceThread();
34 }
35}
29 36
30SessionRequestHandler::~SessionRequestHandler() { 37SessionRequestHandler::~SessionRequestHandler() {
31 kernel.ReleaseServiceThread(service_thread); 38 kernel.ReleaseServiceThread(service_thread);
@@ -44,7 +51,7 @@ bool SessionRequestManager::HasSessionRequestHandler(const HLERequestContext& co
44 LOG_CRITICAL(IPC, "object_id {} is too big!", object_id); 51 LOG_CRITICAL(IPC, "object_id {} is too big!", object_id);
45 return false; 52 return false;
46 } 53 }
47 return DomainHandler(object_id - 1).lock() != nullptr; 54 return !DomainHandler(object_id - 1).expired();
48 } else { 55 } else {
49 return session_handler != nullptr; 56 return session_handler != nullptr;
50 } 57 }
@@ -52,6 +59,9 @@ bool SessionRequestManager::HasSessionRequestHandler(const HLERequestContext& co
52 59
53void SessionRequestHandler::ClientConnected(KServerSession* session) { 60void SessionRequestHandler::ClientConnected(KServerSession* session) {
54 session->ClientConnected(shared_from_this()); 61 session->ClientConnected(shared_from_this());
62
63 // Ensure our server session is tracked globally.
64 kernel.RegisterServerObject(session);
55} 65}
56 66
57void SessionRequestHandler::ClientDisconnected(KServerSession* session) { 67void SessionRequestHandler::ClientDisconnected(KServerSession* session) {
diff --git a/src/core/hle/kernel/hle_ipc.h b/src/core/hle/kernel/hle_ipc.h
index 670cc741c..640146137 100644
--- a/src/core/hle/kernel/hle_ipc.h
+++ b/src/core/hle/kernel/hle_ipc.h
@@ -33,6 +33,11 @@ namespace Service {
33class ServiceFrameworkBase; 33class ServiceFrameworkBase;
34} 34}
35 35
36enum class ServiceThreadType {
37 Default,
38 CreateNew,
39};
40
36namespace Kernel { 41namespace Kernel {
37 42
38class Domain; 43class Domain;
@@ -57,7 +62,8 @@ enum class ThreadWakeupReason;
57 */ 62 */
58class SessionRequestHandler : public std::enable_shared_from_this<SessionRequestHandler> { 63class SessionRequestHandler : public std::enable_shared_from_this<SessionRequestHandler> {
59public: 64public:
60 SessionRequestHandler(KernelCore& kernel, const char* service_name_); 65 SessionRequestHandler(KernelCore& kernel_, const char* service_name_,
66 ServiceThreadType thread_type);
61 virtual ~SessionRequestHandler(); 67 virtual ~SessionRequestHandler();
62 68
63 /** 69 /**
diff --git a/src/core/hle/kernel/k_auto_object.h b/src/core/hle/kernel/k_auto_object.h
index 05779f2d5..423e8d8f5 100644
--- a/src/core/hle/kernel/k_auto_object.h
+++ b/src/core/hle/kernel/k_auto_object.h
@@ -89,9 +89,7 @@ public:
89 explicit KAutoObject(KernelCore& kernel_) : kernel(kernel_) { 89 explicit KAutoObject(KernelCore& kernel_) : kernel(kernel_) {
90 RegisterWithKernel(); 90 RegisterWithKernel();
91 } 91 }
92 virtual ~KAutoObject() { 92 virtual ~KAutoObject() = default;
93 UnregisterWithKernel();
94 }
95 93
96 static KAutoObject* Create(KAutoObject* ptr); 94 static KAutoObject* Create(KAutoObject* ptr);
97 95
@@ -163,11 +161,12 @@ public:
163 do { 161 do {
164 ASSERT(cur_ref_count > 0); 162 ASSERT(cur_ref_count > 0);
165 } while (!m_ref_count.compare_exchange_weak(cur_ref_count, cur_ref_count - 1, 163 } while (!m_ref_count.compare_exchange_weak(cur_ref_count, cur_ref_count - 1,
166 std::memory_order_relaxed)); 164 std::memory_order_acq_rel));
167 165
168 // If ref count hits zero, destroy the object. 166 // If ref count hits zero, destroy the object.
169 if (cur_ref_count - 1 == 0) { 167 if (cur_ref_count - 1 == 0) {
170 this->Destroy(); 168 this->Destroy();
169 this->UnregisterWithKernel();
171 } 170 }
172 } 171 }
173 172
diff --git a/src/core/hle/kernel/k_code_memory.cpp b/src/core/hle/kernel/k_code_memory.cpp
index b365ce7b7..09eaf004c 100644
--- a/src/core/hle/kernel/k_code_memory.cpp
+++ b/src/core/hle/kernel/k_code_memory.cpp
@@ -28,15 +28,21 @@ ResultCode KCodeMemory::Initialize(Core::DeviceMemory& device_memory, VAddr addr
28 auto& page_table = m_owner->PageTable(); 28 auto& page_table = m_owner->PageTable();
29 29
30 // Construct the page group. 30 // Construct the page group.
31 m_page_group = KPageLinkedList(addr, Common::DivideUp(size, PageSize)); 31 m_page_group =
32 KPageLinkedList(page_table.GetPhysicalAddr(addr), Common::DivideUp(size, PageSize));
32 33
33 // Lock the memory. 34 // Lock the memory.
34 R_TRY(page_table.LockForCodeMemory(addr, size)) 35 R_TRY(page_table.LockForCodeMemory(addr, size))
35 36
36 // Clear the memory. 37 // Clear the memory.
37 for (const auto& block : m_page_group.Nodes()) { 38 //
38 std::memset(device_memory.GetPointer(block.GetAddress()), 0xFF, block.GetSize()); 39 // FIXME: this ends up clobbering address ranges outside the scope of the mapping within
39 } 40 // guest memory, and is not specifically required if the guest program is correctly
41 // written, so disable until this is further investigated.
42 //
43 // for (const auto& block : m_page_group.Nodes()) {
44 // std::memset(device_memory.GetPointer(block.GetAddress()), 0xFF, block.GetSize());
45 // }
40 46
41 // Set remaining tracking members. 47 // Set remaining tracking members.
42 m_address = addr; 48 m_address = addr;
diff --git a/src/core/hle/kernel/k_page_linked_list.h b/src/core/hle/kernel/k_page_linked_list.h
index 0e2ae582a..869228322 100644
--- a/src/core/hle/kernel/k_page_linked_list.h
+++ b/src/core/hle/kernel/k_page_linked_list.h
@@ -89,6 +89,10 @@ public:
89 return ResultSuccess; 89 return ResultSuccess;
90 } 90 }
91 91
92 bool Empty() const {
93 return nodes.empty();
94 }
95
92private: 96private:
93 std::list<Node> nodes; 97 std::list<Node> nodes;
94}; 98};
diff --git a/src/core/hle/kernel/k_page_table.cpp b/src/core/hle/kernel/k_page_table.cpp
index 02d93b12e..47ea3c89c 100644
--- a/src/core/hle/kernel/k_page_table.cpp
+++ b/src/core/hle/kernel/k_page_table.cpp
@@ -346,7 +346,8 @@ ResultCode KPageTable::MapCodeMemory(VAddr dst_address, VAddr src_address, std::
346 return ResultSuccess; 346 return ResultSuccess;
347} 347}
348 348
349ResultCode KPageTable::UnmapCodeMemory(VAddr dst_address, VAddr src_address, std::size_t size) { 349ResultCode KPageTable::UnmapCodeMemory(VAddr dst_address, VAddr src_address, std::size_t size,
350 ICacheInvalidationStrategy icache_invalidation_strategy) {
350 // Validate the mapping request. 351 // Validate the mapping request.
351 R_UNLESS(this->CanContain(dst_address, size, KMemoryState::AliasCode), 352 R_UNLESS(this->CanContain(dst_address, size, KMemoryState::AliasCode),
352 ResultInvalidMemoryRegion); 353 ResultInvalidMemoryRegion);
@@ -396,7 +397,11 @@ ResultCode KPageTable::UnmapCodeMemory(VAddr dst_address, VAddr src_address, std
396 bool reprotected_pages = false; 397 bool reprotected_pages = false;
397 SCOPE_EXIT({ 398 SCOPE_EXIT({
398 if (reprotected_pages && any_code_pages) { 399 if (reprotected_pages && any_code_pages) {
399 system.InvalidateCpuInstructionCacheRange(dst_address, size); 400 if (icache_invalidation_strategy == ICacheInvalidationStrategy::InvalidateRange) {
401 system.InvalidateCpuInstructionCacheRange(dst_address, size);
402 } else {
403 system.InvalidateCpuInstructionCaches();
404 }
400 } 405 }
401 }); 406 });
402 407
@@ -486,6 +491,58 @@ VAddr KPageTable::FindFreeArea(VAddr region_start, std::size_t region_num_pages,
486 return address; 491 return address;
487} 492}
488 493
494ResultCode KPageTable::MakePageGroup(KPageLinkedList& pg, VAddr addr, size_t num_pages) {
495 ASSERT(this->IsLockedByCurrentThread());
496
497 const size_t size = num_pages * PageSize;
498
499 // We're making a new group, not adding to an existing one.
500 R_UNLESS(pg.Empty(), ResultInvalidCurrentMemory);
501
502 // Begin traversal.
503 Common::PageTable::TraversalContext context;
504 Common::PageTable::TraversalEntry next_entry;
505 R_UNLESS(page_table_impl.BeginTraversal(next_entry, context, addr), ResultInvalidCurrentMemory);
506
507 // Prepare tracking variables.
508 PAddr cur_addr = next_entry.phys_addr;
509 size_t cur_size = next_entry.block_size - (cur_addr & (next_entry.block_size - 1));
510 size_t tot_size = cur_size;
511
512 // Iterate, adding to group as we go.
513 const auto& memory_layout = system.Kernel().MemoryLayout();
514 while (tot_size < size) {
515 R_UNLESS(page_table_impl.ContinueTraversal(next_entry, context),
516 ResultInvalidCurrentMemory);
517
518 if (next_entry.phys_addr != (cur_addr + cur_size)) {
519 const size_t cur_pages = cur_size / PageSize;
520
521 R_UNLESS(IsHeapPhysicalAddress(memory_layout, cur_addr), ResultInvalidCurrentMemory);
522 R_TRY(pg.AddBlock(cur_addr, cur_pages));
523
524 cur_addr = next_entry.phys_addr;
525 cur_size = next_entry.block_size;
526 } else {
527 cur_size += next_entry.block_size;
528 }
529
530 tot_size += next_entry.block_size;
531 }
532
533 // Ensure we add the right amount for the last block.
534 if (tot_size > size) {
535 cur_size -= (tot_size - size);
536 }
537
538 // Add the last block.
539 const size_t cur_pages = cur_size / PageSize;
540 R_UNLESS(IsHeapPhysicalAddress(memory_layout, cur_addr), ResultInvalidCurrentMemory);
541 R_TRY(pg.AddBlock(cur_addr, cur_pages));
542
543 return ResultSuccess;
544}
545
489ResultCode KPageTable::UnmapProcessMemory(VAddr dst_addr, std::size_t size, 546ResultCode KPageTable::UnmapProcessMemory(VAddr dst_addr, std::size_t size,
490 KPageTable& src_page_table, VAddr src_addr) { 547 KPageTable& src_page_table, VAddr src_addr) {
491 KScopedLightLock lk(general_lock); 548 KScopedLightLock lk(general_lock);
@@ -511,6 +568,8 @@ ResultCode KPageTable::UnmapProcessMemory(VAddr dst_addr, std::size_t size,
511 block_manager->Update(dst_addr, num_pages, KMemoryState::Free, KMemoryPermission::None, 568 block_manager->Update(dst_addr, num_pages, KMemoryState::Free, KMemoryPermission::None,
512 KMemoryAttribute::None); 569 KMemoryAttribute::None);
513 570
571 system.InvalidateCpuInstructionCaches();
572
514 return ResultSuccess; 573 return ResultSuccess;
515} 574}
516 575
@@ -1223,6 +1282,31 @@ ResultCode KPageTable::UnmapPages(VAddr address, std::size_t num_pages, KMemoryS
1223 return ResultSuccess; 1282 return ResultSuccess;
1224} 1283}
1225 1284
1285ResultCode KPageTable::MakeAndOpenPageGroup(KPageLinkedList* out, VAddr address, size_t num_pages,
1286 KMemoryState state_mask, KMemoryState state,
1287 KMemoryPermission perm_mask, KMemoryPermission perm,
1288 KMemoryAttribute attr_mask, KMemoryAttribute attr) {
1289 // Ensure that the page group isn't null.
1290 ASSERT(out != nullptr);
1291
1292 // Make sure that the region we're mapping is valid for the table.
1293 const size_t size = num_pages * PageSize;
1294 R_UNLESS(this->Contains(address, size), ResultInvalidCurrentMemory);
1295
1296 // Lock the table.
1297 KScopedLightLock lk(general_lock);
1298
1299 // Check if state allows us to create the group.
1300 R_TRY(this->CheckMemoryState(address, size, state_mask | KMemoryState::FlagReferenceCounted,
1301 state | KMemoryState::FlagReferenceCounted, perm_mask, perm,
1302 attr_mask, attr));
1303
1304 // Create a new page group for the region.
1305 R_TRY(this->MakePageGroup(*out, address, num_pages));
1306
1307 return ResultSuccess;
1308}
1309
1226ResultCode KPageTable::SetProcessMemoryPermission(VAddr addr, std::size_t size, 1310ResultCode KPageTable::SetProcessMemoryPermission(VAddr addr, std::size_t size,
1227 Svc::MemoryPermission svc_perm) { 1311 Svc::MemoryPermission svc_perm) {
1228 const size_t num_pages = size / PageSize; 1312 const size_t num_pages = size / PageSize;
@@ -1605,57 +1689,21 @@ ResultCode KPageTable::UnlockForDeviceAddressSpace(VAddr addr, std::size_t size)
1605} 1689}
1606 1690
1607ResultCode KPageTable::LockForCodeMemory(VAddr addr, std::size_t size) { 1691ResultCode KPageTable::LockForCodeMemory(VAddr addr, std::size_t size) {
1608 KScopedLightLock lk(general_lock); 1692 return this->LockMemoryAndOpen(
1609 1693 nullptr, nullptr, addr, size, KMemoryState::FlagCanCodeMemory,
1610 KMemoryPermission new_perm = KMemoryPermission::NotMapped | KMemoryPermission::KernelReadWrite; 1694 KMemoryState::FlagCanCodeMemory, KMemoryPermission::All, KMemoryPermission::UserReadWrite,
1611 1695 KMemoryAttribute::All, KMemoryAttribute::None,
1612 KMemoryPermission old_perm{}; 1696 static_cast<KMemoryPermission>(KMemoryPermission::NotMapped |
1613 1697 KMemoryPermission::KernelReadWrite),
1614 if (const ResultCode result{CheckMemoryState( 1698 KMemoryAttribute::Locked);
1615 nullptr, &old_perm, nullptr, nullptr, addr, size, KMemoryState::FlagCanCodeMemory,
1616 KMemoryState::FlagCanCodeMemory, KMemoryPermission::All,
1617 KMemoryPermission::UserReadWrite, KMemoryAttribute::All, KMemoryAttribute::None)};
1618 result.IsError()) {
1619 return result;
1620 }
1621
1622 new_perm = (new_perm != KMemoryPermission::None) ? new_perm : old_perm;
1623
1624 block_manager->UpdateLock(
1625 addr, size / PageSize,
1626 [](KMemoryBlockManager::iterator block, KMemoryPermission permission) {
1627 block->ShareToDevice(permission);
1628 },
1629 new_perm);
1630
1631 return ResultSuccess;
1632} 1699}
1633 1700
1634ResultCode KPageTable::UnlockForCodeMemory(VAddr addr, std::size_t size) { 1701ResultCode KPageTable::UnlockForCodeMemory(VAddr addr, std::size_t size) {
1635 KScopedLightLock lk(general_lock); 1702 return this->UnlockMemory(addr, size, KMemoryState::FlagCanCodeMemory,
1636 1703 KMemoryState::FlagCanCodeMemory, KMemoryPermission::None,
1637 KMemoryPermission new_perm = KMemoryPermission::UserReadWrite; 1704 KMemoryPermission::None, KMemoryAttribute::All,
1638 1705 KMemoryAttribute::Locked, KMemoryPermission::UserReadWrite,
1639 KMemoryPermission old_perm{}; 1706 KMemoryAttribute::Locked, nullptr);
1640
1641 if (const ResultCode result{CheckMemoryState(
1642 nullptr, &old_perm, nullptr, nullptr, addr, size, KMemoryState::FlagCanCodeMemory,
1643 KMemoryState::FlagCanCodeMemory, KMemoryPermission::None, KMemoryPermission::None,
1644 KMemoryAttribute::All, KMemoryAttribute::Locked)};
1645 result.IsError()) {
1646 return result;
1647 }
1648
1649 new_perm = (new_perm != KMemoryPermission::None) ? new_perm : old_perm;
1650
1651 block_manager->UpdateLock(
1652 addr, size / PageSize,
1653 [](KMemoryBlockManager::iterator block, KMemoryPermission permission) {
1654 block->UnshareToDevice(permission);
1655 },
1656 new_perm);
1657
1658 return ResultSuccess;
1659} 1707}
1660 1708
1661ResultCode KPageTable::InitializeMemoryLayout(VAddr start, VAddr end) { 1709ResultCode KPageTable::InitializeMemoryLayout(VAddr start, VAddr end) {
@@ -1991,4 +2039,109 @@ ResultCode KPageTable::CheckMemoryState(KMemoryState* out_state, KMemoryPermissi
1991 return ResultSuccess; 2039 return ResultSuccess;
1992} 2040}
1993 2041
2042ResultCode KPageTable::LockMemoryAndOpen(KPageLinkedList* out_pg, PAddr* out_paddr, VAddr addr,
2043 size_t size, KMemoryState state_mask, KMemoryState state,
2044 KMemoryPermission perm_mask, KMemoryPermission perm,
2045 KMemoryAttribute attr_mask, KMemoryAttribute attr,
2046 KMemoryPermission new_perm, KMemoryAttribute lock_attr) {
2047 // Validate basic preconditions.
2048 ASSERT((lock_attr & attr) == KMemoryAttribute::None);
2049 ASSERT((lock_attr & (KMemoryAttribute::IpcLocked | KMemoryAttribute::DeviceShared)) ==
2050 KMemoryAttribute::None);
2051
2052 // Validate the lock request.
2053 const size_t num_pages = size / PageSize;
2054 R_UNLESS(this->Contains(addr, size), ResultInvalidCurrentMemory);
2055
2056 // Lock the table.
2057 KScopedLightLock lk(general_lock);
2058
2059 // Check that the output page group is empty, if it exists.
2060 if (out_pg) {
2061 ASSERT(out_pg->GetNumPages() == 0);
2062 }
2063
2064 // Check the state.
2065 KMemoryState old_state{};
2066 KMemoryPermission old_perm{};
2067 KMemoryAttribute old_attr{};
2068 size_t num_allocator_blocks{};
2069 R_TRY(this->CheckMemoryState(std::addressof(old_state), std::addressof(old_perm),
2070 std::addressof(old_attr), std::addressof(num_allocator_blocks),
2071 addr, size, state_mask | KMemoryState::FlagReferenceCounted,
2072 state | KMemoryState::FlagReferenceCounted, perm_mask, perm,
2073 attr_mask, attr));
2074
2075 // Get the physical address, if we're supposed to.
2076 if (out_paddr != nullptr) {
2077 ASSERT(this->GetPhysicalAddressLocked(out_paddr, addr));
2078 }
2079
2080 // Make the page group, if we're supposed to.
2081 if (out_pg != nullptr) {
2082 R_TRY(this->MakePageGroup(*out_pg, addr, num_pages));
2083 }
2084
2085 // Decide on new perm and attr.
2086 new_perm = (new_perm != KMemoryPermission::None) ? new_perm : old_perm;
2087 KMemoryAttribute new_attr = static_cast<KMemoryAttribute>(old_attr | lock_attr);
2088
2089 // Update permission, if we need to.
2090 if (new_perm != old_perm) {
2091 R_TRY(Operate(addr, num_pages, new_perm, OperationType::ChangePermissions));
2092 }
2093
2094 // Apply the memory block updates.
2095 block_manager->Update(addr, num_pages, old_state, new_perm, new_attr);
2096
2097 return ResultSuccess;
2098}
2099
2100ResultCode KPageTable::UnlockMemory(VAddr addr, size_t size, KMemoryState state_mask,
2101 KMemoryState state, KMemoryPermission perm_mask,
2102 KMemoryPermission perm, KMemoryAttribute attr_mask,
2103 KMemoryAttribute attr, KMemoryPermission new_perm,
2104 KMemoryAttribute lock_attr, const KPageLinkedList* pg) {
2105 // Validate basic preconditions.
2106 ASSERT((attr_mask & lock_attr) == lock_attr);
2107 ASSERT((attr & lock_attr) == lock_attr);
2108
2109 // Validate the unlock request.
2110 const size_t num_pages = size / PageSize;
2111 R_UNLESS(this->Contains(addr, size), ResultInvalidCurrentMemory);
2112
2113 // Lock the table.
2114 KScopedLightLock lk(general_lock);
2115
2116 // Check the state.
2117 KMemoryState old_state{};
2118 KMemoryPermission old_perm{};
2119 KMemoryAttribute old_attr{};
2120 size_t num_allocator_blocks{};
2121 R_TRY(this->CheckMemoryState(std::addressof(old_state), std::addressof(old_perm),
2122 std::addressof(old_attr), std::addressof(num_allocator_blocks),
2123 addr, size, state_mask | KMemoryState::FlagReferenceCounted,
2124 state | KMemoryState::FlagReferenceCounted, perm_mask, perm,
2125 attr_mask, attr));
2126
2127 // Check the page group.
2128 if (pg != nullptr) {
2129 UNIMPLEMENTED_MSG("PageGroup support is unimplemented!");
2130 }
2131
2132 // Decide on new perm and attr.
2133 new_perm = (new_perm != KMemoryPermission::None) ? new_perm : old_perm;
2134 KMemoryAttribute new_attr = static_cast<KMemoryAttribute>(old_attr & ~lock_attr);
2135
2136 // Update permission, if we need to.
2137 if (new_perm != old_perm) {
2138 R_TRY(Operate(addr, num_pages, new_perm, OperationType::ChangePermissions));
2139 }
2140
2141 // Apply the memory block updates.
2142 block_manager->Update(addr, num_pages, old_state, new_perm, new_attr);
2143
2144 return ResultSuccess;
2145}
2146
1994} // namespace Kernel 2147} // namespace Kernel
diff --git a/src/core/hle/kernel/k_page_table.h b/src/core/hle/kernel/k_page_table.h
index 54c6adf8d..dd6022975 100644
--- a/src/core/hle/kernel/k_page_table.h
+++ b/src/core/hle/kernel/k_page_table.h
@@ -12,6 +12,7 @@
12#include "core/file_sys/program_metadata.h" 12#include "core/file_sys/program_metadata.h"
13#include "core/hle/kernel/k_light_lock.h" 13#include "core/hle/kernel/k_light_lock.h"
14#include "core/hle/kernel/k_memory_block.h" 14#include "core/hle/kernel/k_memory_block.h"
15#include "core/hle/kernel/k_memory_layout.h"
15#include "core/hle/kernel/k_memory_manager.h" 16#include "core/hle/kernel/k_memory_manager.h"
16#include "core/hle/result.h" 17#include "core/hle/result.h"
17 18
@@ -25,6 +26,8 @@ class KMemoryBlockManager;
25 26
26class KPageTable final { 27class KPageTable final {
27public: 28public:
29 enum class ICacheInvalidationStrategy : u32 { InvalidateRange, InvalidateAll };
30
28 YUZU_NON_COPYABLE(KPageTable); 31 YUZU_NON_COPYABLE(KPageTable);
29 YUZU_NON_MOVEABLE(KPageTable); 32 YUZU_NON_MOVEABLE(KPageTable);
30 33
@@ -37,7 +40,8 @@ public:
37 ResultCode MapProcessCode(VAddr addr, std::size_t pages_count, KMemoryState state, 40 ResultCode MapProcessCode(VAddr addr, std::size_t pages_count, KMemoryState state,
38 KMemoryPermission perm); 41 KMemoryPermission perm);
39 ResultCode MapCodeMemory(VAddr dst_address, VAddr src_address, std::size_t size); 42 ResultCode MapCodeMemory(VAddr dst_address, VAddr src_address, std::size_t size);
40 ResultCode UnmapCodeMemory(VAddr dst_address, VAddr src_address, std::size_t size); 43 ResultCode UnmapCodeMemory(VAddr dst_address, VAddr src_address, std::size_t size,
44 ICacheInvalidationStrategy icache_invalidation_strategy);
41 ResultCode UnmapProcessMemory(VAddr dst_addr, std::size_t size, KPageTable& src_page_table, 45 ResultCode UnmapProcessMemory(VAddr dst_addr, std::size_t size, KPageTable& src_page_table,
42 VAddr src_addr); 46 VAddr src_addr);
43 ResultCode MapPhysicalMemory(VAddr addr, std::size_t size); 47 ResultCode MapPhysicalMemory(VAddr addr, std::size_t size);
@@ -71,6 +75,10 @@ public:
71 ResultCode UnlockForDeviceAddressSpace(VAddr addr, std::size_t size); 75 ResultCode UnlockForDeviceAddressSpace(VAddr addr, std::size_t size);
72 ResultCode LockForCodeMemory(VAddr addr, std::size_t size); 76 ResultCode LockForCodeMemory(VAddr addr, std::size_t size);
73 ResultCode UnlockForCodeMemory(VAddr addr, std::size_t size); 77 ResultCode UnlockForCodeMemory(VAddr addr, std::size_t size);
78 ResultCode MakeAndOpenPageGroup(KPageLinkedList* out, VAddr address, size_t num_pages,
79 KMemoryState state_mask, KMemoryState state,
80 KMemoryPermission perm_mask, KMemoryPermission perm,
81 KMemoryAttribute attr_mask, KMemoryAttribute attr);
74 82
75 Common::PageTable& PageTableImpl() { 83 Common::PageTable& PageTableImpl() {
76 return page_table_impl; 84 return page_table_impl;
@@ -159,10 +167,37 @@ private:
159 attr_mask, attr, ignore_attr); 167 attr_mask, attr, ignore_attr);
160 } 168 }
161 169
170 ResultCode LockMemoryAndOpen(KPageLinkedList* out_pg, PAddr* out_paddr, VAddr addr, size_t size,
171 KMemoryState state_mask, KMemoryState state,
172 KMemoryPermission perm_mask, KMemoryPermission perm,
173 KMemoryAttribute attr_mask, KMemoryAttribute attr,
174 KMemoryPermission new_perm, KMemoryAttribute lock_attr);
175 ResultCode UnlockMemory(VAddr addr, size_t size, KMemoryState state_mask, KMemoryState state,
176 KMemoryPermission perm_mask, KMemoryPermission perm,
177 KMemoryAttribute attr_mask, KMemoryAttribute attr,
178 KMemoryPermission new_perm, KMemoryAttribute lock_attr,
179 const KPageLinkedList* pg);
180
181 ResultCode MakePageGroup(KPageLinkedList& pg, VAddr addr, size_t num_pages);
182
162 bool IsLockedByCurrentThread() const { 183 bool IsLockedByCurrentThread() const {
163 return general_lock.IsLockedByCurrentThread(); 184 return general_lock.IsLockedByCurrentThread();
164 } 185 }
165 186
187 bool IsHeapPhysicalAddress(const KMemoryLayout& layout, PAddr phys_addr) {
188 ASSERT(this->IsLockedByCurrentThread());
189
190 return layout.IsHeapPhysicalAddress(cached_physical_heap_region, phys_addr);
191 }
192
193 bool GetPhysicalAddressLocked(PAddr* out, VAddr virt_addr) const {
194 ASSERT(this->IsLockedByCurrentThread());
195
196 *out = GetPhysicalAddr(virt_addr);
197
198 return *out != 0;
199 }
200
166 mutable KLightLock general_lock; 201 mutable KLightLock general_lock;
167 mutable KLightLock map_physical_memory_lock; 202 mutable KLightLock map_physical_memory_lock;
168 203
@@ -322,6 +357,7 @@ private:
322 bool is_aslr_enabled{}; 357 bool is_aslr_enabled{};
323 358
324 u32 heap_fill_value{}; 359 u32 heap_fill_value{};
360 const KMemoryRegion* cached_physical_heap_region{};
325 361
326 KMemoryManager::Pool memory_pool{KMemoryManager::Pool::Application}; 362 KMemoryManager::Pool memory_pool{KMemoryManager::Pool::Application};
327 KMemoryManager::Direction allocation_option{KMemoryManager::Direction::FromFront}; 363 KMemoryManager::Direction allocation_option{KMemoryManager::Direction::FromFront};
diff --git a/src/core/hle/kernel/k_process.h b/src/core/hle/kernel/k_process.h
index 48b17fc74..9f171e3da 100644
--- a/src/core/hle/kernel/k_process.h
+++ b/src/core/hle/kernel/k_process.h
@@ -422,7 +422,7 @@ private:
422 bool is_64bit_process = true; 422 bool is_64bit_process = true;
423 423
424 /// Total running time for the process in ticks. 424 /// Total running time for the process in ticks.
425 u64 total_process_running_time_ticks = 0; 425 std::atomic<u64> total_process_running_time_ticks = 0;
426 426
427 /// Per-process handle table for storing created object handles in. 427 /// Per-process handle table for storing created object handles in.
428 KHandleTable handle_table; 428 KHandleTable handle_table;
diff --git a/src/core/hle/kernel/k_scheduler.cpp b/src/core/hle/kernel/k_scheduler.cpp
index 6c0bb1672..526eb4b70 100644
--- a/src/core/hle/kernel/k_scheduler.cpp
+++ b/src/core/hle/kernel/k_scheduler.cpp
@@ -705,7 +705,7 @@ void KScheduler::Unload(KThread* thread) {
705 prev_thread = nullptr; 705 prev_thread = nullptr;
706 } 706 }
707 707
708 thread->context_guard.Unlock(); 708 thread->context_guard.unlock();
709} 709}
710 710
711void KScheduler::Reload(KThread* thread) { 711void KScheduler::Reload(KThread* thread) {
@@ -794,13 +794,13 @@ void KScheduler::SwitchToCurrent() {
794 do { 794 do {
795 auto next_thread = current_thread.load(); 795 auto next_thread = current_thread.load();
796 if (next_thread != nullptr) { 796 if (next_thread != nullptr) {
797 const auto locked = next_thread->context_guard.TryLock(); 797 const auto locked = next_thread->context_guard.try_lock();
798 if (state.needs_scheduling.load()) { 798 if (state.needs_scheduling.load()) {
799 next_thread->context_guard.Unlock(); 799 next_thread->context_guard.unlock();
800 break; 800 break;
801 } 801 }
802 if (next_thread->GetActiveCore() != core_id) { 802 if (next_thread->GetActiveCore() != core_id) {
803 next_thread->context_guard.Unlock(); 803 next_thread->context_guard.unlock();
804 break; 804 break;
805 } 805 }
806 if (!locked) { 806 if (!locked) {
diff --git a/src/core/hle/kernel/k_scheduler_lock.h b/src/core/hle/kernel/k_scheduler_lock.h
index 93c47f1b1..016e0a818 100644
--- a/src/core/hle/kernel/k_scheduler_lock.h
+++ b/src/core/hle/kernel/k_scheduler_lock.h
@@ -4,6 +4,7 @@
4 4
5#pragma once 5#pragma once
6 6
7#include <atomic>
7#include "common/assert.h" 8#include "common/assert.h"
8#include "core/hle/kernel/k_spin_lock.h" 9#include "core/hle/kernel/k_spin_lock.h"
9#include "core/hle/kernel/k_thread.h" 10#include "core/hle/kernel/k_thread.h"
@@ -75,7 +76,7 @@ private:
75 KernelCore& kernel; 76 KernelCore& kernel;
76 KAlignedSpinLock spin_lock{}; 77 KAlignedSpinLock spin_lock{};
77 s32 lock_count{}; 78 s32 lock_count{};
78 KThread* owner_thread{}; 79 std::atomic<KThread*> owner_thread{};
79}; 80};
80 81
81} // namespace Kernel 82} // namespace Kernel
diff --git a/src/core/hle/kernel/k_server_port.cpp b/src/core/hle/kernel/k_server_port.cpp
index 433fc98e1..e66c0c992 100644
--- a/src/core/hle/kernel/k_server_port.cpp
+++ b/src/core/hle/kernel/k_server_port.cpp
@@ -62,6 +62,12 @@ void KServerPort::Destroy() {
62 62
63 // Close our reference to our parent. 63 // Close our reference to our parent.
64 parent->Close(); 64 parent->Close();
65
66 // Release host emulation members.
67 session_handler.reset();
68
69 // Ensure that the global list tracking server objects does not hold on to a reference.
70 kernel.UnregisterServerObject(this);
65} 71}
66 72
67bool KServerPort::IsSignaled() const { 73bool KServerPort::IsSignaled() const {
diff --git a/src/core/hle/kernel/k_server_session.cpp b/src/core/hle/kernel/k_server_session.cpp
index 30c56ff29..7ac2ef254 100644
--- a/src/core/hle/kernel/k_server_session.cpp
+++ b/src/core/hle/kernel/k_server_session.cpp
@@ -49,6 +49,9 @@ void KServerSession::Destroy() {
49 49
50 // Release host emulation members. 50 // Release host emulation members.
51 manager.reset(); 51 manager.reset();
52
53 // Ensure that the global list tracking server objects does not hold on to a reference.
54 kernel.UnregisterServerObject(this);
52} 55}
53 56
54void KServerSession::OnClientClosed() { 57void KServerSession::OnClientClosed() {
diff --git a/src/core/hle/kernel/k_spin_lock.cpp b/src/core/hle/kernel/k_spin_lock.cpp
index 4412aa4bb..527ff0f9f 100644
--- a/src/core/hle/kernel/k_spin_lock.cpp
+++ b/src/core/hle/kernel/k_spin_lock.cpp
@@ -4,51 +4,18 @@
4 4
5#include "core/hle/kernel/k_spin_lock.h" 5#include "core/hle/kernel/k_spin_lock.h"
6 6
7#if _MSC_VER
8#include <intrin.h>
9#if _M_AMD64
10#define __x86_64__ 1
11#endif
12#if _M_ARM64
13#define __aarch64__ 1
14#endif
15#else
16#if __x86_64__
17#include <xmmintrin.h>
18#endif
19#endif
20
21namespace {
22
23void ThreadPause() {
24#if __x86_64__
25 _mm_pause();
26#elif __aarch64__ && _MSC_VER
27 __yield();
28#elif __aarch64__
29 asm("yield");
30#endif
31}
32
33} // namespace
34
35namespace Kernel { 7namespace Kernel {
36 8
37void KSpinLock::Lock() { 9void KSpinLock::Lock() {
38 while (lck.test_and_set(std::memory_order_acquire)) { 10 lck.lock();
39 ThreadPause();
40 }
41} 11}
42 12
43void KSpinLock::Unlock() { 13void KSpinLock::Unlock() {
44 lck.clear(std::memory_order_release); 14 lck.unlock();
45} 15}
46 16
47bool KSpinLock::TryLock() { 17bool KSpinLock::TryLock() {
48 if (lck.test_and_set(std::memory_order_acquire)) { 18 return lck.try_lock();
49 return false;
50 }
51 return true;
52} 19}
53 20
54} // namespace Kernel 21} // namespace Kernel
diff --git a/src/core/hle/kernel/k_spin_lock.h b/src/core/hle/kernel/k_spin_lock.h
index 4d87d006a..7868b25a5 100644
--- a/src/core/hle/kernel/k_spin_lock.h
+++ b/src/core/hle/kernel/k_spin_lock.h
@@ -4,7 +4,7 @@
4 4
5#pragma once 5#pragma once
6 6
7#include <atomic> 7#include <mutex>
8 8
9#include "core/hle/kernel/k_scoped_lock.h" 9#include "core/hle/kernel/k_scoped_lock.h"
10 10
@@ -25,7 +25,7 @@ public:
25 [[nodiscard]] bool TryLock(); 25 [[nodiscard]] bool TryLock();
26 26
27private: 27private:
28 std::atomic_flag lck = ATOMIC_FLAG_INIT; 28 std::mutex lck;
29}; 29};
30 30
31// TODO(bunnei): Alias for now, in case we want to implement these accurately in the future. 31// TODO(bunnei): Alias for now, in case we want to implement these accurately in the future.
diff --git a/src/core/hle/kernel/k_thread.cpp b/src/core/hle/kernel/k_thread.cpp
index 94c8faf68..af71987e8 100644
--- a/src/core/hle/kernel/k_thread.cpp
+++ b/src/core/hle/kernel/k_thread.cpp
@@ -723,10 +723,10 @@ void KThread::UpdateState() {
723 ASSERT(kernel.GlobalSchedulerContext().IsLocked()); 723 ASSERT(kernel.GlobalSchedulerContext().IsLocked());
724 724
725 // Set our suspend flags in state. 725 // Set our suspend flags in state.
726 const auto old_state = thread_state; 726 const ThreadState old_state = thread_state.load(std::memory_order_relaxed);
727 const auto new_state = 727 const auto new_state =
728 static_cast<ThreadState>(this->GetSuspendFlags()) | (old_state & ThreadState::Mask); 728 static_cast<ThreadState>(this->GetSuspendFlags()) | (old_state & ThreadState::Mask);
729 thread_state = new_state; 729 thread_state.store(new_state, std::memory_order_relaxed);
730 730
731 // Note the state change in scheduler. 731 // Note the state change in scheduler.
732 if (new_state != old_state) { 732 if (new_state != old_state) {
@@ -738,8 +738,8 @@ void KThread::Continue() {
738 ASSERT(kernel.GlobalSchedulerContext().IsLocked()); 738 ASSERT(kernel.GlobalSchedulerContext().IsLocked());
739 739
740 // Clear our suspend flags in state. 740 // Clear our suspend flags in state.
741 const auto old_state = thread_state; 741 const ThreadState old_state = thread_state.load(std::memory_order_relaxed);
742 thread_state = old_state & ThreadState::Mask; 742 thread_state.store(old_state & ThreadState::Mask, std::memory_order_relaxed);
743 743
744 // Note the state change in scheduler. 744 // Note the state change in scheduler.
745 KScheduler::OnThreadStateChanged(kernel, this, old_state); 745 KScheduler::OnThreadStateChanged(kernel, this, old_state);
@@ -1079,17 +1079,10 @@ void KThread::IfDummyThreadTryWait() {
1079 return; 1079 return;
1080 } 1080 }
1081 1081
1082 // Block until we can grab the lock. 1082 // Block until we are no longer waiting.
1083 KScopedSpinLock lk{dummy_wait_lock}; 1083 std::unique_lock lk(dummy_wait_lock);
1084} 1084 dummy_wait_cv.wait(
1085 1085 lk, [&] { return GetState() != ThreadState::Waiting || kernel.IsShuttingDown(); });
1086void KThread::IfDummyThreadBeginWait() {
1087 if (!IsDummyThread()) {
1088 return;
1089 }
1090
1091 // Ensure the thread will block when IfDummyThreadTryWait is called.
1092 dummy_wait_lock.Lock();
1093} 1086}
1094 1087
1095void KThread::IfDummyThreadEndWait() { 1088void KThread::IfDummyThreadEndWait() {
@@ -1097,8 +1090,8 @@ void KThread::IfDummyThreadEndWait() {
1097 return; 1090 return;
1098 } 1091 }
1099 1092
1100 // Ensure the thread will no longer block. 1093 // Wake up the waiting thread.
1101 dummy_wait_lock.Unlock(); 1094 dummy_wait_cv.notify_one();
1102} 1095}
1103 1096
1104void KThread::BeginWait(KThreadQueue* queue) { 1097void KThread::BeginWait(KThreadQueue* queue) {
@@ -1107,9 +1100,6 @@ void KThread::BeginWait(KThreadQueue* queue) {
1107 1100
1108 // Set our wait queue. 1101 // Set our wait queue.
1109 wait_queue = queue; 1102 wait_queue = queue;
1110
1111 // Special case for dummy threads to ensure they block.
1112 IfDummyThreadBeginWait();
1113} 1103}
1114 1104
1115void KThread::NotifyAvailable(KSynchronizationObject* signaled_object, ResultCode wait_result_) { 1105void KThread::NotifyAvailable(KSynchronizationObject* signaled_object, ResultCode wait_result_) {
@@ -1158,10 +1148,11 @@ void KThread::SetState(ThreadState state) {
1158 SetMutexWaitAddressForDebugging({}); 1148 SetMutexWaitAddressForDebugging({});
1159 SetWaitReasonForDebugging({}); 1149 SetWaitReasonForDebugging({});
1160 1150
1161 const ThreadState old_state = thread_state; 1151 const ThreadState old_state = thread_state.load(std::memory_order_relaxed);
1162 thread_state = 1152 thread_state.store(
1163 static_cast<ThreadState>((old_state & ~ThreadState::Mask) | (state & ThreadState::Mask)); 1153 static_cast<ThreadState>((old_state & ~ThreadState::Mask) | (state & ThreadState::Mask)),
1164 if (thread_state != old_state) { 1154 std::memory_order_relaxed);
1155 if (thread_state.load(std::memory_order_relaxed) != old_state) {
1165 KScheduler::OnThreadStateChanged(kernel, this, old_state); 1156 KScheduler::OnThreadStateChanged(kernel, this, old_state);
1166 } 1157 }
1167} 1158}
diff --git a/src/core/hle/kernel/k_thread.h b/src/core/hle/kernel/k_thread.h
index f46db7298..4892fdf76 100644
--- a/src/core/hle/kernel/k_thread.h
+++ b/src/core/hle/kernel/k_thread.h
@@ -5,6 +5,9 @@
5#pragma once 5#pragma once
6 6
7#include <array> 7#include <array>
8#include <atomic>
9#include <condition_variable>
10#include <mutex>
8#include <span> 11#include <span>
9#include <string> 12#include <string>
10#include <utility> 13#include <utility>
@@ -14,6 +17,7 @@
14 17
15#include "common/common_types.h" 18#include "common/common_types.h"
16#include "common/intrusive_red_black_tree.h" 19#include "common/intrusive_red_black_tree.h"
20#include "common/spin_lock.h"
17#include "core/arm/arm_interface.h" 21#include "core/arm/arm_interface.h"
18#include "core/hle/kernel/k_affinity_mask.h" 22#include "core/hle/kernel/k_affinity_mask.h"
19#include "core/hle/kernel/k_light_lock.h" 23#include "core/hle/kernel/k_light_lock.h"
@@ -255,11 +259,11 @@ public:
255 [[nodiscard]] std::shared_ptr<Common::Fiber>& GetHostContext(); 259 [[nodiscard]] std::shared_ptr<Common::Fiber>& GetHostContext();
256 260
257 [[nodiscard]] ThreadState GetState() const { 261 [[nodiscard]] ThreadState GetState() const {
258 return thread_state & ThreadState::Mask; 262 return thread_state.load(std::memory_order_relaxed) & ThreadState::Mask;
259 } 263 }
260 264
261 [[nodiscard]] ThreadState GetRawState() const { 265 [[nodiscard]] ThreadState GetRawState() const {
262 return thread_state; 266 return thread_state.load(std::memory_order_relaxed);
263 } 267 }
264 268
265 void SetState(ThreadState state); 269 void SetState(ThreadState state);
@@ -641,7 +645,6 @@ public:
641 // blocking as needed. 645 // blocking as needed.
642 646
643 void IfDummyThreadTryWait(); 647 void IfDummyThreadTryWait();
644 void IfDummyThreadBeginWait();
645 void IfDummyThreadEndWait(); 648 void IfDummyThreadEndWait();
646 649
647private: 650private:
@@ -751,7 +754,7 @@ private:
751 KAffinityMask original_physical_affinity_mask{}; 754 KAffinityMask original_physical_affinity_mask{};
752 s32 original_physical_ideal_core_id{}; 755 s32 original_physical_ideal_core_id{};
753 s32 num_core_migration_disables{}; 756 s32 num_core_migration_disables{};
754 ThreadState thread_state{}; 757 std::atomic<ThreadState> thread_state{};
755 std::atomic<bool> termination_requested{}; 758 std::atomic<bool> termination_requested{};
756 bool wait_cancelled{}; 759 bool wait_cancelled{};
757 bool cancellable{}; 760 bool cancellable{};
@@ -761,13 +764,14 @@ private:
761 s8 priority_inheritance_count{}; 764 s8 priority_inheritance_count{};
762 bool resource_limit_release_hint{}; 765 bool resource_limit_release_hint{};
763 StackParameters stack_parameters{}; 766 StackParameters stack_parameters{};
764 KSpinLock context_guard{}; 767 Common::SpinLock context_guard{};
765 KSpinLock dummy_wait_lock{};
766 768
767 // For emulation 769 // For emulation
768 std::shared_ptr<Common::Fiber> host_context{}; 770 std::shared_ptr<Common::Fiber> host_context{};
769 bool is_single_core{}; 771 bool is_single_core{};
770 ThreadType thread_type{}; 772 ThreadType thread_type{};
773 std::mutex dummy_wait_lock;
774 std::condition_variable dummy_wait_cv;
771 775
772 // For debugging 776 // For debugging
773 std::vector<KSynchronizationObject*> wait_objects_for_debugging; 777 std::vector<KSynchronizationObject*> wait_objects_for_debugging;
diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp
index 34da7c23b..5984afd7e 100644
--- a/src/core/hle/kernel/kernel.cpp
+++ b/src/core/hle/kernel/kernel.cpp
@@ -61,6 +61,7 @@ struct KernelCore::Impl {
61 global_scheduler_context = std::make_unique<Kernel::GlobalSchedulerContext>(kernel); 61 global_scheduler_context = std::make_unique<Kernel::GlobalSchedulerContext>(kernel);
62 global_handle_table = std::make_unique<Kernel::KHandleTable>(kernel); 62 global_handle_table = std::make_unique<Kernel::KHandleTable>(kernel);
63 global_handle_table->Initialize(KHandleTable::MaxTableSize); 63 global_handle_table->Initialize(KHandleTable::MaxTableSize);
64 default_service_thread = CreateServiceThread(kernel, "DefaultServiceThread");
64 65
65 is_phantom_mode_for_singlecore = false; 66 is_phantom_mode_for_singlecore = false;
66 67
@@ -84,7 +85,7 @@ struct KernelCore::Impl {
84 85
85 void InitializeCores() { 86 void InitializeCores() {
86 for (u32 core_id = 0; core_id < Core::Hardware::NUM_CPU_CORES; core_id++) { 87 for (u32 core_id = 0; core_id < Core::Hardware::NUM_CPU_CORES; core_id++) {
87 cores[core_id].Initialize(current_process->Is64BitProcess()); 88 cores[core_id].Initialize((*current_process).Is64BitProcess());
88 system.Memory().SetCurrentPageTable(*current_process, core_id); 89 system.Memory().SetCurrentPageTable(*current_process, core_id);
89 } 90 }
90 } 91 }
@@ -95,15 +96,15 @@ struct KernelCore::Impl {
95 96
96 process_list.clear(); 97 process_list.clear();
97 98
98 // Close all open server ports. 99 // Close all open server sessions and ports.
99 std::unordered_set<KServerPort*> server_ports_; 100 std::unordered_set<KAutoObject*> server_objects_;
100 { 101 {
101 std::lock_guard lk(server_ports_lock); 102 std::scoped_lock lk(server_objects_lock);
102 server_ports_ = server_ports; 103 server_objects_ = server_objects;
103 server_ports.clear(); 104 server_objects.clear();
104 } 105 }
105 for (auto* server_port : server_ports_) { 106 for (auto* server_object : server_objects_) {
106 server_port->Close(); 107 server_object->Close();
107 } 108 }
108 109
109 // Ensures all service threads gracefully shutdown. 110 // Ensures all service threads gracefully shutdown.
@@ -139,6 +140,7 @@ struct KernelCore::Impl {
139 CleanupObject(font_shared_mem); 140 CleanupObject(font_shared_mem);
140 CleanupObject(irs_shared_mem); 141 CleanupObject(irs_shared_mem);
141 CleanupObject(time_shared_mem); 142 CleanupObject(time_shared_mem);
143 CleanupObject(hidbus_shared_mem);
142 CleanupObject(system_resource_limit); 144 CleanupObject(system_resource_limit);
143 145
144 for (u32 core_id = 0; core_id < Core::Hardware::NUM_CPU_CORES; core_id++) { 146 for (u32 core_id = 0; core_id < Core::Hardware::NUM_CPU_CORES; core_id++) {
@@ -156,7 +158,7 @@ struct KernelCore::Impl {
156 158
157 // Close kernel objects that were not freed on shutdown 159 // Close kernel objects that were not freed on shutdown
158 { 160 {
159 std::lock_guard lk(registered_in_use_objects_lock); 161 std::scoped_lock lk{registered_in_use_objects_lock};
160 if (registered_in_use_objects.size()) { 162 if (registered_in_use_objects.size()) {
161 for (auto& object : registered_in_use_objects) { 163 for (auto& object : registered_in_use_objects) {
162 object->Close(); 164 object->Close();
@@ -167,17 +169,17 @@ struct KernelCore::Impl {
167 169
168 // Shutdown all processes. 170 // Shutdown all processes.
169 if (current_process) { 171 if (current_process) {
170 current_process->Finalize(); 172 (*current_process).Finalize();
171 // current_process->Close(); 173 // current_process->Close();
172 // TODO: The current process should be destroyed based on accurate ref counting after 174 // TODO: The current process should be destroyed based on accurate ref counting after
173 // calling Close(). Adding a manual Destroy() call instead to avoid a memory leak. 175 // calling Close(). Adding a manual Destroy() call instead to avoid a memory leak.
174 current_process->Destroy(); 176 (*current_process).Destroy();
175 current_process = nullptr; 177 current_process = nullptr;
176 } 178 }
177 179
178 // Track kernel objects that were not freed on shutdown 180 // Track kernel objects that were not freed on shutdown
179 { 181 {
180 std::lock_guard lk(registered_objects_lock); 182 std::scoped_lock lk{registered_objects_lock};
181 if (registered_objects.size()) { 183 if (registered_objects.size()) {
182 LOG_DEBUG(Kernel, "{} kernel objects were dangling on shutdown!", 184 LOG_DEBUG(Kernel, "{} kernel objects were dangling on shutdown!",
183 registered_objects.size()); 185 registered_objects.size());
@@ -621,16 +623,20 @@ struct KernelCore::Impl {
621 constexpr std::size_t font_size{0x1100000}; 623 constexpr std::size_t font_size{0x1100000};
622 constexpr std::size_t irs_size{0x8000}; 624 constexpr std::size_t irs_size{0x8000};
623 constexpr std::size_t time_size{0x1000}; 625 constexpr std::size_t time_size{0x1000};
626 constexpr std::size_t hidbus_size{0x1000};
624 627
625 const PAddr hid_phys_addr{system_pool.GetAddress()}; 628 const PAddr hid_phys_addr{system_pool.GetAddress()};
626 const PAddr font_phys_addr{system_pool.GetAddress() + hid_size}; 629 const PAddr font_phys_addr{system_pool.GetAddress() + hid_size};
627 const PAddr irs_phys_addr{system_pool.GetAddress() + hid_size + font_size}; 630 const PAddr irs_phys_addr{system_pool.GetAddress() + hid_size + font_size};
628 const PAddr time_phys_addr{system_pool.GetAddress() + hid_size + font_size + irs_size}; 631 const PAddr time_phys_addr{system_pool.GetAddress() + hid_size + font_size + irs_size};
632 const PAddr hidbus_phys_addr{system_pool.GetAddress() + hid_size + font_size + irs_size +
633 time_size};
629 634
630 hid_shared_mem = KSharedMemory::Create(system.Kernel()); 635 hid_shared_mem = KSharedMemory::Create(system.Kernel());
631 font_shared_mem = KSharedMemory::Create(system.Kernel()); 636 font_shared_mem = KSharedMemory::Create(system.Kernel());
632 irs_shared_mem = KSharedMemory::Create(system.Kernel()); 637 irs_shared_mem = KSharedMemory::Create(system.Kernel());
633 time_shared_mem = KSharedMemory::Create(system.Kernel()); 638 time_shared_mem = KSharedMemory::Create(system.Kernel());
639 hidbus_shared_mem = KSharedMemory::Create(system.Kernel());
634 640
635 hid_shared_mem->Initialize(system.DeviceMemory(), nullptr, 641 hid_shared_mem->Initialize(system.DeviceMemory(), nullptr,
636 {hid_phys_addr, hid_size / PageSize}, 642 {hid_phys_addr, hid_size / PageSize},
@@ -648,6 +654,10 @@ struct KernelCore::Impl {
648 {time_phys_addr, time_size / PageSize}, 654 {time_phys_addr, time_size / PageSize},
649 Svc::MemoryPermission::None, Svc::MemoryPermission::Read, 655 Svc::MemoryPermission::None, Svc::MemoryPermission::Read,
650 time_phys_addr, time_size, "Time:SharedMemory"); 656 time_phys_addr, time_size, "Time:SharedMemory");
657 hidbus_shared_mem->Initialize(system.DeviceMemory(), nullptr,
658 {hidbus_phys_addr, hidbus_size / PageSize},
659 Svc::MemoryPermission::None, Svc::MemoryPermission::Read,
660 hidbus_phys_addr, hidbus_size, "HidBus:SharedMemory");
651 } 661 }
652 662
653 KClientPort* CreateNamedServicePort(std::string name) { 663 KClientPort* CreateNamedServicePort(std::string name) {
@@ -658,13 +668,20 @@ struct KernelCore::Impl {
658 } 668 }
659 669
660 KClientPort* port = &search->second(system.ServiceManager(), system); 670 KClientPort* port = &search->second(system.ServiceManager(), system);
661 { 671 RegisterServerObject(&port->GetParent()->GetServerPort());
662 std::lock_guard lk(server_ports_lock);
663 server_ports.insert(&port->GetParent()->GetServerPort());
664 }
665 return port; 672 return port;
666 } 673 }
667 674
675 void RegisterServerObject(KAutoObject* server_object) {
676 std::scoped_lock lk(server_objects_lock);
677 server_objects.insert(server_object);
678 }
679
680 void UnregisterServerObject(KAutoObject* server_object) {
681 std::scoped_lock lk(server_objects_lock);
682 server_objects.erase(server_object);
683 }
684
668 std::weak_ptr<Kernel::ServiceThread> CreateServiceThread(KernelCore& kernel, 685 std::weak_ptr<Kernel::ServiceThread> CreateServiceThread(KernelCore& kernel,
669 const std::string& name) { 686 const std::string& name) {
670 auto service_thread = std::make_shared<Kernel::ServiceThread>(kernel, 1, name); 687 auto service_thread = std::make_shared<Kernel::ServiceThread>(kernel, 1, name);
@@ -677,6 +694,12 @@ struct KernelCore::Impl {
677 694
678 void ReleaseServiceThread(std::weak_ptr<Kernel::ServiceThread> service_thread) { 695 void ReleaseServiceThread(std::weak_ptr<Kernel::ServiceThread> service_thread) {
679 if (auto strong_ptr = service_thread.lock()) { 696 if (auto strong_ptr = service_thread.lock()) {
697 if (strong_ptr == default_service_thread.lock()) {
698 // Nothing to do here, the service is using default_service_thread, which will be
699 // released on shutdown.
700 return;
701 }
702
680 service_threads_manager.QueueWork( 703 service_threads_manager.QueueWork(
681 [this, strong_ptr{std::move(strong_ptr)}]() { service_threads.erase(strong_ptr); }); 704 [this, strong_ptr{std::move(strong_ptr)}]() { service_threads.erase(strong_ptr); });
682 } 705 }
@@ -686,7 +709,7 @@ struct KernelCore::Impl {
686 service_threads_manager.QueueWork([this]() { service_threads.clear(); }); 709 service_threads_manager.QueueWork([this]() { service_threads.clear(); });
687 } 710 }
688 711
689 std::mutex server_ports_lock; 712 std::mutex server_objects_lock;
690 std::mutex registered_objects_lock; 713 std::mutex registered_objects_lock;
691 std::mutex registered_in_use_objects_lock; 714 std::mutex registered_in_use_objects_lock;
692 715
@@ -697,7 +720,7 @@ struct KernelCore::Impl {
697 720
698 // Lists all processes that exist in the current session. 721 // Lists all processes that exist in the current session.
699 std::vector<KProcess*> process_list; 722 std::vector<KProcess*> process_list;
700 KProcess* current_process{}; 723 std::atomic<KProcess*> current_process{};
701 std::unique_ptr<Kernel::GlobalSchedulerContext> global_scheduler_context; 724 std::unique_ptr<Kernel::GlobalSchedulerContext> global_scheduler_context;
702 Kernel::TimeManager time_manager; 725 Kernel::TimeManager time_manager;
703 726
@@ -716,7 +739,7 @@ struct KernelCore::Impl {
716 /// the ConnectToPort SVC. 739 /// the ConnectToPort SVC.
717 std::unordered_map<std::string, ServiceInterfaceFactory> service_interface_factory; 740 std::unordered_map<std::string, ServiceInterfaceFactory> service_interface_factory;
718 NamedPortTable named_ports; 741 NamedPortTable named_ports;
719 std::unordered_set<KServerPort*> server_ports; 742 std::unordered_set<KAutoObject*> server_objects;
720 std::unordered_set<KAutoObject*> registered_objects; 743 std::unordered_set<KAutoObject*> registered_objects;
721 std::unordered_set<KAutoObject*> registered_in_use_objects; 744 std::unordered_set<KAutoObject*> registered_in_use_objects;
722 745
@@ -734,12 +757,14 @@ struct KernelCore::Impl {
734 Kernel::KSharedMemory* font_shared_mem{}; 757 Kernel::KSharedMemory* font_shared_mem{};
735 Kernel::KSharedMemory* irs_shared_mem{}; 758 Kernel::KSharedMemory* irs_shared_mem{};
736 Kernel::KSharedMemory* time_shared_mem{}; 759 Kernel::KSharedMemory* time_shared_mem{};
760 Kernel::KSharedMemory* hidbus_shared_mem{};
737 761
738 // Memory layout 762 // Memory layout
739 std::unique_ptr<KMemoryLayout> memory_layout; 763 std::unique_ptr<KMemoryLayout> memory_layout;
740 764
741 // Threads used for services 765 // Threads used for services
742 std::unordered_set<std::shared_ptr<Kernel::ServiceThread>> service_threads; 766 std::unordered_set<std::shared_ptr<ServiceThread>> service_threads;
767 std::weak_ptr<ServiceThread> default_service_thread;
743 Common::ThreadWorker service_threads_manager; 768 Common::ThreadWorker service_threads_manager;
744 769
745 std::array<KThread*, Core::Hardware::NUM_CPU_CORES> suspend_threads; 770 std::array<KThread*, Core::Hardware::NUM_CPU_CORES> suspend_threads;
@@ -920,23 +945,31 @@ KClientPort* KernelCore::CreateNamedServicePort(std::string name) {
920 return impl->CreateNamedServicePort(std::move(name)); 945 return impl->CreateNamedServicePort(std::move(name));
921} 946}
922 947
948void KernelCore::RegisterServerObject(KAutoObject* server_object) {
949 impl->RegisterServerObject(server_object);
950}
951
952void KernelCore::UnregisterServerObject(KAutoObject* server_object) {
953 impl->UnregisterServerObject(server_object);
954}
955
923void KernelCore::RegisterKernelObject(KAutoObject* object) { 956void KernelCore::RegisterKernelObject(KAutoObject* object) {
924 std::lock_guard lk(impl->registered_objects_lock); 957 std::scoped_lock lk{impl->registered_objects_lock};
925 impl->registered_objects.insert(object); 958 impl->registered_objects.insert(object);
926} 959}
927 960
928void KernelCore::UnregisterKernelObject(KAutoObject* object) { 961void KernelCore::UnregisterKernelObject(KAutoObject* object) {
929 std::lock_guard lk(impl->registered_objects_lock); 962 std::scoped_lock lk{impl->registered_objects_lock};
930 impl->registered_objects.erase(object); 963 impl->registered_objects.erase(object);
931} 964}
932 965
933void KernelCore::RegisterInUseObject(KAutoObject* object) { 966void KernelCore::RegisterInUseObject(KAutoObject* object) {
934 std::lock_guard lk(impl->registered_in_use_objects_lock); 967 std::scoped_lock lk{impl->registered_in_use_objects_lock};
935 impl->registered_in_use_objects.insert(object); 968 impl->registered_in_use_objects.insert(object);
936} 969}
937 970
938void KernelCore::UnregisterInUseObject(KAutoObject* object) { 971void KernelCore::UnregisterInUseObject(KAutoObject* object) {
939 std::lock_guard lk(impl->registered_in_use_objects_lock); 972 std::scoped_lock lk{impl->registered_in_use_objects_lock};
940 impl->registered_in_use_objects.erase(object); 973 impl->registered_in_use_objects.erase(object);
941} 974}
942 975
@@ -1024,6 +1057,14 @@ const Kernel::KSharedMemory& KernelCore::GetTimeSharedMem() const {
1024 return *impl->time_shared_mem; 1057 return *impl->time_shared_mem;
1025} 1058}
1026 1059
1060Kernel::KSharedMemory& KernelCore::GetHidBusSharedMem() {
1061 return *impl->hidbus_shared_mem;
1062}
1063
1064const Kernel::KSharedMemory& KernelCore::GetHidBusSharedMem() const {
1065 return *impl->hidbus_shared_mem;
1066}
1067
1027void KernelCore::Suspend(bool in_suspention) { 1068void KernelCore::Suspend(bool in_suspention) {
1028 const bool should_suspend = exception_exited || in_suspention; 1069 const bool should_suspend = exception_exited || in_suspention;
1029 { 1070 {
@@ -1065,6 +1106,10 @@ std::weak_ptr<Kernel::ServiceThread> KernelCore::CreateServiceThread(const std::
1065 return impl->CreateServiceThread(*this, name); 1106 return impl->CreateServiceThread(*this, name);
1066} 1107}
1067 1108
1109std::weak_ptr<Kernel::ServiceThread> KernelCore::GetDefaultServiceThread() const {
1110 return impl->default_service_thread;
1111}
1112
1068void KernelCore::ReleaseServiceThread(std::weak_ptr<Kernel::ServiceThread> service_thread) { 1113void KernelCore::ReleaseServiceThread(std::weak_ptr<Kernel::ServiceThread> service_thread) {
1069 impl->ReleaseServiceThread(service_thread); 1114 impl->ReleaseServiceThread(service_thread);
1070} 1115}
diff --git a/src/core/hle/kernel/kernel.h b/src/core/hle/kernel/kernel.h
index 4c68e96df..12e44b8a5 100644
--- a/src/core/hle/kernel/kernel.h
+++ b/src/core/hle/kernel/kernel.h
@@ -195,6 +195,14 @@ public:
195 /// Opens a port to a service previously registered with RegisterNamedService. 195 /// Opens a port to a service previously registered with RegisterNamedService.
196 KClientPort* CreateNamedServicePort(std::string name); 196 KClientPort* CreateNamedServicePort(std::string name);
197 197
198 /// Registers a server session or port with the gobal emulation state, to be freed on shutdown.
199 /// This is necessary because we do not emulate processes for HLE sessions and ports.
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
198 /// Registers all kernel objects with the global emulation state, this is purely for tracking 206 /// Registers all kernel objects with the global emulation state, this is purely for tracking
199 /// leaks after emulation has been shutdown. 207 /// leaks after emulation has been shutdown.
200 void RegisterKernelObject(KAutoObject* object); 208 void RegisterKernelObject(KAutoObject* object);
@@ -256,6 +264,12 @@ public:
256 /// Gets the shared memory object for Time services. 264 /// Gets the shared memory object for Time services.
257 const Kernel::KSharedMemory& GetTimeSharedMem() const; 265 const Kernel::KSharedMemory& GetTimeSharedMem() const;
258 266
267 /// Gets the shared memory object for HIDBus services.
268 Kernel::KSharedMemory& GetHidBusSharedMem();
269
270 /// Gets the shared memory object for HIDBus services.
271 const Kernel::KSharedMemory& GetHidBusSharedMem() const;
272
259 /// Suspend/unsuspend the OS. 273 /// Suspend/unsuspend the OS.
260 void Suspend(bool in_suspention); 274 void Suspend(bool in_suspention);
261 275
@@ -271,9 +285,11 @@ public:
271 void ExitSVCProfile(); 285 void ExitSVCProfile();
272 286
273 /** 287 /**
274 * Creates an HLE service thread, which are used to execute service routines asynchronously. 288 * Creates a host thread to execute HLE service requests, which are used to execute service
275 * While these are allocated per ServerSession, these need to be owned and managed outside 289 * routines asynchronously. While these are allocated per ServerSession, these need to be owned
276 * of ServerSession to avoid a circular dependency. 290 * and managed outside of ServerSession to avoid a circular dependency. In general, most
291 * services can just use the default service thread, and not need their own host service thread.
292 * See GetDefaultServiceThread.
277 * @param name String name for the ServerSession creating this thread, used for debug 293 * @param name String name for the ServerSession creating this thread, used for debug
278 * purposes. 294 * purposes.
279 * @returns The a weak pointer newly created service thread. 295 * @returns The a weak pointer newly created service thread.
@@ -281,6 +297,14 @@ public:
281 std::weak_ptr<Kernel::ServiceThread> CreateServiceThread(const std::string& name); 297 std::weak_ptr<Kernel::ServiceThread> CreateServiceThread(const std::string& name);
282 298
283 /** 299 /**
300 * Gets the default host service thread, which executes HLE service requests. Unless service
301 * requests need to block on the host, the default service thread should be used in favor of
302 * creating a new service thread.
303 * @returns The a weak pointer for the default service thread.
304 */
305 std::weak_ptr<Kernel::ServiceThread> GetDefaultServiceThread() const;
306
307 /**
284 * Releases a HLE service thread, instructing KernelCore to free it. This should be called when 308 * Releases a HLE service thread, instructing KernelCore to free it. This should be called when
285 * the ServerSession associated with the thread is destroyed. 309 * the ServerSession associated with the thread is destroyed.
286 * @param service_thread Service thread to release. 310 * @param service_thread Service thread to release.
diff --git a/src/core/hle/kernel/physical_core.cpp b/src/core/hle/kernel/physical_core.cpp
index 7477668e4..cc49e8c7e 100644
--- a/src/core/hle/kernel/physical_core.cpp
+++ b/src/core/hle/kernel/physical_core.cpp
@@ -2,7 +2,6 @@
2// Licensed under GPLv2 or any later version 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include "common/spin_lock.h"
6#include "core/arm/cpu_interrupt_handler.h" 5#include "core/arm/cpu_interrupt_handler.h"
7#include "core/arm/dynarmic/arm_dynarmic_32.h" 6#include "core/arm/dynarmic/arm_dynarmic_32.h"
8#include "core/arm/dynarmic/arm_dynarmic_64.h" 7#include "core/arm/dynarmic/arm_dynarmic_64.h"
@@ -16,7 +15,7 @@ namespace Kernel {
16PhysicalCore::PhysicalCore(std::size_t core_index_, Core::System& system_, KScheduler& scheduler_, 15PhysicalCore::PhysicalCore(std::size_t core_index_, Core::System& system_, KScheduler& scheduler_,
17 Core::CPUInterrupts& interrupts_) 16 Core::CPUInterrupts& interrupts_)
18 : core_index{core_index_}, system{system_}, scheduler{scheduler_}, 17 : core_index{core_index_}, system{system_}, scheduler{scheduler_},
19 interrupts{interrupts_}, guard{std::make_unique<Common::SpinLock>()} { 18 interrupts{interrupts_}, guard{std::make_unique<std::mutex>()} {
20#ifdef ARCHITECTURE_x86_64 19#ifdef ARCHITECTURE_x86_64
21 // TODO(bunnei): Initialization relies on a core being available. We may later replace this with 20 // TODO(bunnei): Initialization relies on a core being available. We may later replace this with
22 // a 32-bit instance of Dynarmic. This should be abstracted out to a CPU manager. 21 // a 32-bit instance of Dynarmic. This should be abstracted out to a CPU manager.
@@ -58,6 +57,7 @@ bool PhysicalCore::IsInterrupted() const {
58void PhysicalCore::Interrupt() { 57void PhysicalCore::Interrupt() {
59 guard->lock(); 58 guard->lock();
60 interrupts[core_index].SetInterrupt(true); 59 interrupts[core_index].SetInterrupt(true);
60 arm_interface->SignalInterrupt();
61 guard->unlock(); 61 guard->unlock();
62} 62}
63 63
diff --git a/src/core/hle/kernel/physical_core.h b/src/core/hle/kernel/physical_core.h
index 16a032e89..f2112fc1d 100644
--- a/src/core/hle/kernel/physical_core.h
+++ b/src/core/hle/kernel/physical_core.h
@@ -6,13 +6,10 @@
6 6
7#include <cstddef> 7#include <cstddef>
8#include <memory> 8#include <memory>
9#include <mutex>
9 10
10#include "core/arm/arm_interface.h" 11#include "core/arm/arm_interface.h"
11 12
12namespace Common {
13class SpinLock;
14}
15
16namespace Kernel { 13namespace Kernel {
17class KScheduler; 14class KScheduler;
18} // namespace Kernel 15} // namespace Kernel
@@ -91,7 +88,7 @@ private:
91 Core::System& system; 88 Core::System& system;
92 Kernel::KScheduler& scheduler; 89 Kernel::KScheduler& scheduler;
93 Core::CPUInterrupts& interrupts; 90 Core::CPUInterrupts& interrupts;
94 std::unique_ptr<Common::SpinLock> guard; 91 std::unique_ptr<std::mutex> guard;
95 std::unique_ptr<Core::ARM_Interface> arm_interface; 92 std::unique_ptr<Core::ARM_Interface> arm_interface;
96}; 93};
97 94
diff --git a/src/core/hle/kernel/svc.cpp b/src/core/hle/kernel/svc.cpp
index 839171e85..0c86435b5 100644
--- a/src/core/hle/kernel/svc.cpp
+++ b/src/core/hle/kernel/svc.cpp
@@ -1362,8 +1362,11 @@ static ResultCode MapProcessMemory(Core::System& system, VAddr dst_address, Hand
1362 ResultInvalidMemoryRegion); 1362 ResultInvalidMemoryRegion);
1363 1363
1364 // Create a new page group. 1364 // Create a new page group.
1365 KMemoryInfo kBlockInfo = dst_pt.QueryInfo(dst_address); 1365 KPageLinkedList pg;
1366 KPageLinkedList pg(kBlockInfo.GetAddress(), kBlockInfo.GetNumPages()); 1366 R_TRY(src_pt.MakeAndOpenPageGroup(
1367 std::addressof(pg), src_address, size / PageSize, KMemoryState::FlagCanMapProcess,
1368 KMemoryState::FlagCanMapProcess, KMemoryPermission::None, KMemoryPermission::None,
1369 KMemoryAttribute::All, KMemoryAttribute::None));
1367 1370
1368 // Map the group. 1371 // Map the group.
1369 R_TRY(dst_pt.MapPages(dst_address, pg, KMemoryState::SharedCode, 1372 R_TRY(dst_pt.MapPages(dst_address, pg, KMemoryState::SharedCode,
@@ -1408,8 +1411,8 @@ static ResultCode UnmapProcessMemory(Core::System& system, VAddr dst_address, Ha
1408} 1411}
1409 1412
1410static ResultCode CreateCodeMemory(Core::System& system, Handle* out, VAddr address, size_t size) { 1413static ResultCode CreateCodeMemory(Core::System& system, Handle* out, VAddr address, size_t size) {
1411 LOG_TRACE(Kernel_SVC, "called, handle_out={}, address=0x{:X}, size=0x{:X}", 1414 LOG_TRACE(Kernel_SVC, "called, address=0x{:X}, size=0x{:X}", address, size);
1412 static_cast<void*>(out), address, size); 1415
1413 // Get kernel instance. 1416 // Get kernel instance.
1414 auto& kernel = system.Kernel(); 1417 auto& kernel = system.Kernel();
1415 1418
@@ -1664,7 +1667,7 @@ static ResultCode UnmapProcessCodeMemory(Core::System& system, Handle process_ha
1664 return ResultInvalidAddress; 1667 return ResultInvalidAddress;
1665 } 1668 }
1666 1669
1667 if (size == 0 || Common::Is4KBAligned(size)) { 1670 if (size == 0 || !Common::Is4KBAligned(size)) {
1668 LOG_ERROR(Kernel_SVC, "Size is zero or not page-aligned (size=0x{:016X}).", size); 1671 LOG_ERROR(Kernel_SVC, "Size is zero or not page-aligned (size=0x{:016X}).", size);
1669 return ResultInvalidSize; 1672 return ResultInvalidSize;
1670 } 1673 }
@@ -1710,7 +1713,8 @@ static ResultCode UnmapProcessCodeMemory(Core::System& system, Handle process_ha
1710 return ResultInvalidMemoryRegion; 1713 return ResultInvalidMemoryRegion;
1711 } 1714 }
1712 1715
1713 return page_table.UnmapCodeMemory(dst_address, src_address, size); 1716 return page_table.UnmapCodeMemory(dst_address, src_address, size,
1717 KPageTable::ICacheInvalidationStrategy::InvalidateAll);
1714} 1718}
1715 1719
1716/// Exits the current process 1720/// Exits the current process
diff --git a/src/core/hle/kernel/time_manager.cpp b/src/core/hle/kernel/time_manager.cpp
index aa985d820..5b8fe8eae 100644
--- a/src/core/hle/kernel/time_manager.cpp
+++ b/src/core/hle/kernel/time_manager.cpp
@@ -24,7 +24,7 @@ TimeManager::TimeManager(Core::System& system_) : system{system_} {
24} 24}
25 25
26void TimeManager::ScheduleTimeEvent(KThread* thread, s64 nanoseconds) { 26void TimeManager::ScheduleTimeEvent(KThread* thread, s64 nanoseconds) {
27 std::lock_guard lock{mutex}; 27 std::scoped_lock lock{mutex};
28 if (nanoseconds > 0) { 28 if (nanoseconds > 0) {
29 ASSERT(thread); 29 ASSERT(thread);
30 ASSERT(thread->GetState() != ThreadState::Runnable); 30 ASSERT(thread->GetState() != ThreadState::Runnable);
@@ -35,7 +35,7 @@ void TimeManager::ScheduleTimeEvent(KThread* thread, s64 nanoseconds) {
35} 35}
36 36
37void TimeManager::UnscheduleTimeEvent(KThread* thread) { 37void TimeManager::UnscheduleTimeEvent(KThread* thread) {
38 std::lock_guard lock{mutex}; 38 std::scoped_lock lock{mutex};
39 system.CoreTiming().UnscheduleEvent(time_manager_event_type, 39 system.CoreTiming().UnscheduleEvent(time_manager_event_type,
40 reinterpret_cast<uintptr_t>(thread)); 40 reinterpret_cast<uintptr_t>(thread));
41} 41}
diff --git a/src/core/hle/service/am/am.cpp b/src/core/hle/service/am/am.cpp
index 420de3c54..4d7e5ecd3 100644
--- a/src/core/hle/service/am/am.cpp
+++ b/src/core/hle/service/am/am.cpp
@@ -1337,7 +1337,7 @@ IApplicationFunctions::IApplicationFunctions(Core::System& system_)
1337 {200, nullptr, "GetLastApplicationExitReason"}, 1337 {200, nullptr, "GetLastApplicationExitReason"},
1338 {500, nullptr, "StartContinuousRecordingFlushForDebug"}, 1338 {500, nullptr, "StartContinuousRecordingFlushForDebug"},
1339 {1000, nullptr, "CreateMovieMaker"}, 1339 {1000, nullptr, "CreateMovieMaker"},
1340 {1001, nullptr, "PrepareForJit"}, 1340 {1001, &IApplicationFunctions::PrepareForJit, "PrepareForJit"},
1341 }; 1341 };
1342 // clang-format on 1342 // clang-format on
1343 1343
@@ -1787,6 +1787,13 @@ void IApplicationFunctions::GetHealthWarningDisappearedSystemEvent(Kernel::HLERe
1787 rb.PushCopyObjects(health_warning_disappeared_system_event->GetReadableEvent()); 1787 rb.PushCopyObjects(health_warning_disappeared_system_event->GetReadableEvent());
1788} 1788}
1789 1789
1790void IApplicationFunctions::PrepareForJit(Kernel::HLERequestContext& ctx) {
1791 LOG_WARNING(Service_AM, "(STUBBED) called");
1792
1793 IPC::ResponseBuilder rb{ctx, 2};
1794 rb.Push(ResultSuccess);
1795}
1796
1790void InstallInterfaces(SM::ServiceManager& service_manager, NVFlinger::NVFlinger& nvflinger, 1797void InstallInterfaces(SM::ServiceManager& service_manager, NVFlinger::NVFlinger& nvflinger,
1791 Core::System& system) { 1798 Core::System& system) {
1792 auto message_queue = std::make_shared<AppletMessageQueue>(system); 1799 auto message_queue = std::make_shared<AppletMessageQueue>(system);
diff --git a/src/core/hle/service/am/am.h b/src/core/hle/service/am/am.h
index fdd937b82..11a3c0459 100644
--- a/src/core/hle/service/am/am.h
+++ b/src/core/hle/service/am/am.h
@@ -336,6 +336,7 @@ private:
336 void TryPopFromFriendInvitationStorageChannel(Kernel::HLERequestContext& ctx); 336 void TryPopFromFriendInvitationStorageChannel(Kernel::HLERequestContext& ctx);
337 void GetNotificationStorageChannelEvent(Kernel::HLERequestContext& ctx); 337 void GetNotificationStorageChannelEvent(Kernel::HLERequestContext& ctx);
338 void GetHealthWarningDisappearedSystemEvent(Kernel::HLERequestContext& ctx); 338 void GetHealthWarningDisappearedSystemEvent(Kernel::HLERequestContext& ctx);
339 void PrepareForJit(Kernel::HLERequestContext& ctx);
339 340
340 KernelHelpers::ServiceContext service_context; 341 KernelHelpers::ServiceContext service_context;
341 342
diff --git a/src/core/hle/service/am/applets/applet_mii.cpp b/src/core/hle/service/am/applets/applet_mii.cpp
deleted file mode 100644
index facede97e..000000000
--- a/src/core/hle/service/am/applets/applet_mii.cpp
+++ /dev/null
@@ -1,100 +0,0 @@
1// Copyright 2022 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include "common/assert.h"
6#include "common/logging/log.h"
7#include "core/core.h"
8#include "core/frontend/applets/mii.h"
9#include "core/hle/service/am/am.h"
10#include "core/hle/service/am/applets/applet_mii.h"
11
12namespace Service::AM::Applets {
13
14Mii::Mii(Core::System& system_, LibraryAppletMode applet_mode_,
15 const Core::Frontend::MiiApplet& frontend_)
16 : Applet{system_, applet_mode_}, frontend{frontend_}, system{system_} {}
17
18Mii::~Mii() = default;
19
20void Mii::Initialize() {
21 is_complete = false;
22
23 const auto storage = broker.PopNormalDataToApplet();
24 ASSERT(storage != nullptr);
25
26 const auto data = storage->GetData();
27 ASSERT(data.size() == sizeof(MiiAppletInput));
28
29 std::memcpy(&input_data, data.data(), sizeof(MiiAppletInput));
30}
31
32bool Mii::TransactionComplete() const {
33 return is_complete;
34}
35
36ResultCode Mii::GetStatus() const {
37 return ResultSuccess;
38}
39
40void Mii::ExecuteInteractive() {
41 UNREACHABLE_MSG("Unexpected interactive applet data!");
42}
43
44void Mii::Execute() {
45 if (is_complete) {
46 return;
47 }
48
49 const auto callback = [this](const Core::Frontend::MiiParameters& parameters) {
50 DisplayCompleted(parameters);
51 };
52
53 switch (input_data.applet_mode) {
54 case MiiAppletMode::ShowMiiEdit: {
55 Service::Mii::MiiManager manager;
56 Core::Frontend::MiiParameters params{
57 .is_editable = false,
58 .mii_data = input_data.mii_char_info.mii_data,
59 };
60 frontend.ShowMii(params, callback);
61 break;
62 }
63 case MiiAppletMode::EditMii: {
64 Service::Mii::MiiManager manager;
65 Core::Frontend::MiiParameters params{
66 .is_editable = true,
67 .mii_data = input_data.mii_char_info.mii_data,
68 };
69 frontend.ShowMii(params, callback);
70 break;
71 }
72 case MiiAppletMode::CreateMii: {
73 Service::Mii::MiiManager manager;
74 Core::Frontend::MiiParameters params{
75 .is_editable = true,
76 .mii_data = manager.BuildDefault(0),
77 };
78 frontend.ShowMii(params, callback);
79 break;
80 }
81 default:
82 UNIMPLEMENTED_MSG("Unimplemented LibAppletMiiEdit mode={:02X}!", input_data.applet_mode);
83 }
84}
85
86void Mii::DisplayCompleted(const Core::Frontend::MiiParameters& parameters) {
87 is_complete = true;
88
89 std::vector<u8> reply(sizeof(AppletOutputForCharInfoEditing));
90 output_data = {
91 .result = ResultSuccess,
92 .mii_data = parameters.mii_data,
93 };
94
95 std::memcpy(reply.data(), &output_data, sizeof(AppletOutputForCharInfoEditing));
96 broker.PushNormalDataFromApplet(std::make_shared<IStorage>(system, std::move(reply)));
97 broker.SignalStateChanged();
98}
99
100} // namespace Service::AM::Applets
diff --git a/src/core/hle/service/am/applets/applet_mii.h b/src/core/hle/service/am/applets/applet_mii.h
deleted file mode 100644
index 42326bfc2..000000000
--- a/src/core/hle/service/am/applets/applet_mii.h
+++ /dev/null
@@ -1,90 +0,0 @@
1// Copyright 2022 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <array>
8
9#include "core/hle/result.h"
10#include "core/hle/service/am/applets/applets.h"
11#include "core/hle/service/mii/mii_manager.h"
12
13namespace Core {
14class System;
15}
16
17namespace Service::AM::Applets {
18
19// This is nn::mii::AppletMode
20enum class MiiAppletMode : u32 {
21 ShowMiiEdit = 0,
22 AppendMii = 1,
23 AppendMiiImage = 2,
24 UpdateMiiImage = 3,
25 CreateMii = 4,
26 EditMii = 5,
27};
28
29struct MiiCharInfo {
30 Service::Mii::MiiInfo mii_data{};
31 INSERT_PADDING_BYTES(0x28);
32};
33static_assert(sizeof(MiiCharInfo) == 0x80, "MiiCharInfo has incorrect size.");
34
35// This is nn::mii::AppletInput
36struct MiiAppletInput {
37 s32 version{};
38 MiiAppletMode applet_mode{};
39 u32 special_mii_key_code{};
40 union {
41 std::array<Common::UUID, 8> valid_uuid;
42 MiiCharInfo mii_char_info;
43 };
44 Common::UUID used_uuid;
45 INSERT_PADDING_BYTES(0x64);
46};
47static_assert(sizeof(MiiAppletInput) == 0x100, "MiiAppletInput has incorrect size.");
48
49// This is nn::mii::AppletOutput
50struct MiiAppletOutput {
51 ResultCode result{ResultSuccess};
52 s32 index{};
53 INSERT_PADDING_BYTES(0x18);
54};
55static_assert(sizeof(MiiAppletOutput) == 0x20, "MiiAppletOutput has incorrect size.");
56
57// This is nn::mii::AppletOutputForCharInfoEditing
58struct AppletOutputForCharInfoEditing {
59 ResultCode result{ResultSuccess};
60 Service::Mii::MiiInfo mii_data{};
61 INSERT_PADDING_BYTES(0x24);
62};
63static_assert(sizeof(AppletOutputForCharInfoEditing) == 0x80,
64 "AppletOutputForCharInfoEditing has incorrect size.");
65
66class Mii final : public Applet {
67public:
68 explicit Mii(Core::System& system_, LibraryAppletMode applet_mode_,
69 const Core::Frontend::MiiApplet& frontend_);
70 ~Mii() override;
71
72 void Initialize() override;
73
74 bool TransactionComplete() const override;
75 ResultCode GetStatus() const override;
76 void ExecuteInteractive() override;
77 void Execute() override;
78
79 void DisplayCompleted(const Core::Frontend::MiiParameters& parameters);
80
81private:
82 const Core::Frontend::MiiApplet& frontend;
83 MiiAppletInput input_data{};
84 AppletOutputForCharInfoEditing output_data{};
85
86 bool is_complete = false;
87 Core::System& system;
88};
89
90} // namespace Service::AM::Applets
diff --git a/src/core/hle/service/am/applets/applet_mii_edit.cpp b/src/core/hle/service/am/applets/applet_mii_edit.cpp
new file mode 100644
index 000000000..8fbde1be4
--- /dev/null
+++ b/src/core/hle/service/am/applets/applet_mii_edit.cpp
@@ -0,0 +1,139 @@
1// Copyright 2022 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include "common/assert.h"
6#include "common/logging/log.h"
7#include "core/core.h"
8#include "core/frontend/applets/mii_edit.h"
9#include "core/hle/service/am/am.h"
10#include "core/hle/service/am/applets/applet_mii_edit.h"
11#include "core/hle/service/mii/mii_manager.h"
12
13namespace Service::AM::Applets {
14
15MiiEdit::MiiEdit(Core::System& system_, LibraryAppletMode applet_mode_,
16 const Core::Frontend::MiiEditApplet& frontend_)
17 : Applet{system_, applet_mode_}, frontend{frontend_}, system{system_} {}
18
19MiiEdit::~MiiEdit() = default;
20
21void MiiEdit::Initialize() {
22 // Note: MiiEdit is not initialized with common arguments.
23 // Instead, it is initialized by an AppletInput storage with size 0x100 bytes.
24 // Do NOT call Applet::Initialize() here.
25
26 const auto storage = broker.PopNormalDataToApplet();
27 ASSERT(storage != nullptr);
28
29 const auto applet_input_data = storage->GetData();
30 ASSERT(applet_input_data.size() >= sizeof(MiiEditAppletInputCommon));
31
32 std::memcpy(&applet_input_common, applet_input_data.data(), sizeof(MiiEditAppletInputCommon));
33
34 LOG_INFO(Service_AM,
35 "Initializing MiiEdit Applet with MiiEditAppletVersion={} and MiiEditAppletMode={}",
36 applet_input_common.version, applet_input_common.applet_mode);
37
38 switch (applet_input_common.version) {
39 case MiiEditAppletVersion::Version3:
40 ASSERT(applet_input_data.size() ==
41 sizeof(MiiEditAppletInputCommon) + sizeof(MiiEditAppletInputV3));
42 std::memcpy(&applet_input_v3, applet_input_data.data() + sizeof(MiiEditAppletInputCommon),
43 sizeof(MiiEditAppletInputV3));
44 break;
45 case MiiEditAppletVersion::Version4:
46 ASSERT(applet_input_data.size() ==
47 sizeof(MiiEditAppletInputCommon) + sizeof(MiiEditAppletInputV4));
48 std::memcpy(&applet_input_v4, applet_input_data.data() + sizeof(MiiEditAppletInputCommon),
49 sizeof(MiiEditAppletInputV4));
50 break;
51 default:
52 UNIMPLEMENTED_MSG("Unknown MiiEditAppletVersion={} with size={}",
53 applet_input_common.version, applet_input_data.size());
54 ASSERT(applet_input_data.size() >=
55 sizeof(MiiEditAppletInputCommon) + sizeof(MiiEditAppletInputV4));
56 std::memcpy(&applet_input_v4, applet_input_data.data() + sizeof(MiiEditAppletInputCommon),
57 sizeof(MiiEditAppletInputV4));
58 break;
59 }
60}
61
62bool MiiEdit::TransactionComplete() const {
63 return is_complete;
64}
65
66ResultCode MiiEdit::GetStatus() const {
67 return ResultSuccess;
68}
69
70void MiiEdit::ExecuteInteractive() {
71 UNREACHABLE_MSG("Attempted to call interactive execution on non-interactive applet.");
72}
73
74void MiiEdit::Execute() {
75 if (is_complete) {
76 return;
77 }
78
79 // This is a default stub for each of the MiiEdit applet modes.
80 switch (applet_input_common.applet_mode) {
81 case MiiEditAppletMode::ShowMiiEdit:
82 case MiiEditAppletMode::AppendMii:
83 case MiiEditAppletMode::AppendMiiImage:
84 case MiiEditAppletMode::UpdateMiiImage:
85 MiiEditOutput(MiiEditResult::Success, 0);
86 break;
87 case MiiEditAppletMode::CreateMii:
88 case MiiEditAppletMode::EditMii: {
89 Service::Mii::MiiManager mii_manager;
90
91 const MiiEditCharInfo char_info{
92 .mii_info{applet_input_common.applet_mode == MiiEditAppletMode::EditMii
93 ? applet_input_v4.char_info.mii_info
94 : mii_manager.BuildDefault(0)},
95 };
96
97 MiiEditOutputForCharInfoEditing(MiiEditResult::Success, char_info);
98 break;
99 }
100 default:
101 UNIMPLEMENTED_MSG("Unknown MiiEditAppletMode={}", applet_input_common.applet_mode);
102
103 MiiEditOutput(MiiEditResult::Success, 0);
104 break;
105 }
106}
107
108void MiiEdit::MiiEditOutput(MiiEditResult result, s32 index) {
109 const MiiEditAppletOutput applet_output{
110 .result{result},
111 .index{index},
112 };
113
114 std::vector<u8> out_data(sizeof(MiiEditAppletOutput));
115 std::memcpy(out_data.data(), &applet_output, sizeof(MiiEditAppletOutput));
116
117 is_complete = true;
118
119 broker.PushNormalDataFromApplet(std::make_shared<IStorage>(system, std::move(out_data)));
120 broker.SignalStateChanged();
121}
122
123void MiiEdit::MiiEditOutputForCharInfoEditing(MiiEditResult result,
124 const MiiEditCharInfo& char_info) {
125 const MiiEditAppletOutputForCharInfoEditing applet_output{
126 .result{result},
127 .char_info{char_info},
128 };
129
130 std::vector<u8> out_data(sizeof(MiiEditAppletOutputForCharInfoEditing));
131 std::memcpy(out_data.data(), &applet_output, sizeof(MiiEditAppletOutputForCharInfoEditing));
132
133 is_complete = true;
134
135 broker.PushNormalDataFromApplet(std::make_shared<IStorage>(system, std::move(out_data)));
136 broker.SignalStateChanged();
137}
138
139} // namespace Service::AM::Applets
diff --git a/src/core/hle/service/am/applets/applet_mii_edit.h b/src/core/hle/service/am/applets/applet_mii_edit.h
new file mode 100644
index 000000000..e9ca0e2af
--- /dev/null
+++ b/src/core/hle/service/am/applets/applet_mii_edit.h
@@ -0,0 +1,45 @@
1// Copyright 2022 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include "core/hle/result.h"
8#include "core/hle/service/am/applets/applet_mii_edit_types.h"
9#include "core/hle/service/am/applets/applets.h"
10
11namespace Core {
12class System;
13} // namespace Core
14
15namespace Service::AM::Applets {
16
17class MiiEdit final : public Applet {
18public:
19 explicit MiiEdit(Core::System& system_, LibraryAppletMode applet_mode_,
20 const Core::Frontend::MiiEditApplet& frontend_);
21 ~MiiEdit() override;
22
23 void Initialize() override;
24
25 bool TransactionComplete() const override;
26 ResultCode GetStatus() const override;
27 void ExecuteInteractive() override;
28 void Execute() override;
29
30 void MiiEditOutput(MiiEditResult result, s32 index);
31
32 void MiiEditOutputForCharInfoEditing(MiiEditResult result, const MiiEditCharInfo& char_info);
33
34private:
35 const Core::Frontend::MiiEditApplet& frontend;
36 Core::System& system;
37
38 MiiEditAppletInputCommon applet_input_common{};
39 MiiEditAppletInputV3 applet_input_v3{};
40 MiiEditAppletInputV4 applet_input_v4{};
41
42 bool is_complete{false};
43};
44
45} // namespace Service::AM::Applets
diff --git a/src/core/hle/service/am/applets/applet_mii_edit_types.h b/src/core/hle/service/am/applets/applet_mii_edit_types.h
new file mode 100644
index 000000000..70dea0007
--- /dev/null
+++ b/src/core/hle/service/am/applets/applet_mii_edit_types.h
@@ -0,0 +1,83 @@
1// Copyright 2022 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <array>
8
9#include "common/common_funcs.h"
10#include "common/common_types.h"
11#include "core/hle/service/mii/types.h"
12
13namespace Service::AM::Applets {
14
15enum class MiiEditAppletVersion : s32 {
16 Version3 = 0x3, // 1.0.0 - 10.1.1
17 Version4 = 0x4, // 10.2.0+
18};
19
20// This is nn::mii::AppletMode
21enum class MiiEditAppletMode : u32 {
22 ShowMiiEdit = 0,
23 AppendMii = 1,
24 AppendMiiImage = 2,
25 UpdateMiiImage = 3,
26 CreateMii = 4,
27 EditMii = 5,
28};
29
30enum class MiiEditResult : u32 {
31 Success,
32 Cancel,
33};
34
35struct MiiEditCharInfo {
36 Service::Mii::MiiInfo mii_info{};
37};
38static_assert(sizeof(MiiEditCharInfo) == 0x58, "MiiEditCharInfo has incorrect size.");
39
40struct MiiEditAppletInputCommon {
41 MiiEditAppletVersion version{};
42 MiiEditAppletMode applet_mode{};
43};
44static_assert(sizeof(MiiEditAppletInputCommon) == 0x8,
45 "MiiEditAppletInputCommon has incorrect size.");
46
47struct MiiEditAppletInputV3 {
48 u32 special_mii_key_code{};
49 std::array<Common::UUID, 8> valid_uuids{};
50 Common::UUID used_uuid{};
51 INSERT_PADDING_BYTES(0x64);
52};
53static_assert(sizeof(MiiEditAppletInputV3) == 0x100 - sizeof(MiiEditAppletInputCommon),
54 "MiiEditAppletInputV3 has incorrect size.");
55
56struct MiiEditAppletInputV4 {
57 u32 special_mii_key_code{};
58 MiiEditCharInfo char_info{};
59 INSERT_PADDING_BYTES(0x28);
60 Common::UUID used_uuid{};
61 INSERT_PADDING_BYTES(0x64);
62};
63static_assert(sizeof(MiiEditAppletInputV4) == 0x100 - sizeof(MiiEditAppletInputCommon),
64 "MiiEditAppletInputV4 has incorrect size.");
65
66// This is nn::mii::AppletOutput
67struct MiiEditAppletOutput {
68 MiiEditResult result{};
69 s32 index{};
70 INSERT_PADDING_BYTES(0x18);
71};
72static_assert(sizeof(MiiEditAppletOutput) == 0x20, "MiiEditAppletOutput has incorrect size.");
73
74// This is nn::mii::AppletOutputForCharInfoEditing
75struct MiiEditAppletOutputForCharInfoEditing {
76 MiiEditResult result{};
77 MiiEditCharInfo char_info{};
78 INSERT_PADDING_BYTES(0x24);
79};
80static_assert(sizeof(MiiEditAppletOutputForCharInfoEditing) == 0x80,
81 "MiiEditAppletOutputForCharInfoEditing has incorrect size.");
82
83} // namespace Service::AM::Applets
diff --git a/src/core/hle/service/am/applets/applet_software_keyboard.cpp b/src/core/hle/service/am/applets/applet_software_keyboard.cpp
index f38f53f69..ee669686c 100644
--- a/src/core/hle/service/am/applets/applet_software_keyboard.cpp
+++ b/src/core/hle/service/am/applets/applet_software_keyboard.cpp
@@ -226,7 +226,7 @@ void SoftwareKeyboard::InitializeForeground() {
226 ASSERT(work_buffer_storage != nullptr); 226 ASSERT(work_buffer_storage != nullptr);
227 227
228 if (swkbd_config_common.initial_string_length == 0) { 228 if (swkbd_config_common.initial_string_length == 0) {
229 InitializeFrontendKeyboard(); 229 InitializeFrontendNormalKeyboard();
230 return; 230 return;
231 } 231 }
232 232
@@ -243,7 +243,7 @@ void SoftwareKeyboard::InitializeForeground() {
243 243
244 LOG_DEBUG(Service_AM, "\nInitial Text: {}", Common::UTF16ToUTF8(initial_text)); 244 LOG_DEBUG(Service_AM, "\nInitial Text: {}", Common::UTF16ToUTF8(initial_text));
245 245
246 InitializeFrontendKeyboard(); 246 InitializeFrontendNormalKeyboard();
247} 247}
248 248
249void SoftwareKeyboard::InitializeBackground(LibraryAppletMode library_applet_mode) { 249void SoftwareKeyboard::InitializeBackground(LibraryAppletMode library_applet_mode) {
@@ -480,129 +480,173 @@ void SoftwareKeyboard::ChangeState(SwkbdState state) {
480 ReplyDefault(); 480 ReplyDefault();
481} 481}
482 482
483void SoftwareKeyboard::InitializeFrontendKeyboard() { 483void SoftwareKeyboard::InitializeFrontendNormalKeyboard() {
484 if (is_background) { 484 std::u16string ok_text = Common::UTF16StringFromFixedZeroTerminatedBuffer(
485 const auto& appear_arg = swkbd_calc_arg.appear_arg; 485 swkbd_config_common.ok_text.data(), swkbd_config_common.ok_text.size());
486 486
487 std::u16string ok_text = Common::UTF16StringFromFixedZeroTerminatedBuffer( 487 std::u16string header_text = Common::UTF16StringFromFixedZeroTerminatedBuffer(
488 appear_arg.ok_text.data(), appear_arg.ok_text.size()); 488 swkbd_config_common.header_text.data(), swkbd_config_common.header_text.size());
489 489
490 const u32 max_text_length = 490 std::u16string sub_text = Common::UTF16StringFromFixedZeroTerminatedBuffer(
491 appear_arg.max_text_length > 0 && appear_arg.max_text_length <= DEFAULT_MAX_TEXT_LENGTH 491 swkbd_config_common.sub_text.data(), swkbd_config_common.sub_text.size());
492 ? appear_arg.max_text_length 492
493 : DEFAULT_MAX_TEXT_LENGTH; 493 std::u16string guide_text = Common::UTF16StringFromFixedZeroTerminatedBuffer(
494 494 swkbd_config_common.guide_text.data(), swkbd_config_common.guide_text.size());
495 const u32 min_text_length = 495
496 appear_arg.min_text_length <= max_text_length ? appear_arg.min_text_length : 0; 496 const u32 max_text_length =
497 497 swkbd_config_common.max_text_length > 0 &&
498 const s32 initial_cursor_position = 498 swkbd_config_common.max_text_length <= DEFAULT_MAX_TEXT_LENGTH
499 current_cursor_position > 0 ? current_cursor_position : 0; 499 ? swkbd_config_common.max_text_length
500 500 : DEFAULT_MAX_TEXT_LENGTH;
501 const auto text_draw_type = 501
502 max_text_length <= 32 ? SwkbdTextDrawType::Line : SwkbdTextDrawType::Box; 502 const u32 min_text_length = swkbd_config_common.min_text_length <= max_text_length
503 503 ? swkbd_config_common.min_text_length
504 Core::Frontend::KeyboardInitializeParameters initialize_parameters{ 504 : 0;
505 .ok_text{std::move(ok_text)}, 505
506 .header_text{}, 506 const s32 initial_cursor_position = [this] {
507 .sub_text{}, 507 switch (swkbd_config_common.initial_cursor_position) {
508 .guide_text{}, 508 case SwkbdInitialCursorPosition::Start:
509 .initial_text{current_text}, 509 default:
510 .max_text_length{max_text_length}, 510 return 0;
511 .min_text_length{min_text_length}, 511 case SwkbdInitialCursorPosition::End:
512 .initial_cursor_position{initial_cursor_position}, 512 return static_cast<s32>(initial_text.size());
513 .type{appear_arg.type}, 513 }
514 .password_mode{SwkbdPasswordMode::Disabled}, 514 }();
515 .text_draw_type{text_draw_type}, 515
516 .key_disable_flags{appear_arg.key_disable_flags}, 516 const auto text_draw_type = [this, max_text_length] {
517 .use_blur_background{false}, 517 switch (swkbd_config_common.text_draw_type) {
518 .enable_backspace_button{swkbd_calc_arg.enable_backspace_button}, 518 case SwkbdTextDrawType::Line:
519 .enable_return_button{appear_arg.enable_return_button}, 519 default:
520 .disable_cancel_button{appear_arg.disable_cancel_button}, 520 return max_text_length <= 32 ? SwkbdTextDrawType::Line : SwkbdTextDrawType::Box;
521 }; 521 case SwkbdTextDrawType::Box:
522 522 case SwkbdTextDrawType::DownloadCode:
523 frontend.InitializeKeyboard( 523 return swkbd_config_common.text_draw_type;
524 true, std::move(initialize_parameters), {}, 524 }
525 [this](SwkbdReplyType reply_type, std::u16string submitted_text, s32 cursor_position) { 525 }();
526 SubmitTextInline(reply_type, submitted_text, cursor_position); 526
527 }); 527 const auto enable_return_button =
528 } else { 528 text_draw_type == SwkbdTextDrawType::Box ? swkbd_config_common.enable_return_button : false;
529 std::u16string ok_text = Common::UTF16StringFromFixedZeroTerminatedBuffer( 529
530 swkbd_config_common.ok_text.data(), swkbd_config_common.ok_text.size()); 530 const auto disable_cancel_button = swkbd_applet_version >= SwkbdAppletVersion::Version393227
531 531 ? swkbd_config_new.disable_cancel_button
532 std::u16string header_text = Common::UTF16StringFromFixedZeroTerminatedBuffer( 532 : false;
533 swkbd_config_common.header_text.data(), swkbd_config_common.header_text.size()); 533
534 534 Core::Frontend::KeyboardInitializeParameters initialize_parameters{
535 std::u16string sub_text = Common::UTF16StringFromFixedZeroTerminatedBuffer( 535 .ok_text{std::move(ok_text)},
536 swkbd_config_common.sub_text.data(), swkbd_config_common.sub_text.size()); 536 .header_text{std::move(header_text)},
537 537 .sub_text{std::move(sub_text)},
538 std::u16string guide_text = Common::UTF16StringFromFixedZeroTerminatedBuffer( 538 .guide_text{std::move(guide_text)},
539 swkbd_config_common.guide_text.data(), swkbd_config_common.guide_text.size()); 539 .initial_text{initial_text},
540 540 .max_text_length{max_text_length},
541 const u32 max_text_length = 541 .min_text_length{min_text_length},
542 swkbd_config_common.max_text_length > 0 && 542 .initial_cursor_position{initial_cursor_position},
543 swkbd_config_common.max_text_length <= DEFAULT_MAX_TEXT_LENGTH 543 .type{swkbd_config_common.type},
544 ? swkbd_config_common.max_text_length 544 .password_mode{swkbd_config_common.password_mode},
545 : DEFAULT_MAX_TEXT_LENGTH; 545 .text_draw_type{text_draw_type},
546 546 .key_disable_flags{swkbd_config_common.key_disable_flags},
547 const u32 min_text_length = swkbd_config_common.min_text_length <= max_text_length 547 .use_blur_background{swkbd_config_common.use_blur_background},
548 ? swkbd_config_common.min_text_length 548 .enable_backspace_button{true},
549 : 0; 549 .enable_return_button{enable_return_button},
550 550 .disable_cancel_button{disable_cancel_button},
551 const s32 initial_cursor_position = [this] { 551 };
552 switch (swkbd_config_common.initial_cursor_position) { 552
553 case SwkbdInitialCursorPosition::Start: 553 frontend.InitializeKeyboard(
554 default: 554 false, std::move(initialize_parameters),
555 return 0; 555 [this](SwkbdResult result, std::u16string submitted_text, bool confirmed) {
556 case SwkbdInitialCursorPosition::End: 556 SubmitTextNormal(result, submitted_text, confirmed);
557 return static_cast<s32>(initial_text.size()); 557 },
558 } 558 {});
559 }(); 559}
560 560
561 const auto text_draw_type = [this, max_text_length] { 561void SoftwareKeyboard::InitializeFrontendInlineKeyboard(
562 switch (swkbd_config_common.text_draw_type) { 562 Core::Frontend::KeyboardInitializeParameters initialize_parameters) {
563 case SwkbdTextDrawType::Line: 563 frontend.InitializeKeyboard(
564 default: 564 true, std::move(initialize_parameters), {},
565 return max_text_length <= 32 ? SwkbdTextDrawType::Line : SwkbdTextDrawType::Box; 565 [this](SwkbdReplyType reply_type, std::u16string submitted_text, s32 cursor_position) {
566 case SwkbdTextDrawType::Box: 566 SubmitTextInline(reply_type, submitted_text, cursor_position);
567 case SwkbdTextDrawType::DownloadCode: 567 });
568 return swkbd_config_common.text_draw_type; 568}
569 } 569
570 }(); 570void SoftwareKeyboard::InitializeFrontendInlineKeyboardOld() {
571 571 const auto& appear_arg = swkbd_calc_arg_old.appear_arg;
572 const auto enable_return_button = text_draw_type == SwkbdTextDrawType::Box 572
573 ? swkbd_config_common.enable_return_button 573 std::u16string ok_text = Common::UTF16StringFromFixedZeroTerminatedBuffer(
574 : false; 574 appear_arg.ok_text.data(), appear_arg.ok_text.size());
575 575
576 const auto disable_cancel_button = swkbd_applet_version >= SwkbdAppletVersion::Version393227 576 const u32 max_text_length =
577 ? swkbd_config_new.disable_cancel_button 577 appear_arg.max_text_length > 0 && appear_arg.max_text_length <= DEFAULT_MAX_TEXT_LENGTH
578 : false; 578 ? appear_arg.max_text_length
579 579 : DEFAULT_MAX_TEXT_LENGTH;
580 Core::Frontend::KeyboardInitializeParameters initialize_parameters{ 580
581 .ok_text{std::move(ok_text)}, 581 const u32 min_text_length =
582 .header_text{std::move(header_text)}, 582 appear_arg.min_text_length <= max_text_length ? appear_arg.min_text_length : 0;
583 .sub_text{std::move(sub_text)}, 583
584 .guide_text{std::move(guide_text)}, 584 const s32 initial_cursor_position = current_cursor_position > 0 ? current_cursor_position : 0;
585 .initial_text{initial_text}, 585
586 .max_text_length{max_text_length}, 586 const auto text_draw_type =
587 .min_text_length{min_text_length}, 587 max_text_length <= 32 ? SwkbdTextDrawType::Line : SwkbdTextDrawType::Box;
588 .initial_cursor_position{initial_cursor_position}, 588
589 .type{swkbd_config_common.type}, 589 Core::Frontend::KeyboardInitializeParameters initialize_parameters{
590 .password_mode{swkbd_config_common.password_mode}, 590 .ok_text{std::move(ok_text)},
591 .text_draw_type{text_draw_type}, 591 .header_text{},
592 .key_disable_flags{swkbd_config_common.key_disable_flags}, 592 .sub_text{},
593 .use_blur_background{swkbd_config_common.use_blur_background}, 593 .guide_text{},
594 .enable_backspace_button{true}, 594 .initial_text{current_text},
595 .enable_return_button{enable_return_button}, 595 .max_text_length{max_text_length},
596 .disable_cancel_button{disable_cancel_button}, 596 .min_text_length{min_text_length},
597 }; 597 .initial_cursor_position{initial_cursor_position},
598 598 .type{appear_arg.type},
599 frontend.InitializeKeyboard( 599 .password_mode{SwkbdPasswordMode::Disabled},
600 false, std::move(initialize_parameters), 600 .text_draw_type{text_draw_type},
601 [this](SwkbdResult result, std::u16string submitted_text, bool confirmed) { 601 .key_disable_flags{appear_arg.key_disable_flags},
602 SubmitTextNormal(result, submitted_text, confirmed); 602 .use_blur_background{false},
603 }, 603 .enable_backspace_button{swkbd_calc_arg_old.enable_backspace_button},
604 {}); 604 .enable_return_button{appear_arg.enable_return_button},
605 } 605 .disable_cancel_button{appear_arg.disable_cancel_button},
606 };
607
608 InitializeFrontendInlineKeyboard(std::move(initialize_parameters));
609}
610
611void SoftwareKeyboard::InitializeFrontendInlineKeyboardNew() {
612 const auto& appear_arg = swkbd_calc_arg_new.appear_arg;
613
614 std::u16string ok_text = Common::UTF16StringFromFixedZeroTerminatedBuffer(
615 appear_arg.ok_text.data(), appear_arg.ok_text.size());
616
617 const u32 max_text_length =
618 appear_arg.max_text_length > 0 && appear_arg.max_text_length <= DEFAULT_MAX_TEXT_LENGTH
619 ? appear_arg.max_text_length
620 : DEFAULT_MAX_TEXT_LENGTH;
621
622 const u32 min_text_length =
623 appear_arg.min_text_length <= max_text_length ? appear_arg.min_text_length : 0;
624
625 const s32 initial_cursor_position = current_cursor_position > 0 ? current_cursor_position : 0;
626
627 const auto text_draw_type =
628 max_text_length <= 32 ? SwkbdTextDrawType::Line : SwkbdTextDrawType::Box;
629
630 Core::Frontend::KeyboardInitializeParameters initialize_parameters{
631 .ok_text{std::move(ok_text)},
632 .header_text{},
633 .sub_text{},
634 .guide_text{},
635 .initial_text{current_text},
636 .max_text_length{max_text_length},
637 .min_text_length{min_text_length},
638 .initial_cursor_position{initial_cursor_position},
639 .type{appear_arg.type},
640 .password_mode{SwkbdPasswordMode::Disabled},
641 .text_draw_type{text_draw_type},
642 .key_disable_flags{appear_arg.key_disable_flags},
643 .use_blur_background{false},
644 .enable_backspace_button{swkbd_calc_arg_new.enable_backspace_button},
645 .enable_return_button{appear_arg.enable_return_button},
646 .disable_cancel_button{appear_arg.disable_cancel_button},
647 };
648
649 InitializeFrontendInlineKeyboard(std::move(initialize_parameters));
606} 650}
607 651
608void SoftwareKeyboard::ShowNormalKeyboard() { 652void SoftwareKeyboard::ShowNormalKeyboard() {
@@ -614,14 +658,21 @@ void SoftwareKeyboard::ShowTextCheckDialog(SwkbdTextCheckResult text_check_resul
614 frontend.ShowTextCheckDialog(text_check_result, std::move(text_check_message)); 658 frontend.ShowTextCheckDialog(text_check_result, std::move(text_check_message));
615} 659}
616 660
617void SoftwareKeyboard::ShowInlineKeyboard() { 661void SoftwareKeyboard::ShowInlineKeyboard(
662 Core::Frontend::InlineAppearParameters appear_parameters) {
663 frontend.ShowInlineKeyboard(std::move(appear_parameters));
664
665 ChangeState(SwkbdState::InitializedIsShown);
666}
667
668void SoftwareKeyboard::ShowInlineKeyboardOld() {
618 if (swkbd_state != SwkbdState::InitializedIsHidden) { 669 if (swkbd_state != SwkbdState::InitializedIsHidden) {
619 return; 670 return;
620 } 671 }
621 672
622 ChangeState(SwkbdState::InitializedIsAppearing); 673 ChangeState(SwkbdState::InitializedIsAppearing);
623 674
624 const auto& appear_arg = swkbd_calc_arg.appear_arg; 675 const auto& appear_arg = swkbd_calc_arg_old.appear_arg;
625 676
626 const u32 max_text_length = 677 const u32 max_text_length =
627 appear_arg.max_text_length > 0 && appear_arg.max_text_length <= DEFAULT_MAX_TEXT_LENGTH 678 appear_arg.max_text_length > 0 && appear_arg.max_text_length <= DEFAULT_MAX_TEXT_LENGTH
@@ -634,21 +685,54 @@ void SoftwareKeyboard::ShowInlineKeyboard() {
634 Core::Frontend::InlineAppearParameters appear_parameters{ 685 Core::Frontend::InlineAppearParameters appear_parameters{
635 .max_text_length{max_text_length}, 686 .max_text_length{max_text_length},
636 .min_text_length{min_text_length}, 687 .min_text_length{min_text_length},
637 .key_top_scale_x{swkbd_calc_arg.key_top_scale_x}, 688 .key_top_scale_x{swkbd_calc_arg_old.key_top_scale_x},
638 .key_top_scale_y{swkbd_calc_arg.key_top_scale_y}, 689 .key_top_scale_y{swkbd_calc_arg_old.key_top_scale_y},
639 .key_top_translate_x{swkbd_calc_arg.key_top_translate_x}, 690 .key_top_translate_x{swkbd_calc_arg_old.key_top_translate_x},
640 .key_top_translate_y{swkbd_calc_arg.key_top_translate_y}, 691 .key_top_translate_y{swkbd_calc_arg_old.key_top_translate_y},
641 .type{appear_arg.type}, 692 .type{appear_arg.type},
642 .key_disable_flags{appear_arg.key_disable_flags}, 693 .key_disable_flags{appear_arg.key_disable_flags},
643 .key_top_as_floating{swkbd_calc_arg.key_top_as_floating}, 694 .key_top_as_floating{swkbd_calc_arg_old.key_top_as_floating},
644 .enable_backspace_button{swkbd_calc_arg.enable_backspace_button}, 695 .enable_backspace_button{swkbd_calc_arg_old.enable_backspace_button},
645 .enable_return_button{appear_arg.enable_return_button}, 696 .enable_return_button{appear_arg.enable_return_button},
646 .disable_cancel_button{appear_arg.disable_cancel_button}, 697 .disable_cancel_button{appear_arg.disable_cancel_button},
647 }; 698 };
648 699
649 frontend.ShowInlineKeyboard(std::move(appear_parameters)); 700 ShowInlineKeyboard(std::move(appear_parameters));
701}
650 702
651 ChangeState(SwkbdState::InitializedIsShown); 703void SoftwareKeyboard::ShowInlineKeyboardNew() {
704 if (swkbd_state != SwkbdState::InitializedIsHidden) {
705 return;
706 }
707
708 ChangeState(SwkbdState::InitializedIsAppearing);
709
710 const auto& appear_arg = swkbd_calc_arg_new.appear_arg;
711
712 const u32 max_text_length =
713 appear_arg.max_text_length > 0 && appear_arg.max_text_length <= DEFAULT_MAX_TEXT_LENGTH
714 ? appear_arg.max_text_length
715 : DEFAULT_MAX_TEXT_LENGTH;
716
717 const u32 min_text_length =
718 appear_arg.min_text_length <= max_text_length ? appear_arg.min_text_length : 0;
719
720 Core::Frontend::InlineAppearParameters appear_parameters{
721 .max_text_length{max_text_length},
722 .min_text_length{min_text_length},
723 .key_top_scale_x{swkbd_calc_arg_new.key_top_scale_x},
724 .key_top_scale_y{swkbd_calc_arg_new.key_top_scale_y},
725 .key_top_translate_x{swkbd_calc_arg_new.key_top_translate_x},
726 .key_top_translate_y{swkbd_calc_arg_new.key_top_translate_y},
727 .type{appear_arg.type},
728 .key_disable_flags{appear_arg.key_disable_flags},
729 .key_top_as_floating{swkbd_calc_arg_new.key_top_as_floating},
730 .enable_backspace_button{swkbd_calc_arg_new.enable_backspace_button},
731 .enable_return_button{appear_arg.enable_return_button},
732 .disable_cancel_button{appear_arg.disable_cancel_button},
733 };
734
735 ShowInlineKeyboard(std::move(appear_parameters));
652} 736}
653 737
654void SoftwareKeyboard::HideInlineKeyboard() { 738void SoftwareKeyboard::HideInlineKeyboard() {
@@ -693,6 +777,8 @@ void SoftwareKeyboard::RequestFinalize(const std::vector<u8>& request_data) {
693 777
694void SoftwareKeyboard::RequestSetUserWordInfo(const std::vector<u8>& request_data) { 778void SoftwareKeyboard::RequestSetUserWordInfo(const std::vector<u8>& request_data) {
695 LOG_WARNING(Service_AM, "SetUserWordInfo is not implemented."); 779 LOG_WARNING(Service_AM, "SetUserWordInfo is not implemented.");
780
781 ReplyReleasedUserWordInfo();
696} 782}
697 783
698void SoftwareKeyboard::RequestSetCustomizeDic(const std::vector<u8>& request_data) { 784void SoftwareKeyboard::RequestSetCustomizeDic(const std::vector<u8>& request_data) {
@@ -702,53 +788,135 @@ void SoftwareKeyboard::RequestSetCustomizeDic(const std::vector<u8>& request_dat
702void SoftwareKeyboard::RequestCalc(const std::vector<u8>& request_data) { 788void SoftwareKeyboard::RequestCalc(const std::vector<u8>& request_data) {
703 LOG_DEBUG(Service_AM, "Processing Request: Calc"); 789 LOG_DEBUG(Service_AM, "Processing Request: Calc");
704 790
705 ASSERT(request_data.size() == sizeof(SwkbdRequestCommand) + sizeof(SwkbdCalcArg)); 791 ASSERT(request_data.size() >= sizeof(SwkbdRequestCommand) + sizeof(SwkbdCalcArgCommon));
706 792
707 std::memcpy(&swkbd_calc_arg, request_data.data() + sizeof(SwkbdRequestCommand), 793 std::memcpy(&swkbd_calc_arg_common, request_data.data() + sizeof(SwkbdRequestCommand),
708 sizeof(SwkbdCalcArg)); 794 sizeof(SwkbdCalcArgCommon));
795
796 switch (swkbd_calc_arg_common.calc_arg_size) {
797 case sizeof(SwkbdCalcArgCommon) + sizeof(SwkbdCalcArgOld):
798 ASSERT(request_data.size() ==
799 sizeof(SwkbdRequestCommand) + sizeof(SwkbdCalcArgCommon) + sizeof(SwkbdCalcArgOld));
800 std::memcpy(&swkbd_calc_arg_old,
801 request_data.data() + sizeof(SwkbdRequestCommand) + sizeof(SwkbdCalcArgCommon),
802 sizeof(SwkbdCalcArgOld));
803 RequestCalcOld();
804 break;
805 case sizeof(SwkbdCalcArgCommon) + sizeof(SwkbdCalcArgNew):
806 ASSERT(request_data.size() ==
807 sizeof(SwkbdRequestCommand) + sizeof(SwkbdCalcArgCommon) + sizeof(SwkbdCalcArgNew));
808 std::memcpy(&swkbd_calc_arg_new,
809 request_data.data() + sizeof(SwkbdRequestCommand) + sizeof(SwkbdCalcArgCommon),
810 sizeof(SwkbdCalcArgNew));
811 RequestCalcNew();
812 break;
813 default:
814 UNIMPLEMENTED_MSG("Unknown SwkbdCalcArg size={}", swkbd_calc_arg_common.calc_arg_size);
815 ASSERT(request_data.size() >=
816 sizeof(SwkbdRequestCommand) + sizeof(SwkbdCalcArgCommon) + sizeof(SwkbdCalcArgNew));
817 std::memcpy(&swkbd_calc_arg_new,
818 request_data.data() + sizeof(SwkbdRequestCommand) + sizeof(SwkbdCalcArgCommon),
819 sizeof(SwkbdCalcArgNew));
820 RequestCalcNew();
821 break;
822 }
823}
824
825void SoftwareKeyboard::RequestCalcOld() {
826 if (swkbd_calc_arg_common.flags.set_input_text) {
827 current_text = Common::UTF16StringFromFixedZeroTerminatedBuffer(
828 swkbd_calc_arg_old.input_text.data(), swkbd_calc_arg_old.input_text.size());
829 }
830
831 if (swkbd_calc_arg_common.flags.set_cursor_position) {
832 current_cursor_position = swkbd_calc_arg_old.cursor_position;
833 }
834
835 if (swkbd_calc_arg_common.flags.set_utf8_mode) {
836 inline_use_utf8 = swkbd_calc_arg_old.utf8_mode;
837 }
838
839 if (swkbd_state <= SwkbdState::InitializedIsHidden &&
840 swkbd_calc_arg_common.flags.unset_customize_dic) {
841 ReplyUnsetCustomizeDic();
842 }
843
844 if (swkbd_state <= SwkbdState::InitializedIsHidden &&
845 swkbd_calc_arg_common.flags.unset_user_word_info) {
846 ReplyReleasedUserWordInfo();
847 }
848
849 if (swkbd_state == SwkbdState::NotInitialized &&
850 swkbd_calc_arg_common.flags.set_initialize_arg) {
851 InitializeFrontendInlineKeyboardOld();
852
853 ChangeState(SwkbdState::InitializedIsHidden);
854
855 ReplyFinishedInitialize();
856 }
857
858 if (!swkbd_calc_arg_common.flags.set_initialize_arg &&
859 (swkbd_calc_arg_common.flags.set_input_text ||
860 swkbd_calc_arg_common.flags.set_cursor_position)) {
861 InlineTextChanged();
862 }
863
864 if (swkbd_state == SwkbdState::InitializedIsHidden && swkbd_calc_arg_common.flags.appear) {
865 ShowInlineKeyboardOld();
866 return;
867 }
868
869 if (swkbd_state == SwkbdState::InitializedIsShown && swkbd_calc_arg_common.flags.disappear) {
870 HideInlineKeyboard();
871 return;
872 }
873}
709 874
710 if (swkbd_calc_arg.flags.set_input_text) { 875void SoftwareKeyboard::RequestCalcNew() {
876 if (swkbd_calc_arg_common.flags.set_input_text) {
711 current_text = Common::UTF16StringFromFixedZeroTerminatedBuffer( 877 current_text = Common::UTF16StringFromFixedZeroTerminatedBuffer(
712 swkbd_calc_arg.input_text.data(), swkbd_calc_arg.input_text.size()); 878 swkbd_calc_arg_new.input_text.data(), swkbd_calc_arg_new.input_text.size());
713 } 879 }
714 880
715 if (swkbd_calc_arg.flags.set_cursor_position) { 881 if (swkbd_calc_arg_common.flags.set_cursor_position) {
716 current_cursor_position = swkbd_calc_arg.cursor_position; 882 current_cursor_position = swkbd_calc_arg_new.cursor_position;
717 } 883 }
718 884
719 if (swkbd_calc_arg.flags.set_utf8_mode) { 885 if (swkbd_calc_arg_common.flags.set_utf8_mode) {
720 inline_use_utf8 = swkbd_calc_arg.utf8_mode; 886 inline_use_utf8 = swkbd_calc_arg_new.utf8_mode;
721 } 887 }
722 888
723 if (swkbd_state <= SwkbdState::InitializedIsHidden && 889 if (swkbd_state <= SwkbdState::InitializedIsHidden &&
724 swkbd_calc_arg.flags.unset_customize_dic) { 890 swkbd_calc_arg_common.flags.unset_customize_dic) {
725 ReplyUnsetCustomizeDic(); 891 ReplyUnsetCustomizeDic();
726 } 892 }
727 893
728 if (swkbd_state <= SwkbdState::InitializedIsHidden && 894 if (swkbd_state <= SwkbdState::InitializedIsHidden &&
729 swkbd_calc_arg.flags.unset_user_word_info) { 895 swkbd_calc_arg_common.flags.unset_user_word_info) {
730 ReplyReleasedUserWordInfo(); 896 ReplyReleasedUserWordInfo();
731 } 897 }
732 898
733 if (swkbd_state == SwkbdState::NotInitialized && swkbd_calc_arg.flags.set_initialize_arg) { 899 if (swkbd_state == SwkbdState::NotInitialized &&
734 InitializeFrontendKeyboard(); 900 swkbd_calc_arg_common.flags.set_initialize_arg) {
901 InitializeFrontendInlineKeyboardNew();
735 902
736 ChangeState(SwkbdState::InitializedIsHidden); 903 ChangeState(SwkbdState::InitializedIsHidden);
737 904
738 ReplyFinishedInitialize(); 905 ReplyFinishedInitialize();
739 } 906 }
740 907
741 if (!swkbd_calc_arg.flags.set_initialize_arg && 908 if (!swkbd_calc_arg_common.flags.set_initialize_arg &&
742 (swkbd_calc_arg.flags.set_input_text || swkbd_calc_arg.flags.set_cursor_position)) { 909 (swkbd_calc_arg_common.flags.set_input_text ||
910 swkbd_calc_arg_common.flags.set_cursor_position)) {
743 InlineTextChanged(); 911 InlineTextChanged();
744 } 912 }
745 913
746 if (swkbd_state == SwkbdState::InitializedIsHidden && swkbd_calc_arg.flags.appear) { 914 if (swkbd_state == SwkbdState::InitializedIsHidden && swkbd_calc_arg_common.flags.appear) {
747 ShowInlineKeyboard(); 915 ShowInlineKeyboardNew();
748 return; 916 return;
749 } 917 }
750 918
751 if (swkbd_state == SwkbdState::InitializedIsShown && swkbd_calc_arg.flags.disappear) { 919 if (swkbd_state == SwkbdState::InitializedIsShown && swkbd_calc_arg_common.flags.disappear) {
752 HideInlineKeyboard(); 920 HideInlineKeyboard();
753 return; 921 return;
754 } 922 }
diff --git a/src/core/hle/service/am/applets/applet_software_keyboard.h b/src/core/hle/service/am/applets/applet_software_keyboard.h
index a0fddd965..7ee4c5eea 100644
--- a/src/core/hle/service/am/applets/applet_software_keyboard.h
+++ b/src/core/hle/service/am/applets/applet_software_keyboard.h
@@ -13,6 +13,11 @@ namespace Core {
13class System; 13class System;
14} 14}
15 15
16namespace Core::Frontend {
17struct KeyboardInitializeParameters;
18struct InlineAppearParameters;
19} // namespace Core::Frontend
20
16namespace Service::AM::Applets { 21namespace Service::AM::Applets {
17 22
18class SoftwareKeyboard final : public Applet { 23class SoftwareKeyboard final : public Applet {
@@ -78,13 +83,22 @@ private:
78 void ChangeState(SwkbdState state); 83 void ChangeState(SwkbdState state);
79 84
80 /** 85 /**
81 * Signals the frontend to initialize the software keyboard with common parameters. 86 * Signals the frontend to initialize the normal software keyboard with common parameters.
82 * This initializes either the normal software keyboard or the inline software keyboard
83 * depending on the state of is_background.
84 * Note that this does not cause the keyboard to appear. 87 * Note that this does not cause the keyboard to appear.
85 * Use the respective Show*Keyboard() functions to cause the respective keyboards to appear. 88 * Use the ShowNormalKeyboard() functions to cause the keyboard to appear.
86 */ 89 */
87 void InitializeFrontendKeyboard(); 90 void InitializeFrontendNormalKeyboard();
91
92 /**
93 * Signals the frontend to initialize the inline software keyboard with common parameters.
94 * Note that this does not cause the keyboard to appear.
95 * Use the ShowInlineKeyboard() to cause the keyboard to appear.
96 */
97 void InitializeFrontendInlineKeyboard(
98 Core::Frontend::KeyboardInitializeParameters initialize_parameters);
99
100 void InitializeFrontendInlineKeyboardOld();
101 void InitializeFrontendInlineKeyboardNew();
88 102
89 /// Signals the frontend to show the normal software keyboard. 103 /// Signals the frontend to show the normal software keyboard.
90 void ShowNormalKeyboard(); 104 void ShowNormalKeyboard();
@@ -94,7 +108,10 @@ private:
94 std::u16string text_check_message); 108 std::u16string text_check_message);
95 109
96 /// Signals the frontend to show the inline software keyboard. 110 /// Signals the frontend to show the inline software keyboard.
97 void ShowInlineKeyboard(); 111 void ShowInlineKeyboard(Core::Frontend::InlineAppearParameters appear_parameters);
112
113 void ShowInlineKeyboardOld();
114 void ShowInlineKeyboardNew();
98 115
99 /// Signals the frontend to hide the inline software keyboard. 116 /// Signals the frontend to hide the inline software keyboard.
100 void HideInlineKeyboard(); 117 void HideInlineKeyboard();
@@ -111,6 +128,8 @@ private:
111 void RequestSetUserWordInfo(const std::vector<u8>& request_data); 128 void RequestSetUserWordInfo(const std::vector<u8>& request_data);
112 void RequestSetCustomizeDic(const std::vector<u8>& request_data); 129 void RequestSetCustomizeDic(const std::vector<u8>& request_data);
113 void RequestCalc(const std::vector<u8>& request_data); 130 void RequestCalc(const std::vector<u8>& request_data);
131 void RequestCalcOld();
132 void RequestCalcNew();
114 void RequestSetCustomizedDictionaries(const std::vector<u8>& request_data); 133 void RequestSetCustomizedDictionaries(const std::vector<u8>& request_data);
115 void RequestUnsetCustomizedDictionaries(const std::vector<u8>& request_data); 134 void RequestUnsetCustomizedDictionaries(const std::vector<u8>& request_data);
116 void RequestSetChangedStringV2Flag(const std::vector<u8>& request_data); 135 void RequestSetChangedStringV2Flag(const std::vector<u8>& request_data);
@@ -149,7 +168,9 @@ private:
149 168
150 SwkbdState swkbd_state{SwkbdState::NotInitialized}; 169 SwkbdState swkbd_state{SwkbdState::NotInitialized};
151 SwkbdInitializeArg swkbd_initialize_arg; 170 SwkbdInitializeArg swkbd_initialize_arg;
152 SwkbdCalcArg swkbd_calc_arg; 171 SwkbdCalcArgCommon swkbd_calc_arg_common;
172 SwkbdCalcArgOld swkbd_calc_arg_old;
173 SwkbdCalcArgNew swkbd_calc_arg_new;
153 bool use_changed_string_v2{false}; 174 bool use_changed_string_v2{false};
154 bool use_moved_cursor_v2{false}; 175 bool use_moved_cursor_v2{false};
155 bool inline_use_utf8{false}; 176 bool inline_use_utf8{false};
diff --git a/src/core/hle/service/am/applets/applet_software_keyboard_types.h b/src/core/hle/service/am/applets/applet_software_keyboard_types.h
index 21aa8e800..de1ca7aa5 100644
--- a/src/core/hle/service/am/applets/applet_software_keyboard_types.h
+++ b/src/core/hle/service/am/applets/applet_software_keyboard_types.h
@@ -10,6 +10,7 @@
10#include "common/common_funcs.h" 10#include "common/common_funcs.h"
11#include "common/common_types.h" 11#include "common/common_types.h"
12#include "common/swap.h" 12#include "common/swap.h"
13#include "common/uuid.h"
13 14
14namespace Service::AM::Applets { 15namespace Service::AM::Applets {
15 16
@@ -216,7 +217,7 @@ struct SwkbdInitializeArg {
216}; 217};
217static_assert(sizeof(SwkbdInitializeArg) == 0x8, "SwkbdInitializeArg has incorrect size."); 218static_assert(sizeof(SwkbdInitializeArg) == 0x8, "SwkbdInitializeArg has incorrect size.");
218 219
219struct SwkbdAppearArg { 220struct SwkbdAppearArgOld {
220 SwkbdType type{}; 221 SwkbdType type{};
221 std::array<char16_t, MAX_OK_TEXT_LENGTH + 1> ok_text{}; 222 std::array<char16_t, MAX_OK_TEXT_LENGTH + 1> ok_text{};
222 char16_t left_optional_symbol_key{}; 223 char16_t left_optional_symbol_key{};
@@ -229,19 +230,76 @@ struct SwkbdAppearArg {
229 bool enable_return_button{}; 230 bool enable_return_button{};
230 INSERT_PADDING_BYTES(3); 231 INSERT_PADDING_BYTES(3);
231 u32 flags{}; 232 u32 flags{};
232 INSERT_PADDING_WORDS(6); 233 bool is_use_save_data{};
234 INSERT_PADDING_BYTES(7);
235 Common::UUID user_id{};
233}; 236};
234static_assert(sizeof(SwkbdAppearArg) == 0x48, "SwkbdAppearArg has incorrect size."); 237static_assert(sizeof(SwkbdAppearArgOld) == 0x48, "SwkbdAppearArg has incorrect size.");
235 238
236struct SwkbdCalcArg { 239struct SwkbdAppearArgNew {
240 SwkbdType type{};
241 std::array<char16_t, MAX_OK_TEXT_LENGTH + 1> ok_text{};
242 char16_t left_optional_symbol_key{};
243 char16_t right_optional_symbol_key{};
244 bool use_prediction{};
245 bool disable_cancel_button{};
246 SwkbdKeyDisableFlags key_disable_flags{};
247 u32 max_text_length{};
248 u32 min_text_length{};
249 bool enable_return_button{};
250 INSERT_PADDING_BYTES(3);
251 u32 flags{};
252 bool is_use_save_data{};
253 INSERT_PADDING_BYTES(7);
254 Common::UUID user_id{};
255 u64 start_sampling_number{};
256 INSERT_PADDING_WORDS(8);
257};
258static_assert(sizeof(SwkbdAppearArgNew) == 0x70, "SwkbdAppearArg has incorrect size.");
259
260struct SwkbdCalcArgCommon {
237 u32 unknown{}; 261 u32 unknown{};
238 u16 calc_arg_size{}; 262 u16 calc_arg_size{};
239 INSERT_PADDING_BYTES(2); 263 INSERT_PADDING_BYTES(2);
240 SwkbdCalcArgFlags flags{}; 264 SwkbdCalcArgFlags flags{};
241 SwkbdInitializeArg initialize_arg{}; 265 SwkbdInitializeArg initialize_arg{};
266};
267static_assert(sizeof(SwkbdCalcArgCommon) == 0x18, "SwkbdCalcArgCommon has incorrect size.");
268
269struct SwkbdCalcArgOld {
270 f32 volume{};
271 s32 cursor_position{};
272 SwkbdAppearArgOld appear_arg{};
273 std::array<char16_t, 0x1FA> input_text{};
274 bool utf8_mode{};
275 INSERT_PADDING_BYTES(1);
276 bool enable_backspace_button{};
277 INSERT_PADDING_BYTES(3);
278 bool key_top_as_floating{};
279 bool footer_scalable{};
280 bool alpha_enabled_in_input_mode{};
281 u8 input_mode_fade_type{};
282 bool disable_touch{};
283 bool disable_hardware_keyboard{};
284 INSERT_PADDING_BYTES(8);
285 f32 key_top_scale_x{};
286 f32 key_top_scale_y{};
287 f32 key_top_translate_x{};
288 f32 key_top_translate_y{};
289 f32 key_top_bg_alpha{};
290 f32 footer_bg_alpha{};
291 f32 balloon_scale{};
292 INSERT_PADDING_WORDS(4);
293 u8 se_group{};
294 INSERT_PADDING_BYTES(3);
295};
296static_assert(sizeof(SwkbdCalcArgOld) == 0x4A0 - sizeof(SwkbdCalcArgCommon),
297 "SwkbdCalcArgOld has incorrect size.");
298
299struct SwkbdCalcArgNew {
300 SwkbdAppearArgNew appear_arg{};
242 f32 volume{}; 301 f32 volume{};
243 s32 cursor_position{}; 302 s32 cursor_position{};
244 SwkbdAppearArg appear_arg{};
245 std::array<char16_t, 0x1FA> input_text{}; 303 std::array<char16_t, 0x1FA> input_text{};
246 bool utf8_mode{}; 304 bool utf8_mode{};
247 INSERT_PADDING_BYTES(1); 305 INSERT_PADDING_BYTES(1);
@@ -264,8 +322,10 @@ struct SwkbdCalcArg {
264 INSERT_PADDING_WORDS(4); 322 INSERT_PADDING_WORDS(4);
265 u8 se_group{}; 323 u8 se_group{};
266 INSERT_PADDING_BYTES(3); 324 INSERT_PADDING_BYTES(3);
325 INSERT_PADDING_WORDS(8);
267}; 326};
268static_assert(sizeof(SwkbdCalcArg) == 0x4A0, "SwkbdCalcArg has incorrect size."); 327static_assert(sizeof(SwkbdCalcArgNew) == 0x4E8 - sizeof(SwkbdCalcArgCommon),
328 "SwkbdCalcArgNew has incorrect size.");
269 329
270struct SwkbdChangedStringArg { 330struct SwkbdChangedStringArg {
271 u32 text_length{}; 331 u32 text_length{};
diff --git a/src/core/hle/service/am/applets/applet_web_browser.cpp b/src/core/hle/service/am/applets/applet_web_browser.cpp
index bb5cb61be..a4b3fb187 100644
--- a/src/core/hle/service/am/applets/applet_web_browser.cpp
+++ b/src/core/hle/service/am/applets/applet_web_browser.cpp
@@ -446,6 +446,14 @@ void WebBrowser::ExecuteLogin() {
446} 446}
447 447
448void WebBrowser::ExecuteOffline() { 448void WebBrowser::ExecuteOffline() {
449 // TODO (Morph): This is a hack for WebSession foreground web applets such as those used by
450 // Super Mario 3D All-Stars.
451 // TODO (Morph): Implement WebSession.
452 if (applet_mode == LibraryAppletMode::AllForegroundInitiallyHidden) {
453 LOG_WARNING(Service_AM, "WebSession is not implemented");
454 return;
455 }
456
449 const auto main_url = GetMainURL(Common::FS::PathToUTF8String(offline_document)); 457 const auto main_url = GetMainURL(Common::FS::PathToUTF8String(offline_document));
450 458
451 if (!Common::FS::Exists(main_url)) { 459 if (!Common::FS::Exists(main_url)) {
diff --git a/src/core/hle/service/am/applets/applets.cpp b/src/core/hle/service/am/applets/applets.cpp
index 79e62679d..1f4c9786a 100644
--- a/src/core/hle/service/am/applets/applets.cpp
+++ b/src/core/hle/service/am/applets/applets.cpp
@@ -9,7 +9,7 @@
9#include "core/frontend/applets/controller.h" 9#include "core/frontend/applets/controller.h"
10#include "core/frontend/applets/error.h" 10#include "core/frontend/applets/error.h"
11#include "core/frontend/applets/general_frontend.h" 11#include "core/frontend/applets/general_frontend.h"
12#include "core/frontend/applets/mii.h" 12#include "core/frontend/applets/mii_edit.h"
13#include "core/frontend/applets/profile_select.h" 13#include "core/frontend/applets/profile_select.h"
14#include "core/frontend/applets/software_keyboard.h" 14#include "core/frontend/applets/software_keyboard.h"
15#include "core/frontend/applets/web_browser.h" 15#include "core/frontend/applets/web_browser.h"
@@ -20,7 +20,7 @@
20#include "core/hle/service/am/applets/applet_controller.h" 20#include "core/hle/service/am/applets/applet_controller.h"
21#include "core/hle/service/am/applets/applet_error.h" 21#include "core/hle/service/am/applets/applet_error.h"
22#include "core/hle/service/am/applets/applet_general_backend.h" 22#include "core/hle/service/am/applets/applet_general_backend.h"
23#include "core/hle/service/am/applets/applet_mii.h" 23#include "core/hle/service/am/applets/applet_mii_edit.h"
24#include "core/hle/service/am/applets/applet_profile_select.h" 24#include "core/hle/service/am/applets/applet_profile_select.h"
25#include "core/hle/service/am/applets/applet_software_keyboard.h" 25#include "core/hle/service/am/applets/applet_software_keyboard.h"
26#include "core/hle/service/am/applets/applet_web_browser.h" 26#include "core/hle/service/am/applets/applet_web_browser.h"
@@ -173,12 +173,12 @@ void Applet::Initialize() {
173AppletFrontendSet::AppletFrontendSet() = default; 173AppletFrontendSet::AppletFrontendSet() = default;
174 174
175AppletFrontendSet::AppletFrontendSet(ControllerApplet controller_applet, ErrorApplet error_applet, 175AppletFrontendSet::AppletFrontendSet(ControllerApplet controller_applet, ErrorApplet error_applet,
176 MiiEdit mii_edit_,
176 ParentalControlsApplet parental_controls_applet, 177 ParentalControlsApplet parental_controls_applet,
177 MiiApplet mii_applet, PhotoViewer photo_viewer_, 178 PhotoViewer photo_viewer_, ProfileSelect profile_select_,
178 ProfileSelect profile_select_,
179 SoftwareKeyboard software_keyboard_, WebBrowser web_browser_) 179 SoftwareKeyboard software_keyboard_, WebBrowser web_browser_)
180 : controller{std::move(controller_applet)}, error{std::move(error_applet)}, 180 : controller{std::move(controller_applet)}, error{std::move(error_applet)},
181 parental_controls{std::move(parental_controls_applet)}, mii{std::move(mii_applet)}, 181 mii_edit{std::move(mii_edit_)}, parental_controls{std::move(parental_controls_applet)},
182 photo_viewer{std::move(photo_viewer_)}, profile_select{std::move(profile_select_)}, 182 photo_viewer{std::move(photo_viewer_)}, profile_select{std::move(profile_select_)},
183 software_keyboard{std::move(software_keyboard_)}, web_browser{std::move(web_browser_)} {} 183 software_keyboard{std::move(software_keyboard_)}, web_browser{std::move(web_browser_)} {}
184 184
@@ -205,12 +205,12 @@ void AppletManager::SetAppletFrontendSet(AppletFrontendSet set) {
205 frontend.error = std::move(set.error); 205 frontend.error = std::move(set.error);
206 } 206 }
207 207
208 if (set.parental_controls != nullptr) { 208 if (set.mii_edit != nullptr) {
209 frontend.parental_controls = std::move(set.parental_controls); 209 frontend.mii_edit = std::move(set.mii_edit);
210 } 210 }
211 211
212 if (set.mii != nullptr) { 212 if (set.parental_controls != nullptr) {
213 frontend.mii = std::move(set.mii); 213 frontend.parental_controls = std::move(set.parental_controls);
214 } 214 }
215 215
216 if (set.photo_viewer != nullptr) { 216 if (set.photo_viewer != nullptr) {
@@ -245,15 +245,15 @@ void AppletManager::SetDefaultAppletsIfMissing() {
245 frontend.error = std::make_unique<Core::Frontend::DefaultErrorApplet>(); 245 frontend.error = std::make_unique<Core::Frontend::DefaultErrorApplet>();
246 } 246 }
247 247
248 if (frontend.mii_edit == nullptr) {
249 frontend.mii_edit = std::make_unique<Core::Frontend::DefaultMiiEditApplet>();
250 }
251
248 if (frontend.parental_controls == nullptr) { 252 if (frontend.parental_controls == nullptr) {
249 frontend.parental_controls = 253 frontend.parental_controls =
250 std::make_unique<Core::Frontend::DefaultParentalControlsApplet>(); 254 std::make_unique<Core::Frontend::DefaultParentalControlsApplet>();
251 } 255 }
252 256
253 if (frontend.mii == nullptr) {
254 frontend.mii = std::make_unique<Core::Frontend::DefaultMiiApplet>();
255 }
256
257 if (frontend.photo_viewer == nullptr) { 257 if (frontend.photo_viewer == nullptr) {
258 frontend.photo_viewer = std::make_unique<Core::Frontend::DefaultPhotoViewerApplet>(); 258 frontend.photo_viewer = std::make_unique<Core::Frontend::DefaultPhotoViewerApplet>();
259 } 259 }
@@ -289,7 +289,7 @@ std::shared_ptr<Applet> AppletManager::GetApplet(AppletId id, LibraryAppletMode
289 case AppletId::SoftwareKeyboard: 289 case AppletId::SoftwareKeyboard:
290 return std::make_shared<SoftwareKeyboard>(system, mode, *frontend.software_keyboard); 290 return std::make_shared<SoftwareKeyboard>(system, mode, *frontend.software_keyboard);
291 case AppletId::MiiEdit: 291 case AppletId::MiiEdit:
292 return std::make_shared<Mii>(system, mode, *frontend.mii); 292 return std::make_shared<MiiEdit>(system, mode, *frontend.mii_edit);
293 case AppletId::Web: 293 case AppletId::Web:
294 case AppletId::Shop: 294 case AppletId::Shop:
295 case AppletId::OfflineWeb: 295 case AppletId::OfflineWeb:
diff --git a/src/core/hle/service/am/applets/applets.h b/src/core/hle/service/am/applets/applets.h
index 0c44aec79..50a7bdceb 100644
--- a/src/core/hle/service/am/applets/applets.h
+++ b/src/core/hle/service/am/applets/applets.h
@@ -20,8 +20,8 @@ namespace Core::Frontend {
20class ControllerApplet; 20class ControllerApplet;
21class ECommerceApplet; 21class ECommerceApplet;
22class ErrorApplet; 22class ErrorApplet;
23class MiiEditApplet;
23class ParentalControlsApplet; 24class ParentalControlsApplet;
24class MiiApplet;
25class PhotoViewerApplet; 25class PhotoViewerApplet;
26class ProfileSelectApplet; 26class ProfileSelectApplet;
27class SoftwareKeyboardApplet; 27class SoftwareKeyboardApplet;
@@ -179,8 +179,8 @@ protected:
179struct AppletFrontendSet { 179struct AppletFrontendSet {
180 using ControllerApplet = std::unique_ptr<Core::Frontend::ControllerApplet>; 180 using ControllerApplet = std::unique_ptr<Core::Frontend::ControllerApplet>;
181 using ErrorApplet = std::unique_ptr<Core::Frontend::ErrorApplet>; 181 using ErrorApplet = std::unique_ptr<Core::Frontend::ErrorApplet>;
182 using MiiEdit = std::unique_ptr<Core::Frontend::MiiEditApplet>;
182 using ParentalControlsApplet = std::unique_ptr<Core::Frontend::ParentalControlsApplet>; 183 using ParentalControlsApplet = std::unique_ptr<Core::Frontend::ParentalControlsApplet>;
183 using MiiApplet = std::unique_ptr<Core::Frontend::MiiApplet>;
184 using PhotoViewer = std::unique_ptr<Core::Frontend::PhotoViewerApplet>; 184 using PhotoViewer = std::unique_ptr<Core::Frontend::PhotoViewerApplet>;
185 using ProfileSelect = std::unique_ptr<Core::Frontend::ProfileSelectApplet>; 185 using ProfileSelect = std::unique_ptr<Core::Frontend::ProfileSelectApplet>;
186 using SoftwareKeyboard = std::unique_ptr<Core::Frontend::SoftwareKeyboardApplet>; 186 using SoftwareKeyboard = std::unique_ptr<Core::Frontend::SoftwareKeyboardApplet>;
@@ -188,7 +188,7 @@ struct AppletFrontendSet {
188 188
189 AppletFrontendSet(); 189 AppletFrontendSet();
190 AppletFrontendSet(ControllerApplet controller_applet, ErrorApplet error_applet, 190 AppletFrontendSet(ControllerApplet controller_applet, ErrorApplet error_applet,
191 ParentalControlsApplet parental_controls_applet, MiiApplet mii_applet, 191 MiiEdit mii_edit_, ParentalControlsApplet parental_controls_applet,
192 PhotoViewer photo_viewer_, ProfileSelect profile_select_, 192 PhotoViewer photo_viewer_, ProfileSelect profile_select_,
193 SoftwareKeyboard software_keyboard_, WebBrowser web_browser_); 193 SoftwareKeyboard software_keyboard_, WebBrowser web_browser_);
194 ~AppletFrontendSet(); 194 ~AppletFrontendSet();
@@ -201,8 +201,8 @@ struct AppletFrontendSet {
201 201
202 ControllerApplet controller; 202 ControllerApplet controller;
203 ErrorApplet error; 203 ErrorApplet error;
204 MiiEdit mii_edit;
204 ParentalControlsApplet parental_controls; 205 ParentalControlsApplet parental_controls;
205 MiiApplet mii;
206 PhotoViewer photo_viewer; 206 PhotoViewer photo_viewer;
207 ProfileSelect profile_select; 207 ProfileSelect profile_select;
208 SoftwareKeyboard software_keyboard; 208 SoftwareKeyboard software_keyboard;
diff --git a/src/core/hle/service/audio/audout_u.cpp b/src/core/hle/service/audio/audout_u.cpp
index affa7971c..a72956a28 100644
--- a/src/core/hle/service/audio/audout_u.cpp
+++ b/src/core/hle/service/audio/audout_u.cpp
@@ -41,9 +41,10 @@ public:
41 explicit IAudioOut(Core::System& system_, AudoutParams audio_params_, 41 explicit IAudioOut(Core::System& system_, AudoutParams audio_params_,
42 AudioCore::AudioOut& audio_core_, std::string&& device_name_, 42 AudioCore::AudioOut& audio_core_, std::string&& device_name_,
43 std::string&& unique_name) 43 std::string&& unique_name)
44 : ServiceFramework{system_, "IAudioOut"}, audio_core{audio_core_}, 44 : ServiceFramework{system_, "IAudioOut", ServiceThreadType::CreateNew},
45 device_name{std::move(device_name_)}, audio_params{audio_params_}, 45 audio_core{audio_core_}, device_name{std::move(device_name_)},
46 main_memory{system.Memory()}, service_context{system_, "IAudioOut"} { 46 audio_params{audio_params_}, main_memory{system.Memory()}, service_context{system_,
47 "IAudioOut"} {
47 // clang-format off 48 // clang-format off
48 static const FunctionInfo functions[] = { 49 static const FunctionInfo functions[] = {
49 {0, &IAudioOut::GetAudioOutState, "GetAudioOutState"}, 50 {0, &IAudioOut::GetAudioOutState, "GetAudioOutState"},
diff --git a/src/core/hle/service/audio/audren_u.cpp b/src/core/hle/service/audio/audren_u.cpp
index f45e5cecc..d4ffeb21d 100644
--- a/src/core/hle/service/audio/audren_u.cpp
+++ b/src/core/hle/service/audio/audren_u.cpp
@@ -24,7 +24,8 @@ public:
24 explicit IAudioRenderer(Core::System& system_, 24 explicit IAudioRenderer(Core::System& system_,
25 const AudioCommon::AudioRendererParameter& audren_params, 25 const AudioCommon::AudioRendererParameter& audren_params,
26 const std::size_t instance_number) 26 const std::size_t instance_number)
27 : ServiceFramework{system_, "IAudioRenderer"}, service_context{system_, "IAudioRenderer"} { 27 : ServiceFramework{system_, "IAudioRenderer", ServiceThreadType::CreateNew},
28 service_context{system_, "IAudioRenderer"} {
28 // clang-format off 29 // clang-format off
29 static const FunctionInfo functions[] = { 30 static const FunctionInfo functions[] = {
30 {0, &IAudioRenderer::GetSampleRate, "GetSampleRate"}, 31 {0, &IAudioRenderer::GetSampleRate, "GetSampleRate"},
diff --git a/src/core/hle/service/filesystem/filesystem.cpp b/src/core/hle/service/filesystem/filesystem.cpp
index 3703ca4c6..4208337db 100644
--- a/src/core/hle/service/filesystem/filesystem.cpp
+++ b/src/core/hle/service/filesystem/filesystem.cpp
@@ -174,7 +174,7 @@ ResultCode VfsDirectoryServiceWrapper::RenameFile(const std::string& src_path_,
174 ASSERT_MSG(dest != nullptr, "Newly created file with success cannot be found."); 174 ASSERT_MSG(dest != nullptr, "Newly created file with success cannot be found.");
175 175
176 ASSERT_MSG(dest->WriteBytes(src->ReadAllBytes()) == src->GetSize(), 176 ASSERT_MSG(dest->WriteBytes(src->ReadAllBytes()) == src->GetSize(),
177 "Could not write all of the bytes but everything else has succeded."); 177 "Could not write all of the bytes but everything else has succeeded.");
178 178
179 if (!src->GetContainingDirectory()->DeleteFile(Common::FS::GetFilename(src_path))) { 179 if (!src->GetContainingDirectory()->DeleteFile(Common::FS::GetFilename(src_path))) {
180 // TODO(DarkLordZach): Find a better error code for this 180 // TODO(DarkLordZach): Find a better error code for this
diff --git a/src/core/hle/service/filesystem/fsp_srv.cpp b/src/core/hle/service/filesystem/fsp_srv.cpp
index b087e7bba..c07929ab8 100644
--- a/src/core/hle/service/filesystem/fsp_srv.cpp
+++ b/src/core/hle/service/filesystem/fsp_srv.cpp
@@ -58,7 +58,8 @@ enum class FileSystemType : u8 {
58class IStorage final : public ServiceFramework<IStorage> { 58class IStorage final : public ServiceFramework<IStorage> {
59public: 59public:
60 explicit IStorage(Core::System& system_, FileSys::VirtualFile backend_) 60 explicit IStorage(Core::System& system_, FileSys::VirtualFile backend_)
61 : ServiceFramework{system_, "IStorage"}, backend(std::move(backend_)) { 61 : ServiceFramework{system_, "IStorage", ServiceThreadType::CreateNew},
62 backend(std::move(backend_)) {
62 static const FunctionInfo functions[] = { 63 static const FunctionInfo functions[] = {
63 {0, &IStorage::Read, "Read"}, 64 {0, &IStorage::Read, "Read"},
64 {1, nullptr, "Write"}, 65 {1, nullptr, "Write"},
@@ -116,7 +117,8 @@ private:
116class IFile final : public ServiceFramework<IFile> { 117class IFile final : public ServiceFramework<IFile> {
117public: 118public:
118 explicit IFile(Core::System& system_, FileSys::VirtualFile backend_) 119 explicit IFile(Core::System& system_, FileSys::VirtualFile backend_)
119 : ServiceFramework{system_, "IFile"}, backend(std::move(backend_)) { 120 : ServiceFramework{system_, "IFile", ServiceThreadType::CreateNew},
121 backend(std::move(backend_)) {
120 static const FunctionInfo functions[] = { 122 static const FunctionInfo functions[] = {
121 {0, &IFile::Read, "Read"}, 123 {0, &IFile::Read, "Read"},
122 {1, &IFile::Write, "Write"}, 124 {1, &IFile::Write, "Write"},
@@ -252,7 +254,8 @@ static void BuildEntryIndex(std::vector<FileSys::Entry>& entries, const std::vec
252class IDirectory final : public ServiceFramework<IDirectory> { 254class IDirectory final : public ServiceFramework<IDirectory> {
253public: 255public:
254 explicit IDirectory(Core::System& system_, FileSys::VirtualDir backend_) 256 explicit IDirectory(Core::System& system_, FileSys::VirtualDir backend_)
255 : ServiceFramework{system_, "IDirectory"}, backend(std::move(backend_)) { 257 : ServiceFramework{system_, "IDirectory", ServiceThreadType::CreateNew},
258 backend(std::move(backend_)) {
256 static const FunctionInfo functions[] = { 259 static const FunctionInfo functions[] = {
257 {0, &IDirectory::Read, "Read"}, 260 {0, &IDirectory::Read, "Read"},
258 {1, &IDirectory::GetEntryCount, "GetEntryCount"}, 261 {1, &IDirectory::GetEntryCount, "GetEntryCount"},
@@ -308,8 +311,8 @@ private:
308class IFileSystem final : public ServiceFramework<IFileSystem> { 311class IFileSystem final : public ServiceFramework<IFileSystem> {
309public: 312public:
310 explicit IFileSystem(Core::System& system_, FileSys::VirtualDir backend_, SizeGetter size_) 313 explicit IFileSystem(Core::System& system_, FileSys::VirtualDir backend_, SizeGetter size_)
311 : ServiceFramework{system_, "IFileSystem"}, backend{std::move(backend_)}, size{std::move( 314 : ServiceFramework{system_, "IFileSystem", ServiceThreadType::CreateNew},
312 size_)} { 315 backend{std::move(backend_)}, size{std::move(size_)} {
313 static const FunctionInfo functions[] = { 316 static const FunctionInfo functions[] = {
314 {0, &IFileSystem::CreateFile, "CreateFile"}, 317 {0, &IFileSystem::CreateFile, "CreateFile"},
315 {1, &IFileSystem::DeleteFile, "DeleteFile"}, 318 {1, &IFileSystem::DeleteFile, "DeleteFile"},
diff --git a/src/core/hle/service/hid/controllers/npad.cpp b/src/core/hle/service/hid/controllers/npad.cpp
index e5c951e06..68407e39a 100644
--- a/src/core/hle/service/hid/controllers/npad.cpp
+++ b/src/core/hle/service/hid/controllers/npad.cpp
@@ -17,6 +17,7 @@
17#include "core/hle/kernel/k_readable_event.h" 17#include "core/hle/kernel/k_readable_event.h"
18#include "core/hle/kernel/k_writable_event.h" 18#include "core/hle/kernel/k_writable_event.h"
19#include "core/hle/service/hid/controllers/npad.h" 19#include "core/hle/service/hid/controllers/npad.h"
20#include "core/hle/service/hid/errors.h"
20#include "core/hle/service/kernel_helpers.h" 21#include "core/hle/service/kernel_helpers.h"
21 22
22namespace Service::HID { 23namespace Service::HID {
@@ -48,15 +49,17 @@ bool Controller_NPad::IsNpadIdValid(Core::HID::NpadIdType npad_id) {
48} 49}
49 50
50bool Controller_NPad::IsDeviceHandleValid(const Core::HID::VibrationDeviceHandle& device_handle) { 51bool Controller_NPad::IsDeviceHandleValid(const Core::HID::VibrationDeviceHandle& device_handle) {
51 return IsNpadIdValid(static_cast<Core::HID::NpadIdType>(device_handle.npad_id)) && 52 const auto npad_id = IsNpadIdValid(static_cast<Core::HID::NpadIdType>(device_handle.npad_id));
52 device_handle.npad_type < Core::HID::NpadStyleIndex::MaxNpadType && 53 const bool npad_type = device_handle.npad_type < Core::HID::NpadStyleIndex::MaxNpadType;
53 device_handle.device_index < Core::HID::DeviceIndex::MaxDeviceIndex; 54 const bool device_index = device_handle.device_index < Core::HID::DeviceIndex::MaxDeviceIndex;
55 return npad_id && npad_type && device_index;
54} 56}
55 57
56bool Controller_NPad::IsDeviceHandleValid(const Core::HID::SixAxisSensorHandle& device_handle) { 58bool Controller_NPad::IsDeviceHandleValid(const Core::HID::SixAxisSensorHandle& device_handle) {
57 return IsNpadIdValid(static_cast<Core::HID::NpadIdType>(device_handle.npad_id)) && 59 const auto npad_id = IsNpadIdValid(static_cast<Core::HID::NpadIdType>(device_handle.npad_id));
58 device_handle.npad_type < Core::HID::NpadStyleIndex::MaxNpadType && 60 const bool npad_type = device_handle.npad_type < Core::HID::NpadStyleIndex::MaxNpadType;
59 device_handle.device_index < Core::HID::DeviceIndex::MaxDeviceIndex; 61 const bool device_index = device_handle.device_index < Core::HID::DeviceIndex::MaxDeviceIndex;
62 return npad_id && npad_type && device_index;
60} 63}
61 64
62Controller_NPad::Controller_NPad(Core::HID::HIDCore& hid_core_, 65Controller_NPad::Controller_NPad(Core::HID::HIDCore& hid_core_,
@@ -262,11 +265,6 @@ void Controller_NPad::OnInit() {
262 service_context.CreateEvent(fmt::format("npad:NpadStyleSetChanged_{}", i)); 265 service_context.CreateEvent(fmt::format("npad:NpadStyleSetChanged_{}", i));
263 } 266 }
264 267
265 if (hid_core.GetSupportedStyleTag().raw == Core::HID::NpadStyleSet::None) {
266 // We want to support all controllers
267 hid_core.SetSupportedStyleTag({Core::HID::NpadStyleSet::All});
268 }
269
270 supported_npad_id_types.resize(npad_id_list.size()); 268 supported_npad_id_types.resize(npad_id_list.size());
271 std::memcpy(supported_npad_id_types.data(), npad_id_list.data(), 269 std::memcpy(supported_npad_id_types.data(), npad_id_list.data(),
272 npad_id_list.size() * sizeof(Core::HID::NpadIdType)); 270 npad_id_list.size() * sizeof(Core::HID::NpadIdType));
@@ -288,14 +286,6 @@ void Controller_NPad::OnInit() {
288 WriteEmptyEntry(npad); 286 WriteEmptyEntry(npad);
289 } 287 }
290 } 288 }
291
292 // Connect controllers
293 for (auto& controller : controller_data) {
294 const auto& device = controller.device;
295 if (device->IsConnected()) {
296 AddNewControllerAt(device->GetNpadStyleIndex(), device->GetNpadIdType());
297 }
298 }
299} 289}
300 290
301void Controller_NPad::WriteEmptyEntry(NpadInternalState& npad) { 291void Controller_NPad::WriteEmptyEntry(NpadInternalState& npad) {
@@ -320,6 +310,7 @@ void Controller_NPad::WriteEmptyEntry(NpadInternalState& npad) {
320} 310}
321 311
322void Controller_NPad::OnRelease() { 312void Controller_NPad::OnRelease() {
313 is_controller_initialized = false;
323 for (std::size_t i = 0; i < controller_data.size(); ++i) { 314 for (std::size_t i = 0; i < controller_data.size(); ++i) {
324 auto& controller = controller_data[i]; 315 auto& controller = controller_data[i];
325 service_context.CloseEvent(controller.styleset_changed_event); 316 service_context.CloseEvent(controller.styleset_changed_event);
@@ -330,7 +321,7 @@ void Controller_NPad::OnRelease() {
330} 321}
331 322
332void Controller_NPad::RequestPadStateUpdate(Core::HID::NpadIdType npad_id) { 323void Controller_NPad::RequestPadStateUpdate(Core::HID::NpadIdType npad_id) {
333 std::lock_guard lock{mutex}; 324 std::scoped_lock lock{mutex};
334 auto& controller = GetControllerFromNpadIdType(npad_id); 325 auto& controller = GetControllerFromNpadIdType(npad_id);
335 const auto controller_type = controller.device->GetNpadStyleIndex(); 326 const auto controller_type = controller.device->GetNpadStyleIndex();
336 if (!controller.device->IsConnected()) { 327 if (!controller.device->IsConnected()) {
@@ -651,9 +642,27 @@ void Controller_NPad::OnMotionUpdate(const Core::Timing::CoreTiming& core_timing
651 642
652void Controller_NPad::SetSupportedStyleSet(Core::HID::NpadStyleTag style_set) { 643void Controller_NPad::SetSupportedStyleSet(Core::HID::NpadStyleTag style_set) {
653 hid_core.SetSupportedStyleTag(style_set); 644 hid_core.SetSupportedStyleTag(style_set);
645
646 if (is_controller_initialized) {
647 return;
648 }
649
650 // Once SetSupportedStyleSet is called controllers are fully initialized
651 is_controller_initialized = true;
652
653 // Connect all active controllers
654 for (auto& controller : controller_data) {
655 const auto& device = controller.device;
656 if (device->IsConnected()) {
657 AddNewControllerAt(device->GetNpadStyleIndex(), device->GetNpadIdType());
658 }
659 }
654} 660}
655 661
656Core::HID::NpadStyleTag Controller_NPad::GetSupportedStyleSet() const { 662Core::HID::NpadStyleTag Controller_NPad::GetSupportedStyleSet() const {
663 if (!is_controller_initialized) {
664 return {Core::HID::NpadStyleSet::None};
665 }
657 return hid_core.GetSupportedStyleTag(); 666 return hid_core.GetSupportedStyleTag();
658} 667}
659 668
@@ -1001,87 +1010,271 @@ void Controller_NPad::DisconnectNpad(Core::HID::NpadIdType npad_id) {
1001 WriteEmptyEntry(controller.shared_memory_entry); 1010 WriteEmptyEntry(controller.shared_memory_entry);
1002} 1011}
1003 1012
1004void Controller_NPad::SetGyroscopeZeroDriftMode(Core::HID::SixAxisSensorHandle sixaxis_handle, 1013ResultCode Controller_NPad::SetGyroscopeZeroDriftMode(Core::HID::SixAxisSensorHandle sixaxis_handle,
1005 GyroscopeZeroDriftMode drift_mode) { 1014 GyroscopeZeroDriftMode drift_mode) {
1006 if (!IsDeviceHandleValid(sixaxis_handle)) { 1015 if (!IsDeviceHandleValid(sixaxis_handle)) {
1007 LOG_ERROR(Service_HID, "Invalid handle"); 1016 LOG_ERROR(Service_HID, "Invalid handle");
1008 return; 1017 return NpadInvalidHandle;
1018 }
1019
1020 auto& controller = GetControllerFromHandle(sixaxis_handle);
1021 switch (sixaxis_handle.npad_type) {
1022 case Core::HID::NpadStyleIndex::ProController:
1023 controller.sixaxis_fullkey.gyroscope_zero_drift_mode = drift_mode;
1024 break;
1025 case Core::HID::NpadStyleIndex::Handheld:
1026 controller.sixaxis_handheld.gyroscope_zero_drift_mode = drift_mode;
1027 break;
1028 case Core::HID::NpadStyleIndex::JoyconDual:
1029 case Core::HID::NpadStyleIndex::GameCube:
1030 case Core::HID::NpadStyleIndex::Pokeball:
1031 if (sixaxis_handle.device_index == Core::HID::DeviceIndex::Left) {
1032 controller.sixaxis_dual_left.gyroscope_zero_drift_mode = drift_mode;
1033 break;
1034 }
1035 controller.sixaxis_dual_right.gyroscope_zero_drift_mode = drift_mode;
1036 break;
1037 case Core::HID::NpadStyleIndex::JoyconLeft:
1038 controller.sixaxis_left.gyroscope_zero_drift_mode = drift_mode;
1039 break;
1040 case Core::HID::NpadStyleIndex::JoyconRight:
1041 controller.sixaxis_right.gyroscope_zero_drift_mode = drift_mode;
1042 break;
1043 default:
1044 LOG_ERROR(Service_HID, "Invalid Npad type {}", sixaxis_handle.npad_type);
1045 return NpadInvalidHandle;
1046 }
1047
1048 return ResultSuccess;
1049}
1050
1051ResultCode Controller_NPad::GetGyroscopeZeroDriftMode(Core::HID::SixAxisSensorHandle sixaxis_handle,
1052 GyroscopeZeroDriftMode& drift_mode) const {
1053 if (!IsDeviceHandleValid(sixaxis_handle)) {
1054 LOG_ERROR(Service_HID, "Invalid handle");
1055 return NpadInvalidHandle;
1009 } 1056 }
1057
1010 auto& controller = GetControllerFromHandle(sixaxis_handle); 1058 auto& controller = GetControllerFromHandle(sixaxis_handle);
1011 controller.gyroscope_zero_drift_mode = drift_mode; 1059 switch (sixaxis_handle.npad_type) {
1060 case Core::HID::NpadStyleIndex::ProController:
1061 drift_mode = controller.sixaxis_fullkey.gyroscope_zero_drift_mode;
1062 break;
1063 case Core::HID::NpadStyleIndex::Handheld:
1064 drift_mode = controller.sixaxis_handheld.gyroscope_zero_drift_mode;
1065 break;
1066 case Core::HID::NpadStyleIndex::JoyconDual:
1067 case Core::HID::NpadStyleIndex::GameCube:
1068 case Core::HID::NpadStyleIndex::Pokeball:
1069 if (sixaxis_handle.device_index == Core::HID::DeviceIndex::Left) {
1070 drift_mode = controller.sixaxis_dual_left.gyroscope_zero_drift_mode;
1071 break;
1072 }
1073 drift_mode = controller.sixaxis_dual_right.gyroscope_zero_drift_mode;
1074 break;
1075 case Core::HID::NpadStyleIndex::JoyconLeft:
1076 drift_mode = controller.sixaxis_left.gyroscope_zero_drift_mode;
1077 break;
1078 case Core::HID::NpadStyleIndex::JoyconRight:
1079 drift_mode = controller.sixaxis_right.gyroscope_zero_drift_mode;
1080 break;
1081 default:
1082 LOG_ERROR(Service_HID, "Invalid Npad type {}", sixaxis_handle.npad_type);
1083 return NpadInvalidHandle;
1084 }
1085
1086 return ResultSuccess;
1012} 1087}
1013 1088
1014Controller_NPad::GyroscopeZeroDriftMode Controller_NPad::GetGyroscopeZeroDriftMode( 1089ResultCode Controller_NPad::IsSixAxisSensorAtRest(Core::HID::SixAxisSensorHandle sixaxis_handle,
1015 Core::HID::SixAxisSensorHandle sixaxis_handle) const { 1090 bool& is_at_rest) const {
1016 if (!IsDeviceHandleValid(sixaxis_handle)) { 1091 if (!IsDeviceHandleValid(sixaxis_handle)) {
1017 LOG_ERROR(Service_HID, "Invalid handle"); 1092 LOG_ERROR(Service_HID, "Invalid handle");
1018 // Return the default value 1093 return NpadInvalidHandle;
1019 return GyroscopeZeroDriftMode::Standard;
1020 } 1094 }
1021 const auto& controller = GetControllerFromHandle(sixaxis_handle); 1095 const auto& controller = GetControllerFromHandle(sixaxis_handle);
1022 return controller.gyroscope_zero_drift_mode; 1096 is_at_rest = controller.sixaxis_at_rest;
1097 return ResultSuccess;
1023} 1098}
1024 1099
1025bool Controller_NPad::IsSixAxisSensorAtRest(Core::HID::SixAxisSensorHandle sixaxis_handle) const { 1100ResultCode Controller_NPad::IsFirmwareUpdateAvailableForSixAxisSensor(
1101 Core::HID::SixAxisSensorHandle sixaxis_handle, bool& is_firmware_available) const {
1026 if (!IsDeviceHandleValid(sixaxis_handle)) { 1102 if (!IsDeviceHandleValid(sixaxis_handle)) {
1027 LOG_ERROR(Service_HID, "Invalid handle"); 1103 LOG_ERROR(Service_HID, "Invalid handle");
1028 // Return the default value 1104 return NpadInvalidHandle;
1029 return true;
1030 } 1105 }
1031 const auto& controller = GetControllerFromHandle(sixaxis_handle); 1106
1032 return controller.sixaxis_at_rest; 1107 // We don't support joycon firmware updates
1108 is_firmware_available = false;
1109 return ResultSuccess;
1033} 1110}
1034 1111
1035void Controller_NPad::SetSixAxisEnabled(Core::HID::SixAxisSensorHandle sixaxis_handle, 1112ResultCode Controller_NPad::SetSixAxisEnabled(Core::HID::SixAxisSensorHandle sixaxis_handle,
1036 bool sixaxis_status) { 1113 bool sixaxis_status) {
1037 if (!IsDeviceHandleValid(sixaxis_handle)) { 1114 if (!IsDeviceHandleValid(sixaxis_handle)) {
1038 LOG_ERROR(Service_HID, "Invalid handle"); 1115 LOG_ERROR(Service_HID, "Invalid handle");
1039 return; 1116 return NpadInvalidHandle;
1040 } 1117 }
1041 auto& controller = GetControllerFromHandle(sixaxis_handle); 1118 auto& controller = GetControllerFromHandle(sixaxis_handle);
1042 controller.sixaxis_sensor_enabled = sixaxis_status; 1119 controller.sixaxis_sensor_enabled = sixaxis_status;
1120 return ResultSuccess;
1043} 1121}
1044 1122
1045void Controller_NPad::SetSixAxisFusionEnabled(Core::HID::SixAxisSensorHandle sixaxis_handle, 1123ResultCode Controller_NPad::IsSixAxisSensorFusionEnabled(
1046 bool sixaxis_fusion_status) { 1124 Core::HID::SixAxisSensorHandle sixaxis_handle, bool& is_fusion_enabled) const {
1047 if (!IsDeviceHandleValid(sixaxis_handle)) { 1125 if (!IsDeviceHandleValid(sixaxis_handle)) {
1048 LOG_ERROR(Service_HID, "Invalid handle"); 1126 LOG_ERROR(Service_HID, "Invalid handle");
1049 return; 1127 return NpadInvalidHandle;
1050 } 1128 }
1129
1051 auto& controller = GetControllerFromHandle(sixaxis_handle); 1130 auto& controller = GetControllerFromHandle(sixaxis_handle);
1052 controller.sixaxis_fusion_enabled = sixaxis_fusion_status; 1131 switch (sixaxis_handle.npad_type) {
1053} 1132 case Core::HID::NpadStyleIndex::ProController:
1133 is_fusion_enabled = controller.sixaxis_fullkey.is_fusion_enabled;
1134 break;
1135 case Core::HID::NpadStyleIndex::Handheld:
1136 is_fusion_enabled = controller.sixaxis_handheld.is_fusion_enabled;
1137 break;
1138 case Core::HID::NpadStyleIndex::JoyconDual:
1139 case Core::HID::NpadStyleIndex::GameCube:
1140 case Core::HID::NpadStyleIndex::Pokeball:
1141 if (sixaxis_handle.device_index == Core::HID::DeviceIndex::Left) {
1142 is_fusion_enabled = controller.sixaxis_dual_left.is_fusion_enabled;
1143 break;
1144 }
1145 is_fusion_enabled = controller.sixaxis_dual_right.is_fusion_enabled;
1146 break;
1147 case Core::HID::NpadStyleIndex::JoyconLeft:
1148 is_fusion_enabled = controller.sixaxis_left.is_fusion_enabled;
1149 break;
1150 case Core::HID::NpadStyleIndex::JoyconRight:
1151 is_fusion_enabled = controller.sixaxis_right.is_fusion_enabled;
1152 break;
1153 default:
1154 LOG_ERROR(Service_HID, "Invalid Npad type {}", sixaxis_handle.npad_type);
1155 return NpadInvalidHandle;
1156 }
1054 1157
1055void Controller_NPad::SetSixAxisFusionParameters( 1158 return ResultSuccess;
1056 Core::HID::SixAxisSensorHandle sixaxis_handle, 1159}
1057 Core::HID::SixAxisSensorFusionParameters sixaxis_fusion_parameters) { 1160ResultCode Controller_NPad::SetSixAxisFusionEnabled(Core::HID::SixAxisSensorHandle sixaxis_handle,
1161 bool is_fusion_enabled) {
1058 if (!IsDeviceHandleValid(sixaxis_handle)) { 1162 if (!IsDeviceHandleValid(sixaxis_handle)) {
1059 LOG_ERROR(Service_HID, "Invalid handle"); 1163 LOG_ERROR(Service_HID, "Invalid handle");
1060 return; 1164 return NpadInvalidHandle;
1061 } 1165 }
1166
1062 auto& controller = GetControllerFromHandle(sixaxis_handle); 1167 auto& controller = GetControllerFromHandle(sixaxis_handle);
1063 controller.sixaxis_fusion = sixaxis_fusion_parameters; 1168 switch (sixaxis_handle.npad_type) {
1169 case Core::HID::NpadStyleIndex::ProController:
1170 controller.sixaxis_fullkey.is_fusion_enabled = is_fusion_enabled;
1171 break;
1172 case Core::HID::NpadStyleIndex::Handheld:
1173 controller.sixaxis_handheld.is_fusion_enabled = is_fusion_enabled;
1174 break;
1175 case Core::HID::NpadStyleIndex::JoyconDual:
1176 case Core::HID::NpadStyleIndex::GameCube:
1177 case Core::HID::NpadStyleIndex::Pokeball:
1178 if (sixaxis_handle.device_index == Core::HID::DeviceIndex::Left) {
1179 controller.sixaxis_dual_left.is_fusion_enabled = is_fusion_enabled;
1180 break;
1181 }
1182 controller.sixaxis_dual_right.is_fusion_enabled = is_fusion_enabled;
1183 break;
1184 case Core::HID::NpadStyleIndex::JoyconLeft:
1185 controller.sixaxis_left.is_fusion_enabled = is_fusion_enabled;
1186 break;
1187 case Core::HID::NpadStyleIndex::JoyconRight:
1188 controller.sixaxis_right.is_fusion_enabled = is_fusion_enabled;
1189 break;
1190 default:
1191 LOG_ERROR(Service_HID, "Invalid Npad type {}", sixaxis_handle.npad_type);
1192 return NpadInvalidHandle;
1193 }
1194
1195 return ResultSuccess;
1064} 1196}
1065 1197
1066Core::HID::SixAxisSensorFusionParameters Controller_NPad::GetSixAxisFusionParameters( 1198ResultCode Controller_NPad::SetSixAxisFusionParameters(
1067 Core::HID::SixAxisSensorHandle sixaxis_handle) { 1199 Core::HID::SixAxisSensorHandle sixaxis_handle,
1200 Core::HID::SixAxisSensorFusionParameters sixaxis_fusion_parameters) {
1068 if (!IsDeviceHandleValid(sixaxis_handle)) { 1201 if (!IsDeviceHandleValid(sixaxis_handle)) {
1069 LOG_ERROR(Service_HID, "Invalid handle"); 1202 LOG_ERROR(Service_HID, "Invalid handle");
1070 // Since these parameters are unknow just return zeros 1203 return NpadInvalidHandle;
1071 return {}; 1204 }
1205 const auto param1 = sixaxis_fusion_parameters.parameter1;
1206 if (param1 < 0.0f || param1 > 1.0f) {
1207 return InvalidSixAxisFusionRange;
1072 } 1208 }
1209
1073 auto& controller = GetControllerFromHandle(sixaxis_handle); 1210 auto& controller = GetControllerFromHandle(sixaxis_handle);
1074 return controller.sixaxis_fusion; 1211 switch (sixaxis_handle.npad_type) {
1212 case Core::HID::NpadStyleIndex::ProController:
1213 controller.sixaxis_fullkey.fusion = sixaxis_fusion_parameters;
1214 break;
1215 case Core::HID::NpadStyleIndex::Handheld:
1216 controller.sixaxis_handheld.fusion = sixaxis_fusion_parameters;
1217 break;
1218 case Core::HID::NpadStyleIndex::JoyconDual:
1219 case Core::HID::NpadStyleIndex::GameCube:
1220 case Core::HID::NpadStyleIndex::Pokeball:
1221 if (sixaxis_handle.device_index == Core::HID::DeviceIndex::Left) {
1222 controller.sixaxis_dual_left.fusion = sixaxis_fusion_parameters;
1223 break;
1224 }
1225 controller.sixaxis_dual_right.fusion = sixaxis_fusion_parameters;
1226 break;
1227 case Core::HID::NpadStyleIndex::JoyconLeft:
1228 controller.sixaxis_left.fusion = sixaxis_fusion_parameters;
1229 break;
1230 case Core::HID::NpadStyleIndex::JoyconRight:
1231 controller.sixaxis_right.fusion = sixaxis_fusion_parameters;
1232 break;
1233 default:
1234 LOG_ERROR(Service_HID, "Invalid Npad type {}", sixaxis_handle.npad_type);
1235 return NpadInvalidHandle;
1236 }
1237
1238 return ResultSuccess;
1075} 1239}
1076 1240
1077void Controller_NPad::ResetSixAxisFusionParameters(Core::HID::SixAxisSensorHandle sixaxis_handle) { 1241ResultCode Controller_NPad::GetSixAxisFusionParameters(
1242 Core::HID::SixAxisSensorHandle sixaxis_handle,
1243 Core::HID::SixAxisSensorFusionParameters& parameters) const {
1078 if (!IsDeviceHandleValid(sixaxis_handle)) { 1244 if (!IsDeviceHandleValid(sixaxis_handle)) {
1079 LOG_ERROR(Service_HID, "Invalid handle"); 1245 LOG_ERROR(Service_HID, "Invalid handle");
1080 return; 1246 return NpadInvalidHandle;
1081 } 1247 }
1082 auto& controller = GetControllerFromHandle(sixaxis_handle); 1248
1083 // Since these parameters are unknow just fill with zeros 1249 const auto& controller = GetControllerFromHandle(sixaxis_handle);
1084 controller.sixaxis_fusion = {}; 1250 switch (sixaxis_handle.npad_type) {
1251 case Core::HID::NpadStyleIndex::ProController:
1252 parameters = controller.sixaxis_fullkey.fusion;
1253 break;
1254 case Core::HID::NpadStyleIndex::Handheld:
1255 parameters = controller.sixaxis_handheld.fusion;
1256 break;
1257 case Core::HID::NpadStyleIndex::JoyconDual:
1258 case Core::HID::NpadStyleIndex::GameCube:
1259 case Core::HID::NpadStyleIndex::Pokeball:
1260 if (sixaxis_handle.device_index == Core::HID::DeviceIndex::Left) {
1261 parameters = controller.sixaxis_dual_left.fusion;
1262 break;
1263 }
1264 parameters = controller.sixaxis_dual_right.fusion;
1265 break;
1266 case Core::HID::NpadStyleIndex::JoyconLeft:
1267 parameters = controller.sixaxis_left.fusion;
1268 break;
1269 case Core::HID::NpadStyleIndex::JoyconRight:
1270 parameters = controller.sixaxis_right.fusion;
1271 break;
1272 default:
1273 LOG_ERROR(Service_HID, "Invalid Npad type {}", sixaxis_handle.npad_type);
1274 return NpadInvalidHandle;
1275 }
1276
1277 return ResultSuccess;
1085} 1278}
1086 1279
1087void Controller_NPad::MergeSingleJoyAsDualJoy(Core::HID::NpadIdType npad_id_1, 1280void Controller_NPad::MergeSingleJoyAsDualJoy(Core::HID::NpadIdType npad_id_1,
diff --git a/src/core/hle/service/hid/controllers/npad.h b/src/core/hle/service/hid/controllers/npad.h
index 3287cf435..f7313711f 100644
--- a/src/core/hle/service/hid/controllers/npad.h
+++ b/src/core/hle/service/hid/controllers/npad.h
@@ -28,7 +28,9 @@ class KReadableEvent;
28 28
29namespace Service::KernelHelpers { 29namespace Service::KernelHelpers {
30class ServiceContext; 30class ServiceContext;
31} 31} // namespace Service::KernelHelpers
32
33union ResultCode;
32 34
33namespace Service::HID { 35namespace Service::HID {
34 36
@@ -143,20 +145,26 @@ public:
143 145
144 void DisconnectNpad(Core::HID::NpadIdType npad_id); 146 void DisconnectNpad(Core::HID::NpadIdType npad_id);
145 147
146 void SetGyroscopeZeroDriftMode(Core::HID::SixAxisSensorHandle sixaxis_handle, 148 ResultCode SetGyroscopeZeroDriftMode(Core::HID::SixAxisSensorHandle sixaxis_handle,
147 GyroscopeZeroDriftMode drift_mode); 149 GyroscopeZeroDriftMode drift_mode);
148 GyroscopeZeroDriftMode GetGyroscopeZeroDriftMode( 150 ResultCode GetGyroscopeZeroDriftMode(Core::HID::SixAxisSensorHandle sixaxis_handle,
149 Core::HID::SixAxisSensorHandle sixaxis_handle) const; 151 GyroscopeZeroDriftMode& drift_mode) const;
150 bool IsSixAxisSensorAtRest(Core::HID::SixAxisSensorHandle sixaxis_handle) const; 152 ResultCode IsSixAxisSensorAtRest(Core::HID::SixAxisSensorHandle sixaxis_handle,
151 void SetSixAxisEnabled(Core::HID::SixAxisSensorHandle sixaxis_handle, bool sixaxis_status); 153 bool& is_at_rest) const;
152 void SetSixAxisFusionEnabled(Core::HID::SixAxisSensorHandle sixaxis_handle, 154 ResultCode IsFirmwareUpdateAvailableForSixAxisSensor(
153 bool sixaxis_fusion_status); 155 Core::HID::SixAxisSensorHandle sixaxis_handle, bool& is_firmware_available) const;
154 void SetSixAxisFusionParameters( 156 ResultCode SetSixAxisEnabled(Core::HID::SixAxisSensorHandle sixaxis_handle,
157 bool sixaxis_status);
158 ResultCode IsSixAxisSensorFusionEnabled(Core::HID::SixAxisSensorHandle sixaxis_handle,
159 bool& is_fusion_enabled) const;
160 ResultCode SetSixAxisFusionEnabled(Core::HID::SixAxisSensorHandle sixaxis_handle,
161 bool is_fusion_enabled);
162 ResultCode SetSixAxisFusionParameters(
155 Core::HID::SixAxisSensorHandle sixaxis_handle, 163 Core::HID::SixAxisSensorHandle sixaxis_handle,
156 Core::HID::SixAxisSensorFusionParameters sixaxis_fusion_parameters); 164 Core::HID::SixAxisSensorFusionParameters sixaxis_fusion_parameters);
157 Core::HID::SixAxisSensorFusionParameters GetSixAxisFusionParameters( 165 ResultCode GetSixAxisFusionParameters(
158 Core::HID::SixAxisSensorHandle sixaxis_handle); 166 Core::HID::SixAxisSensorHandle sixaxis_handle,
159 void ResetSixAxisFusionParameters(Core::HID::SixAxisSensorHandle sixaxis_handle); 167 Core::HID::SixAxisSensorFusionParameters& parameters) const;
160 Core::HID::LedPattern GetLedPattern(Core::HID::NpadIdType npad_id); 168 Core::HID::LedPattern GetLedPattern(Core::HID::NpadIdType npad_id);
161 bool IsUnintendedHomeButtonInputProtectionEnabled(Core::HID::NpadIdType npad_id) const; 169 bool IsUnintendedHomeButtonInputProtectionEnabled(Core::HID::NpadIdType npad_id) const;
162 void SetUnintendedHomeButtonInputProtectionEnabled(bool is_protection_enabled, 170 void SetUnintendedHomeButtonInputProtectionEnabled(bool is_protection_enabled,
@@ -191,16 +199,16 @@ private:
191 199
192 // This is nn::hid::detail::NpadFullKeyColorState 200 // This is nn::hid::detail::NpadFullKeyColorState
193 struct NpadFullKeyColorState { 201 struct NpadFullKeyColorState {
194 ColorAttribute attribute; 202 ColorAttribute attribute{ColorAttribute::NoController};
195 Core::HID::NpadControllerColor fullkey; 203 Core::HID::NpadControllerColor fullkey{};
196 }; 204 };
197 static_assert(sizeof(NpadFullKeyColorState) == 0xC, "NpadFullKeyColorState is an invalid size"); 205 static_assert(sizeof(NpadFullKeyColorState) == 0xC, "NpadFullKeyColorState is an invalid size");
198 206
199 // This is nn::hid::detail::NpadJoyColorState 207 // This is nn::hid::detail::NpadJoyColorState
200 struct NpadJoyColorState { 208 struct NpadJoyColorState {
201 ColorAttribute attribute; 209 ColorAttribute attribute{ColorAttribute::NoController};
202 Core::HID::NpadControllerColor left; 210 Core::HID::NpadControllerColor left{};
203 Core::HID::NpadControllerColor right; 211 Core::HID::NpadControllerColor right{};
204 }; 212 };
205 static_assert(sizeof(NpadJoyColorState) == 0x14, "NpadJoyColorState is an invalid size"); 213 static_assert(sizeof(NpadJoyColorState) == 0x14, "NpadJoyColorState is an invalid size");
206 214
@@ -226,11 +234,11 @@ private:
226 // This is nn::hid::NpadPalmaState 234 // This is nn::hid::NpadPalmaState
227 // This is nn::hid::NpadSystemExtState 235 // This is nn::hid::NpadSystemExtState
228 struct NPadGenericState { 236 struct NPadGenericState {
229 s64_le sampling_number; 237 s64_le sampling_number{};
230 Core::HID::NpadButtonState npad_buttons; 238 Core::HID::NpadButtonState npad_buttons{};
231 Core::HID::AnalogStickState l_stick; 239 Core::HID::AnalogStickState l_stick{};
232 Core::HID::AnalogStickState r_stick; 240 Core::HID::AnalogStickState r_stick{};
233 NpadAttribute connection_status; 241 NpadAttribute connection_status{};
234 INSERT_PADDING_BYTES(4); // Reserved 242 INSERT_PADDING_BYTES(4); // Reserved
235 }; 243 };
236 static_assert(sizeof(NPadGenericState) == 0x28, "NPadGenericState is an invalid size"); 244 static_assert(sizeof(NPadGenericState) == 0x28, "NPadGenericState is an invalid size");
@@ -253,7 +261,7 @@ private:
253 Common::Vec3f gyro{}; 261 Common::Vec3f gyro{};
254 Common::Vec3f rotation{}; 262 Common::Vec3f rotation{};
255 std::array<Common::Vec3f, 3> orientation{}; 263 std::array<Common::Vec3f, 3> orientation{};
256 SixAxisSensorAttribute attribute; 264 SixAxisSensorAttribute attribute{};
257 INSERT_PADDING_BYTES(4); // Reserved 265 INSERT_PADDING_BYTES(4); // Reserved
258 }; 266 };
259 static_assert(sizeof(SixAxisSensorState) == 0x60, "SixAxisSensorState is an invalid size"); 267 static_assert(sizeof(SixAxisSensorState) == 0x60, "SixAxisSensorState is an invalid size");
@@ -325,11 +333,11 @@ private:
325 333
326 // This is nn::hid::detail::NfcXcdDeviceHandleStateImpl 334 // This is nn::hid::detail::NfcXcdDeviceHandleStateImpl
327 struct NfcXcdDeviceHandleStateImpl { 335 struct NfcXcdDeviceHandleStateImpl {
328 u64 handle; 336 u64 handle{};
329 bool is_available; 337 bool is_available{};
330 bool is_activated; 338 bool is_activated{};
331 INSERT_PADDING_BYTES(0x6); // Reserved 339 INSERT_PADDING_BYTES(0x6); // Reserved
332 u64 sampling_number; 340 u64 sampling_number{};
333 }; 341 };
334 static_assert(sizeof(NfcXcdDeviceHandleStateImpl) == 0x18, 342 static_assert(sizeof(NfcXcdDeviceHandleStateImpl) == 0x18,
335 "NfcXcdDeviceHandleStateImpl is an invalid size"); 343 "NfcXcdDeviceHandleStateImpl is an invalid size");
@@ -366,8 +374,8 @@ private:
366 }; 374 };
367 375
368 struct AppletFooterUi { 376 struct AppletFooterUi {
369 AppletFooterUiAttributes attributes; 377 AppletFooterUiAttributes attributes{};
370 AppletFooterUiType type; 378 AppletFooterUiType type{AppletFooterUiType::None};
371 INSERT_PADDING_BYTES(0x5B); // Reserved 379 INSERT_PADDING_BYTES(0x5B); // Reserved
372 }; 380 };
373 static_assert(sizeof(AppletFooterUi) == 0x60, "AppletFooterUi is an invalid size"); 381 static_assert(sizeof(AppletFooterUi) == 0x60, "AppletFooterUi is an invalid size");
@@ -404,41 +412,41 @@ private:
404 412
405 // This is nn::hid::detail::NpadInternalState 413 // This is nn::hid::detail::NpadInternalState
406 struct NpadInternalState { 414 struct NpadInternalState {
407 Core::HID::NpadStyleTag style_tag; 415 Core::HID::NpadStyleTag style_tag{Core::HID::NpadStyleSet::None};
408 NpadJoyAssignmentMode assignment_mode; 416 NpadJoyAssignmentMode assignment_mode{NpadJoyAssignmentMode::Dual};
409 NpadFullKeyColorState fullkey_color; 417 NpadFullKeyColorState fullkey_color{};
410 NpadJoyColorState joycon_color; 418 NpadJoyColorState joycon_color{};
411 Lifo<NPadGenericState, hid_entry_count> fullkey_lifo; 419 Lifo<NPadGenericState, hid_entry_count> fullkey_lifo{};
412 Lifo<NPadGenericState, hid_entry_count> handheld_lifo; 420 Lifo<NPadGenericState, hid_entry_count> handheld_lifo{};
413 Lifo<NPadGenericState, hid_entry_count> joy_dual_lifo; 421 Lifo<NPadGenericState, hid_entry_count> joy_dual_lifo{};
414 Lifo<NPadGenericState, hid_entry_count> joy_left_lifo; 422 Lifo<NPadGenericState, hid_entry_count> joy_left_lifo{};
415 Lifo<NPadGenericState, hid_entry_count> joy_right_lifo; 423 Lifo<NPadGenericState, hid_entry_count> joy_right_lifo{};
416 Lifo<NPadGenericState, hid_entry_count> palma_lifo; 424 Lifo<NPadGenericState, hid_entry_count> palma_lifo{};
417 Lifo<NPadGenericState, hid_entry_count> system_ext_lifo; 425 Lifo<NPadGenericState, hid_entry_count> system_ext_lifo{};
418 Lifo<SixAxisSensorState, hid_entry_count> sixaxis_fullkey_lifo; 426 Lifo<SixAxisSensorState, hid_entry_count> sixaxis_fullkey_lifo{};
419 Lifo<SixAxisSensorState, hid_entry_count> sixaxis_handheld_lifo; 427 Lifo<SixAxisSensorState, hid_entry_count> sixaxis_handheld_lifo{};
420 Lifo<SixAxisSensorState, hid_entry_count> sixaxis_dual_left_lifo; 428 Lifo<SixAxisSensorState, hid_entry_count> sixaxis_dual_left_lifo{};
421 Lifo<SixAxisSensorState, hid_entry_count> sixaxis_dual_right_lifo; 429 Lifo<SixAxisSensorState, hid_entry_count> sixaxis_dual_right_lifo{};
422 Lifo<SixAxisSensorState, hid_entry_count> sixaxis_left_lifo; 430 Lifo<SixAxisSensorState, hid_entry_count> sixaxis_left_lifo{};
423 Lifo<SixAxisSensorState, hid_entry_count> sixaxis_right_lifo; 431 Lifo<SixAxisSensorState, hid_entry_count> sixaxis_right_lifo{};
424 DeviceType device_type; 432 DeviceType device_type{};
425 INSERT_PADDING_BYTES(0x4); // Reserved 433 INSERT_PADDING_BYTES(0x4); // Reserved
426 NPadSystemProperties system_properties; 434 NPadSystemProperties system_properties{};
427 NpadSystemButtonProperties button_properties; 435 NpadSystemButtonProperties button_properties{};
428 Core::HID::NpadBatteryLevel battery_level_dual; 436 Core::HID::NpadBatteryLevel battery_level_dual{};
429 Core::HID::NpadBatteryLevel battery_level_left; 437 Core::HID::NpadBatteryLevel battery_level_left{};
430 Core::HID::NpadBatteryLevel battery_level_right; 438 Core::HID::NpadBatteryLevel battery_level_right{};
431 union { 439 union {
432 Lifo<NfcXcdDeviceHandleStateImpl, 0x2> nfc_xcd_device_lifo{}; 440 AppletFooterUi applet_footer{};
433 AppletFooterUi applet_footer; 441 Lifo<NfcXcdDeviceHandleStateImpl, 0x2> nfc_xcd_device_lifo;
434 }; 442 };
435 INSERT_PADDING_BYTES(0x20); // Unknown 443 INSERT_PADDING_BYTES(0x20); // Unknown
436 Lifo<NpadGcTriggerState, hid_entry_count> gc_trigger_lifo; 444 Lifo<NpadGcTriggerState, hid_entry_count> gc_trigger_lifo{};
437 NpadLarkType lark_type_l_and_main; 445 NpadLarkType lark_type_l_and_main{};
438 NpadLarkType lark_type_r; 446 NpadLarkType lark_type_r{};
439 NpadLuciaType lucia_type; 447 NpadLuciaType lucia_type{};
440 NpadLagonType lagon_type; 448 NpadLagonType lagon_type{};
441 NpadLagerType lager_type; 449 NpadLagerType lager_type{};
442 // FW 13.x Investigate there is some sort of bitflag related to joycons 450 // FW 13.x Investigate there is some sort of bitflag related to joycons
443 INSERT_PADDING_BYTES(0x4); 451 INSERT_PADDING_BYTES(0x4);
444 INSERT_PADDING_BYTES(0xc08); // Unknown 452 INSERT_PADDING_BYTES(0xc08); // Unknown
@@ -451,6 +459,12 @@ private:
451 std::chrono::steady_clock::time_point last_vibration_timepoint{}; 459 std::chrono::steady_clock::time_point last_vibration_timepoint{};
452 }; 460 };
453 461
462 struct SixaxisParameters {
463 bool is_fusion_enabled{true};
464 Core::HID::SixAxisSensorFusionParameters fusion{};
465 GyroscopeZeroDriftMode gyroscope_zero_drift_mode{GyroscopeZeroDriftMode::Standard};
466 };
467
454 struct NpadControllerData { 468 struct NpadControllerData {
455 Core::HID::EmulatedController* device; 469 Core::HID::EmulatedController* device;
456 Kernel::KEvent* styleset_changed_event{}; 470 Kernel::KEvent* styleset_changed_event{};
@@ -467,9 +481,12 @@ private:
467 // Motion parameters 481 // Motion parameters
468 bool sixaxis_at_rest{true}; 482 bool sixaxis_at_rest{true};
469 bool sixaxis_sensor_enabled{true}; 483 bool sixaxis_sensor_enabled{true};
470 bool sixaxis_fusion_enabled{false}; 484 SixaxisParameters sixaxis_fullkey{};
471 Core::HID::SixAxisSensorFusionParameters sixaxis_fusion{}; 485 SixaxisParameters sixaxis_handheld{};
472 GyroscopeZeroDriftMode gyroscope_zero_drift_mode{GyroscopeZeroDriftMode::Standard}; 486 SixaxisParameters sixaxis_dual_left{};
487 SixaxisParameters sixaxis_dual_right{};
488 SixaxisParameters sixaxis_left{};
489 SixaxisParameters sixaxis_right{};
473 490
474 // Current pad state 491 // Current pad state
475 NPadGenericState npad_pad_state{}; 492 NPadGenericState npad_pad_state{};
@@ -481,6 +498,7 @@ private:
481 SixAxisSensorState sixaxis_dual_right_state{}; 498 SixAxisSensorState sixaxis_dual_right_state{};
482 SixAxisSensorState sixaxis_left_lifo_state{}; 499 SixAxisSensorState sixaxis_left_lifo_state{};
483 SixAxisSensorState sixaxis_right_lifo_state{}; 500 SixAxisSensorState sixaxis_right_lifo_state{};
501
484 int callback_key; 502 int callback_key;
485 }; 503 };
486 504
@@ -511,7 +529,8 @@ private:
511 NpadHandheldActivationMode handheld_activation_mode{NpadHandheldActivationMode::Dual}; 529 NpadHandheldActivationMode handheld_activation_mode{NpadHandheldActivationMode::Dual};
512 NpadCommunicationMode communication_mode{NpadCommunicationMode::Default}; 530 NpadCommunicationMode communication_mode{NpadCommunicationMode::Default};
513 bool permit_vibration_session_enabled{false}; 531 bool permit_vibration_session_enabled{false};
514 bool analog_stick_use_center_clamp{}; 532 bool analog_stick_use_center_clamp{false};
515 bool is_in_lr_assignment_mode{false}; 533 bool is_in_lr_assignment_mode{false};
534 bool is_controller_initialized{false};
516}; 535};
517} // namespace Service::HID 536} // namespace Service::HID
diff --git a/src/core/hle/service/hid/errors.h b/src/core/hle/service/hid/errors.h
index 3583642e7..7c5fb3e52 100644
--- a/src/core/hle/service/hid/errors.h
+++ b/src/core/hle/service/hid/errors.h
@@ -8,6 +8,8 @@
8 8
9namespace Service::HID { 9namespace Service::HID {
10 10
11constexpr ResultCode ERR_NPAD_NOT_CONNECTED{ErrorModule::HID, 710}; 11constexpr ResultCode NpadInvalidHandle{ErrorModule::HID, 100};
12constexpr ResultCode InvalidSixAxisFusionRange{ErrorModule::HID, 423};
13constexpr ResultCode NpadNotConnected{ErrorModule::HID, 710};
12 14
13} // namespace Service::HID 15} // namespace Service::HID
diff --git a/src/core/hle/service/hid/hid.cpp b/src/core/hle/service/hid/hid.cpp
index d9202ea6c..baf21df62 100644
--- a/src/core/hle/service/hid/hid.cpp
+++ b/src/core/hle/service/hid/hid.cpp
@@ -16,6 +16,7 @@
16#include "core/hle/kernel/kernel.h" 16#include "core/hle/kernel/kernel.h"
17#include "core/hle/service/hid/errors.h" 17#include "core/hle/service/hid/errors.h"
18#include "core/hle/service/hid/hid.h" 18#include "core/hle/service/hid/hid.h"
19#include "core/hle/service/hid/hidbus.h"
19#include "core/hle/service/hid/irs.h" 20#include "core/hle/service/hid/irs.h"
20#include "core/hle/service/hid/xcd.h" 21#include "core/hle/service/hid/xcd.h"
21#include "core/memory.h" 22#include "core/memory.h"
@@ -247,7 +248,7 @@ Hid::Hid(Core::System& system_)
247 {65, nullptr, "GetJoySixAxisSensorLifoHandle"}, 248 {65, nullptr, "GetJoySixAxisSensorLifoHandle"},
248 {66, &Hid::StartSixAxisSensor, "StartSixAxisSensor"}, 249 {66, &Hid::StartSixAxisSensor, "StartSixAxisSensor"},
249 {67, &Hid::StopSixAxisSensor, "StopSixAxisSensor"}, 250 {67, &Hid::StopSixAxisSensor, "StopSixAxisSensor"},
250 {68, nullptr, "IsSixAxisSensorFusionEnabled"}, 251 {68, &Hid::IsSixAxisSensorFusionEnabled, "IsSixAxisSensorFusionEnabled"},
251 {69, &Hid::EnableSixAxisSensorFusion, "EnableSixAxisSensorFusion"}, 252 {69, &Hid::EnableSixAxisSensorFusion, "EnableSixAxisSensorFusion"},
252 {70, &Hid::SetSixAxisSensorFusionParameters, "SetSixAxisSensorFusionParameters"}, 253 {70, &Hid::SetSixAxisSensorFusionParameters, "SetSixAxisSensorFusionParameters"},
253 {71, &Hid::GetSixAxisSensorFusionParameters, "GetSixAxisSensorFusionParameters"}, 254 {71, &Hid::GetSixAxisSensorFusionParameters, "GetSixAxisSensorFusionParameters"},
@@ -527,8 +528,8 @@ void Hid::StartSixAxisSensor(Kernel::HLERequestContext& ctx) {
527 528
528 const auto parameters{rp.PopRaw<Parameters>()}; 529 const auto parameters{rp.PopRaw<Parameters>()};
529 530
530 applet_resource->GetController<Controller_NPad>(HidController::NPad) 531 auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad);
531 .SetSixAxisEnabled(parameters.sixaxis_handle, true); 532 const auto result = controller.SetSixAxisEnabled(parameters.sixaxis_handle, true);
532 533
533 LOG_DEBUG(Service_HID, 534 LOG_DEBUG(Service_HID,
534 "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}", 535 "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}",
@@ -536,7 +537,7 @@ void Hid::StartSixAxisSensor(Kernel::HLERequestContext& ctx) {
536 parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id); 537 parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id);
537 538
538 IPC::ResponseBuilder rb{ctx, 2}; 539 IPC::ResponseBuilder rb{ctx, 2};
539 rb.Push(ResultSuccess); 540 rb.Push(result);
540} 541}
541 542
542void Hid::StopSixAxisSensor(Kernel::HLERequestContext& ctx) { 543void Hid::StopSixAxisSensor(Kernel::HLERequestContext& ctx) {
@@ -550,8 +551,8 @@ void Hid::StopSixAxisSensor(Kernel::HLERequestContext& ctx) {
550 551
551 const auto parameters{rp.PopRaw<Parameters>()}; 552 const auto parameters{rp.PopRaw<Parameters>()};
552 553
553 applet_resource->GetController<Controller_NPad>(HidController::NPad) 554 auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad);
554 .SetSixAxisEnabled(parameters.sixaxis_handle, false); 555 const auto result = controller.SetSixAxisEnabled(parameters.sixaxis_handle, false);
555 556
556 LOG_DEBUG(Service_HID, 557 LOG_DEBUG(Service_HID,
557 "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}", 558 "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}",
@@ -559,7 +560,33 @@ void Hid::StopSixAxisSensor(Kernel::HLERequestContext& ctx) {
559 parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id); 560 parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id);
560 561
561 IPC::ResponseBuilder rb{ctx, 2}; 562 IPC::ResponseBuilder rb{ctx, 2};
562 rb.Push(ResultSuccess); 563 rb.Push(result);
564}
565
566void Hid::IsSixAxisSensorFusionEnabled(Kernel::HLERequestContext& ctx) {
567 IPC::RequestParser rp{ctx};
568 struct Parameters {
569 Core::HID::SixAxisSensorHandle sixaxis_handle;
570 INSERT_PADDING_WORDS_NOINIT(1);
571 u64 applet_resource_user_id;
572 };
573 static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
574
575 const auto parameters{rp.PopRaw<Parameters>()};
576
577 bool is_enabled{};
578 auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad);
579 const auto result =
580 controller.IsSixAxisSensorFusionEnabled(parameters.sixaxis_handle, is_enabled);
581
582 LOG_DEBUG(Service_HID,
583 "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}",
584 parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id,
585 parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id);
586
587 IPC::ResponseBuilder rb{ctx, 3};
588 rb.Push(result);
589 rb.Push(is_enabled);
563} 590}
564 591
565void Hid::EnableSixAxisSensorFusion(Kernel::HLERequestContext& ctx) { 592void Hid::EnableSixAxisSensorFusion(Kernel::HLERequestContext& ctx) {
@@ -574,9 +601,9 @@ void Hid::EnableSixAxisSensorFusion(Kernel::HLERequestContext& ctx) {
574 601
575 const auto parameters{rp.PopRaw<Parameters>()}; 602 const auto parameters{rp.PopRaw<Parameters>()};
576 603
577 applet_resource->GetController<Controller_NPad>(HidController::NPad) 604 auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad);
578 .SetSixAxisFusionEnabled(parameters.sixaxis_handle, 605 const auto result = controller.SetSixAxisFusionEnabled(parameters.sixaxis_handle,
579 parameters.enable_sixaxis_sensor_fusion); 606 parameters.enable_sixaxis_sensor_fusion);
580 607
581 LOG_DEBUG(Service_HID, 608 LOG_DEBUG(Service_HID,
582 "called, enable_sixaxis_sensor_fusion={}, npad_type={}, npad_id={}, " 609 "called, enable_sixaxis_sensor_fusion={}, npad_type={}, npad_id={}, "
@@ -586,7 +613,7 @@ void Hid::EnableSixAxisSensorFusion(Kernel::HLERequestContext& ctx) {
586 parameters.applet_resource_user_id); 613 parameters.applet_resource_user_id);
587 614
588 IPC::ResponseBuilder rb{ctx, 2}; 615 IPC::ResponseBuilder rb{ctx, 2};
589 rb.Push(ResultSuccess); 616 rb.Push(result);
590} 617}
591 618
592void Hid::SetSixAxisSensorFusionParameters(Kernel::HLERequestContext& ctx) { 619void Hid::SetSixAxisSensorFusionParameters(Kernel::HLERequestContext& ctx) {
@@ -601,8 +628,9 @@ void Hid::SetSixAxisSensorFusionParameters(Kernel::HLERequestContext& ctx) {
601 628
602 const auto parameters{rp.PopRaw<Parameters>()}; 629 const auto parameters{rp.PopRaw<Parameters>()};
603 630
604 applet_resource->GetController<Controller_NPad>(HidController::NPad) 631 auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad);
605 .SetSixAxisFusionParameters(parameters.sixaxis_handle, parameters.sixaxis_fusion); 632 const auto result =
633 controller.SetSixAxisFusionParameters(parameters.sixaxis_handle, parameters.sixaxis_fusion);
606 634
607 LOG_DEBUG(Service_HID, 635 LOG_DEBUG(Service_HID,
608 "called, npad_type={}, npad_id={}, device_index={}, parameter1={}, " 636 "called, npad_type={}, npad_id={}, device_index={}, parameter1={}, "
@@ -612,7 +640,7 @@ void Hid::SetSixAxisSensorFusionParameters(Kernel::HLERequestContext& ctx) {
612 parameters.sixaxis_fusion.parameter2, parameters.applet_resource_user_id); 640 parameters.sixaxis_fusion.parameter2, parameters.applet_resource_user_id);
613 641
614 IPC::ResponseBuilder rb{ctx, 2}; 642 IPC::ResponseBuilder rb{ctx, 2};
615 rb.Push(ResultSuccess); 643 rb.Push(result);
616} 644}
617 645
618void Hid::GetSixAxisSensorFusionParameters(Kernel::HLERequestContext& ctx) { 646void Hid::GetSixAxisSensorFusionParameters(Kernel::HLERequestContext& ctx) {
@@ -626,9 +654,11 @@ void Hid::GetSixAxisSensorFusionParameters(Kernel::HLERequestContext& ctx) {
626 654
627 const auto parameters{rp.PopRaw<Parameters>()}; 655 const auto parameters{rp.PopRaw<Parameters>()};
628 656
629 const auto sixaxis_fusion_parameters = 657 Core::HID::SixAxisSensorFusionParameters fusion_parameters{};
630 applet_resource->GetController<Controller_NPad>(HidController::NPad) 658 const auto& controller =
631 .GetSixAxisFusionParameters(parameters.sixaxis_handle); 659 GetAppletResource()->GetController<Controller_NPad>(HidController::NPad);
660 const auto result =
661 controller.GetSixAxisFusionParameters(parameters.sixaxis_handle, fusion_parameters);
632 662
633 LOG_DEBUG(Service_HID, 663 LOG_DEBUG(Service_HID,
634 "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}", 664 "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}",
@@ -636,8 +666,8 @@ void Hid::GetSixAxisSensorFusionParameters(Kernel::HLERequestContext& ctx) {
636 parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id); 666 parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id);
637 667
638 IPC::ResponseBuilder rb{ctx, 4}; 668 IPC::ResponseBuilder rb{ctx, 4};
639 rb.Push(ResultSuccess); 669 rb.Push(result);
640 rb.PushRaw(sixaxis_fusion_parameters); 670 rb.PushRaw(fusion_parameters);
641} 671}
642 672
643void Hid::ResetSixAxisSensorFusionParameters(Kernel::HLERequestContext& ctx) { 673void Hid::ResetSixAxisSensorFusionParameters(Kernel::HLERequestContext& ctx) {
@@ -651,8 +681,15 @@ void Hid::ResetSixAxisSensorFusionParameters(Kernel::HLERequestContext& ctx) {
651 681
652 const auto parameters{rp.PopRaw<Parameters>()}; 682 const auto parameters{rp.PopRaw<Parameters>()};
653 683
654 applet_resource->GetController<Controller_NPad>(HidController::NPad) 684 // Since these parameters are unknow just use what HW outputs
655 .ResetSixAxisFusionParameters(parameters.sixaxis_handle); 685 const Core::HID::SixAxisSensorFusionParameters fusion_parameters{
686 .parameter1 = 0.03f,
687 .parameter2 = 0.4f,
688 };
689 auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad);
690 const auto result1 =
691 controller.SetSixAxisFusionParameters(parameters.sixaxis_handle, fusion_parameters);
692 const auto result2 = controller.SetSixAxisFusionEnabled(parameters.sixaxis_handle, true);
656 693
657 LOG_DEBUG(Service_HID, 694 LOG_DEBUG(Service_HID,
658 "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}", 695 "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}",
@@ -660,6 +697,14 @@ void Hid::ResetSixAxisSensorFusionParameters(Kernel::HLERequestContext& ctx) {
660 parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id); 697 parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id);
661 698
662 IPC::ResponseBuilder rb{ctx, 2}; 699 IPC::ResponseBuilder rb{ctx, 2};
700 if (result1.IsError()) {
701 rb.Push(result1);
702 return;
703 }
704 if (result2.IsError()) {
705 rb.Push(result2);
706 return;
707 }
663 rb.Push(ResultSuccess); 708 rb.Push(ResultSuccess);
664} 709}
665 710
@@ -669,8 +714,8 @@ void Hid::SetGyroscopeZeroDriftMode(Kernel::HLERequestContext& ctx) {
669 const auto drift_mode{rp.PopEnum<Controller_NPad::GyroscopeZeroDriftMode>()}; 714 const auto drift_mode{rp.PopEnum<Controller_NPad::GyroscopeZeroDriftMode>()};
670 const auto applet_resource_user_id{rp.Pop<u64>()}; 715 const auto applet_resource_user_id{rp.Pop<u64>()};
671 716
672 applet_resource->GetController<Controller_NPad>(HidController::NPad) 717 auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad);
673 .SetGyroscopeZeroDriftMode(sixaxis_handle, drift_mode); 718 const auto result = controller.SetGyroscopeZeroDriftMode(sixaxis_handle, drift_mode);
674 719
675 LOG_DEBUG(Service_HID, 720 LOG_DEBUG(Service_HID,
676 "called, npad_type={}, npad_id={}, device_index={}, drift_mode={}, " 721 "called, npad_type={}, npad_id={}, device_index={}, drift_mode={}, "
@@ -679,7 +724,7 @@ void Hid::SetGyroscopeZeroDriftMode(Kernel::HLERequestContext& ctx) {
679 drift_mode, applet_resource_user_id); 724 drift_mode, applet_resource_user_id);
680 725
681 IPC::ResponseBuilder rb{ctx, 2}; 726 IPC::ResponseBuilder rb{ctx, 2};
682 rb.Push(ResultSuccess); 727 rb.Push(result);
683} 728}
684 729
685void Hid::GetGyroscopeZeroDriftMode(Kernel::HLERequestContext& ctx) { 730void Hid::GetGyroscopeZeroDriftMode(Kernel::HLERequestContext& ctx) {
@@ -693,15 +738,18 @@ void Hid::GetGyroscopeZeroDriftMode(Kernel::HLERequestContext& ctx) {
693 738
694 const auto parameters{rp.PopRaw<Parameters>()}; 739 const auto parameters{rp.PopRaw<Parameters>()};
695 740
741 auto drift_mode{Controller_NPad::GyroscopeZeroDriftMode::Standard};
742 auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad);
743 const auto result = controller.GetGyroscopeZeroDriftMode(parameters.sixaxis_handle, drift_mode);
744
696 LOG_DEBUG(Service_HID, 745 LOG_DEBUG(Service_HID,
697 "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}", 746 "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}",
698 parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id, 747 parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id,
699 parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id); 748 parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id);
700 749
701 IPC::ResponseBuilder rb{ctx, 3}; 750 IPC::ResponseBuilder rb{ctx, 3};
702 rb.Push(ResultSuccess); 751 rb.Push(result);
703 rb.PushEnum(applet_resource->GetController<Controller_NPad>(HidController::NPad) 752 rb.PushEnum(drift_mode);
704 .GetGyroscopeZeroDriftMode(parameters.sixaxis_handle));
705} 753}
706 754
707void Hid::ResetGyroscopeZeroDriftMode(Kernel::HLERequestContext& ctx) { 755void Hid::ResetGyroscopeZeroDriftMode(Kernel::HLERequestContext& ctx) {
@@ -714,10 +762,10 @@ void Hid::ResetGyroscopeZeroDriftMode(Kernel::HLERequestContext& ctx) {
714 static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size."); 762 static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
715 763
716 const auto parameters{rp.PopRaw<Parameters>()}; 764 const auto parameters{rp.PopRaw<Parameters>()};
717 const auto drift_mode{Controller_NPad::GyroscopeZeroDriftMode::Standard};
718 765
719 applet_resource->GetController<Controller_NPad>(HidController::NPad) 766 const auto drift_mode{Controller_NPad::GyroscopeZeroDriftMode::Standard};
720 .SetGyroscopeZeroDriftMode(parameters.sixaxis_handle, drift_mode); 767 auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad);
768 const auto result = controller.SetGyroscopeZeroDriftMode(parameters.sixaxis_handle, drift_mode);
721 769
722 LOG_DEBUG(Service_HID, 770 LOG_DEBUG(Service_HID,
723 "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}", 771 "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}",
@@ -725,7 +773,7 @@ void Hid::ResetGyroscopeZeroDriftMode(Kernel::HLERequestContext& ctx) {
725 parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id); 773 parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id);
726 774
727 IPC::ResponseBuilder rb{ctx, 2}; 775 IPC::ResponseBuilder rb{ctx, 2};
728 rb.Push(ResultSuccess); 776 rb.Push(result);
729} 777}
730 778
731void Hid::IsSixAxisSensorAtRest(Kernel::HLERequestContext& ctx) { 779void Hid::IsSixAxisSensorAtRest(Kernel::HLERequestContext& ctx) {
@@ -739,15 +787,18 @@ void Hid::IsSixAxisSensorAtRest(Kernel::HLERequestContext& ctx) {
739 787
740 const auto parameters{rp.PopRaw<Parameters>()}; 788 const auto parameters{rp.PopRaw<Parameters>()};
741 789
790 bool is_at_rest{};
791 auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad);
792 const auto result = controller.IsSixAxisSensorAtRest(parameters.sixaxis_handle, is_at_rest);
793
742 LOG_DEBUG(Service_HID, 794 LOG_DEBUG(Service_HID,
743 "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}", 795 "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}",
744 parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id, 796 parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id,
745 parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id); 797 parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id);
746 798
747 IPC::ResponseBuilder rb{ctx, 3}; 799 IPC::ResponseBuilder rb{ctx, 3};
748 rb.Push(ResultSuccess); 800 rb.Push(result);
749 rb.Push(applet_resource->GetController<Controller_NPad>(HidController::NPad) 801 rb.Push(is_at_rest);
750 .IsSixAxisSensorAtRest(parameters.sixaxis_handle));
751} 802}
752 803
753void Hid::IsFirmwareUpdateAvailableForSixAxisSensor(Kernel::HLERequestContext& ctx) { 804void Hid::IsFirmwareUpdateAvailableForSixAxisSensor(Kernel::HLERequestContext& ctx) {
@@ -761,6 +812,11 @@ void Hid::IsFirmwareUpdateAvailableForSixAxisSensor(Kernel::HLERequestContext& c
761 812
762 const auto parameters{rp.PopRaw<Parameters>()}; 813 const auto parameters{rp.PopRaw<Parameters>()};
763 814
815 bool is_firmware_available{};
816 auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad);
817 const auto result = controller.IsFirmwareUpdateAvailableForSixAxisSensor(
818 parameters.sixaxis_handle, is_firmware_available);
819
764 LOG_WARNING( 820 LOG_WARNING(
765 Service_HID, 821 Service_HID,
766 "(STUBBED) called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}", 822 "(STUBBED) called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}",
@@ -768,8 +824,8 @@ void Hid::IsFirmwareUpdateAvailableForSixAxisSensor(Kernel::HLERequestContext& c
768 parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id); 824 parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id);
769 825
770 IPC::ResponseBuilder rb{ctx, 3}; 826 IPC::ResponseBuilder rb{ctx, 3};
771 rb.Push(ResultSuccess); 827 rb.Push(result);
772 rb.Push(false); 828 rb.Push(is_firmware_available);
773} 829}
774 830
775void Hid::ActivateGesture(Kernel::HLERequestContext& ctx) { 831void Hid::ActivateGesture(Kernel::HLERequestContext& ctx) {
@@ -878,6 +934,10 @@ void Hid::AcquireNpadStyleSetUpdateEventHandle(Kernel::HLERequestContext& ctx) {
878 LOG_DEBUG(Service_HID, "called, npad_id={}, applet_resource_user_id={}, unknown={}", 934 LOG_DEBUG(Service_HID, "called, npad_id={}, applet_resource_user_id={}, unknown={}",
879 parameters.npad_id, parameters.applet_resource_user_id, parameters.unknown); 935 parameters.npad_id, parameters.applet_resource_user_id, parameters.unknown);
880 936
937 // Games expect this event to be signaled after calling this function
938 applet_resource->GetController<Controller_NPad>(HidController::NPad)
939 .SignalStyleSetChangedEvent(parameters.npad_id);
940
881 IPC::ResponseBuilder rb{ctx, 2, 1}; 941 IPC::ResponseBuilder rb{ctx, 2, 1};
882 rb.Push(ResultSuccess); 942 rb.Push(ResultSuccess);
883 rb.PushCopyObjects(applet_resource->GetController<Controller_NPad>(HidController::NPad) 943 rb.PushCopyObjects(applet_resource->GetController<Controller_NPad>(HidController::NPad)
@@ -1115,7 +1175,7 @@ void Hid::SwapNpadAssignment(Kernel::HLERequestContext& ctx) {
1115 rb.Push(ResultSuccess); 1175 rb.Push(ResultSuccess);
1116 } else { 1176 } else {
1117 LOG_ERROR(Service_HID, "Npads are not connected!"); 1177 LOG_ERROR(Service_HID, "Npads are not connected!");
1118 rb.Push(ERR_NPAD_NOT_CONNECTED); 1178 rb.Push(NpadNotConnected);
1119 } 1179 }
1120} 1180}
1121 1181
@@ -2124,32 +2184,6 @@ public:
2124 } 2184 }
2125}; 2185};
2126 2186
2127class HidBus final : public ServiceFramework<HidBus> {
2128public:
2129 explicit HidBus(Core::System& system_) : ServiceFramework{system_, "hidbus"} {
2130 // clang-format off
2131 static const FunctionInfo functions[] = {
2132 {1, nullptr, "GetBusHandle"},
2133 {2, nullptr, "IsExternalDeviceConnected"},
2134 {3, nullptr, "Initialize"},
2135 {4, nullptr, "Finalize"},
2136 {5, nullptr, "EnableExternalDevice"},
2137 {6, nullptr, "GetExternalDeviceId"},
2138 {7, nullptr, "SendCommandAsync"},
2139 {8, nullptr, "GetSendCommandAsynceResult"},
2140 {9, nullptr, "SetEventForSendCommandAsycResult"},
2141 {10, nullptr, "GetSharedMemoryHandle"},
2142 {11, nullptr, "EnableJoyPollingReceiveMode"},
2143 {12, nullptr, "DisableJoyPollingReceiveMode"},
2144 {13, nullptr, "GetPollingData"},
2145 {14, nullptr, "SetStatusManagerType"},
2146 };
2147 // clang-format on
2148
2149 RegisterHandlers(functions);
2150 }
2151};
2152
2153void InstallInterfaces(SM::ServiceManager& service_manager, Core::System& system) { 2187void InstallInterfaces(SM::ServiceManager& service_manager, Core::System& system) {
2154 std::make_shared<Hid>(system)->InstallAsService(service_manager); 2188 std::make_shared<Hid>(system)->InstallAsService(service_manager);
2155 std::make_shared<HidBus>(system)->InstallAsService(service_manager); 2189 std::make_shared<HidBus>(system)->InstallAsService(service_manager);
diff --git a/src/core/hle/service/hid/hid.h b/src/core/hle/service/hid/hid.h
index c281081a7..666cb6ff5 100644
--- a/src/core/hle/service/hid/hid.h
+++ b/src/core/hle/service/hid/hid.h
@@ -103,6 +103,7 @@ private:
103 void DeactivateSixAxisSensor(Kernel::HLERequestContext& ctx); 103 void DeactivateSixAxisSensor(Kernel::HLERequestContext& ctx);
104 void StartSixAxisSensor(Kernel::HLERequestContext& ctx); 104 void StartSixAxisSensor(Kernel::HLERequestContext& ctx);
105 void StopSixAxisSensor(Kernel::HLERequestContext& ctx); 105 void StopSixAxisSensor(Kernel::HLERequestContext& ctx);
106 void IsSixAxisSensorFusionEnabled(Kernel::HLERequestContext& ctx);
106 void EnableSixAxisSensorFusion(Kernel::HLERequestContext& ctx); 107 void EnableSixAxisSensorFusion(Kernel::HLERequestContext& ctx);
107 void SetSixAxisSensorFusionParameters(Kernel::HLERequestContext& ctx); 108 void SetSixAxisSensorFusionParameters(Kernel::HLERequestContext& ctx);
108 void GetSixAxisSensorFusionParameters(Kernel::HLERequestContext& ctx); 109 void GetSixAxisSensorFusionParameters(Kernel::HLERequestContext& ctx);
diff --git a/src/core/hle/service/hid/hidbus.cpp b/src/core/hle/service/hid/hidbus.cpp
new file mode 100644
index 000000000..af7662a15
--- /dev/null
+++ b/src/core/hle/service/hid/hidbus.cpp
@@ -0,0 +1,531 @@
1// Copyright 2021 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include "common/logging/log.h"
6#include "common/settings.h"
7#include "core/core.h"
8#include "core/core_timing.h"
9#include "core/core_timing_util.h"
10#include "core/hid/hid_types.h"
11#include "core/hle/ipc_helpers.h"
12#include "core/hle/kernel/k_event.h"
13#include "core/hle/kernel/k_readable_event.h"
14#include "core/hle/kernel/k_shared_memory.h"
15#include "core/hle/kernel/k_transfer_memory.h"
16#include "core/hle/service/hid/hidbus.h"
17#include "core/hle/service/hid/hidbus/ringcon.h"
18#include "core/hle/service/hid/hidbus/starlink.h"
19#include "core/hle/service/hid/hidbus/stubbed.h"
20#include "core/hle/service/service.h"
21#include "core/memory.h"
22
23namespace Service::HID {
24// (15ms, 66Hz)
25constexpr auto hidbus_update_ns = std::chrono::nanoseconds{15 * 1000 * 1000};
26
27HidBus::HidBus(Core::System& system_)
28 : ServiceFramework{system_, "hidbus"}, service_context{system_, service_name} {
29
30 // clang-format off
31 static const FunctionInfo functions[] = {
32 {1, &HidBus::GetBusHandle, "GetBusHandle"},
33 {2, &HidBus::IsExternalDeviceConnected, "IsExternalDeviceConnected"},
34 {3, &HidBus::Initialize, "Initialize"},
35 {4, &HidBus::Finalize, "Finalize"},
36 {5, &HidBus::EnableExternalDevice, "EnableExternalDevice"},
37 {6, &HidBus::GetExternalDeviceId, "GetExternalDeviceId"},
38 {7, &HidBus::SendCommandAsync, "SendCommandAsync"},
39 {8, &HidBus::GetSendCommandAsynceResult, "GetSendCommandAsynceResult"},
40 {9, &HidBus::SetEventForSendCommandAsycResult, "SetEventForSendCommandAsycResult"},
41 {10, &HidBus::GetSharedMemoryHandle, "GetSharedMemoryHandle"},
42 {11, &HidBus::EnableJoyPollingReceiveMode, "EnableJoyPollingReceiveMode"},
43 {12, &HidBus::DisableJoyPollingReceiveMode, "DisableJoyPollingReceiveMode"},
44 {13, nullptr, "GetPollingData"},
45 {14, &HidBus::SetStatusManagerType, "SetStatusManagerType"},
46 };
47 // clang-format on
48
49 RegisterHandlers(functions);
50
51 // Register update callbacks
52 hidbus_update_event = Core::Timing::CreateEvent(
53 "Hidbus::UpdateCallback",
54 [this](std::uintptr_t user_data, std::chrono::nanoseconds ns_late) {
55 const auto guard = LockService();
56 UpdateHidbus(user_data, ns_late);
57 });
58
59 system_.CoreTiming().ScheduleEvent(hidbus_update_ns, hidbus_update_event);
60}
61
62HidBus::~HidBus() {
63 system.CoreTiming().UnscheduleEvent(hidbus_update_event, 0);
64}
65
66void HidBus::UpdateHidbus(std::uintptr_t user_data, std::chrono::nanoseconds ns_late) {
67 auto& core_timing = system.CoreTiming();
68
69 if (is_hidbus_enabled) {
70 for (std::size_t i = 0; i < devices.size(); ++i) {
71 if (!devices[i].is_device_initializated) {
72 continue;
73 }
74 auto& device = devices[i].device;
75 device->OnUpdate();
76 auto& cur_entry = hidbus_status.entries[devices[i].handle.internal_index];
77 cur_entry.is_polling_mode = device->IsPollingMode();
78 cur_entry.polling_mode = device->GetPollingMode();
79 cur_entry.is_enabled = device->IsEnabled();
80
81 u8* shared_memory = system.Kernel().GetHidBusSharedMem().GetPointer();
82 std::memcpy(shared_memory + (i * sizeof(HidbusStatusManagerEntry)), &hidbus_status,
83 sizeof(HidbusStatusManagerEntry));
84 }
85 }
86
87 // If ns_late is higher than the update rate ignore the delay
88 if (ns_late > hidbus_update_ns) {
89 ns_late = {};
90 }
91
92 core_timing.ScheduleEvent(hidbus_update_ns - ns_late, hidbus_update_event);
93}
94
95std::optional<std::size_t> HidBus::GetDeviceIndexFromHandle(BusHandle handle) const {
96 for (std::size_t i = 0; i < devices.size(); ++i) {
97 const auto& device_handle = devices[i].handle;
98 if (handle.abstracted_pad_id == device_handle.abstracted_pad_id &&
99 handle.internal_index == device_handle.internal_index &&
100 handle.player_number == device_handle.player_number &&
101 handle.bus_type == device_handle.bus_type &&
102 handle.is_valid == device_handle.is_valid) {
103 return i;
104 }
105 }
106 return std::nullopt;
107}
108
109void HidBus::GetBusHandle(Kernel::HLERequestContext& ctx) {
110 IPC::RequestParser rp{ctx};
111 struct Parameters {
112 Core::HID::NpadIdType npad_id;
113 INSERT_PADDING_WORDS_NOINIT(1);
114 BusType bus_type;
115 u64 applet_resource_user_id;
116 };
117 static_assert(sizeof(Parameters) == 0x18, "Parameters has incorrect size.");
118
119 const auto parameters{rp.PopRaw<Parameters>()};
120
121 LOG_INFO(Service_HID, "called, npad_id={}, bus_type={}, applet_resource_user_id={}",
122 parameters.npad_id, parameters.bus_type, parameters.applet_resource_user_id);
123
124 bool is_handle_found = 0;
125 std::size_t handle_index = 0;
126
127 for (std::size_t i = 0; i < devices.size(); i++) {
128 const auto& handle = devices[i].handle;
129 if (!handle.is_valid) {
130 continue;
131 }
132 if (static_cast<Core::HID::NpadIdType>(handle.player_number) == parameters.npad_id &&
133 handle.bus_type == parameters.bus_type) {
134 is_handle_found = true;
135 handle_index = i;
136 break;
137 }
138 }
139
140 // Handle not found. Create a new one
141 if (!is_handle_found) {
142 for (std::size_t i = 0; i < devices.size(); i++) {
143 if (devices[i].handle.is_valid) {
144 continue;
145 }
146 devices[i].handle = {
147 .abstracted_pad_id = static_cast<u8>(i),
148 .internal_index = static_cast<u8>(i),
149 .player_number = static_cast<u8>(parameters.npad_id),
150 .bus_type = parameters.bus_type,
151 .is_valid = true,
152 };
153 handle_index = i;
154 break;
155 }
156 }
157
158 struct OutData {
159 bool is_valid;
160 INSERT_PADDING_BYTES(7);
161 BusHandle handle;
162 };
163 static_assert(sizeof(OutData) == 0x10, "OutData has incorrect size.");
164
165 const OutData out_data{
166 .is_valid = true,
167 .handle = devices[handle_index].handle,
168 };
169
170 IPC::ResponseBuilder rb{ctx, 6};
171 rb.Push(ResultSuccess);
172 rb.PushRaw(out_data);
173}
174
175void HidBus::IsExternalDeviceConnected(Kernel::HLERequestContext& ctx) {
176 IPC::RequestParser rp{ctx};
177 const auto bus_handle_{rp.PopRaw<BusHandle>()};
178
179 LOG_INFO(Service_HID,
180 "Called, abstracted_pad_id={}, bus_type={}, internal_index={}, "
181 "player_number={}, is_valid={}",
182 bus_handle_.abstracted_pad_id, bus_handle_.bus_type, bus_handle_.internal_index,
183 bus_handle_.player_number, bus_handle_.is_valid);
184
185 const auto device_index = GetDeviceIndexFromHandle(bus_handle_);
186
187 if (device_index) {
188 const auto& device = devices[device_index.value()].device;
189 const bool is_attached = device->IsDeviceActivated();
190
191 IPC::ResponseBuilder rb{ctx, 3};
192 rb.Push(ResultSuccess);
193 rb.Push(is_attached);
194 return;
195 }
196
197 LOG_ERROR(Service_HID, "Invalid handle");
198 IPC::ResponseBuilder rb{ctx, 2};
199 rb.Push(ResultUnknown);
200 return;
201}
202
203void HidBus::Initialize(Kernel::HLERequestContext& ctx) {
204 IPC::RequestParser rp{ctx};
205 const auto bus_handle_{rp.PopRaw<BusHandle>()};
206 const auto applet_resource_user_id{rp.Pop<u64>()};
207
208 LOG_INFO(Service_HID,
209 "called, abstracted_pad_id={} bus_type={} internal_index={} "
210 "player_number={} is_valid={}, applet_resource_user_id={}",
211 bus_handle_.abstracted_pad_id, bus_handle_.bus_type, bus_handle_.internal_index,
212 bus_handle_.player_number, bus_handle_.is_valid, applet_resource_user_id);
213
214 is_hidbus_enabled = true;
215
216 const auto device_index = GetDeviceIndexFromHandle(bus_handle_);
217
218 if (device_index) {
219 const auto entry_index = devices[device_index.value()].handle.internal_index;
220 auto& cur_entry = hidbus_status.entries[entry_index];
221
222 if (bus_handle_.internal_index == 0 && Settings::values.enable_ring_controller) {
223 MakeDevice<RingController>(bus_handle_);
224 devices[device_index.value()].is_device_initializated = true;
225 devices[device_index.value()].device->ActivateDevice();
226 cur_entry.is_in_focus = true;
227 cur_entry.is_connected = true;
228 cur_entry.is_connected_result = ResultSuccess;
229 cur_entry.is_enabled = false;
230 cur_entry.is_polling_mode = false;
231 } else {
232 MakeDevice<HidbusStubbed>(bus_handle_);
233 devices[device_index.value()].is_device_initializated = true;
234 cur_entry.is_in_focus = true;
235 cur_entry.is_connected = false;
236 cur_entry.is_connected_result = ResultSuccess;
237 cur_entry.is_enabled = false;
238 cur_entry.is_polling_mode = false;
239 }
240
241 std::memcpy(system.Kernel().GetHidBusSharedMem().GetPointer(), &hidbus_status,
242 sizeof(hidbus_status));
243
244 IPC::ResponseBuilder rb{ctx, 2};
245 rb.Push(ResultSuccess);
246 return;
247 }
248
249 LOG_ERROR(Service_HID, "Invalid handle");
250 IPC::ResponseBuilder rb{ctx, 2};
251 rb.Push(ResultUnknown);
252 return;
253}
254
255void HidBus::Finalize(Kernel::HLERequestContext& ctx) {
256 IPC::RequestParser rp{ctx};
257 const auto bus_handle_{rp.PopRaw<BusHandle>()};
258 const auto applet_resource_user_id{rp.Pop<u64>()};
259
260 LOG_INFO(Service_HID,
261 "called, abstracted_pad_id={}, bus_type={}, internal_index={}, "
262 "player_number={}, is_valid={}, applet_resource_user_id={}",
263 bus_handle_.abstracted_pad_id, bus_handle_.bus_type, bus_handle_.internal_index,
264 bus_handle_.player_number, bus_handle_.is_valid, applet_resource_user_id);
265
266 const auto device_index = GetDeviceIndexFromHandle(bus_handle_);
267
268 if (device_index) {
269 const auto entry_index = devices[device_index.value()].handle.internal_index;
270 auto& cur_entry = hidbus_status.entries[entry_index];
271 auto& device = devices[device_index.value()].device;
272 devices[device_index.value()].is_device_initializated = false;
273 device->DeactivateDevice();
274
275 cur_entry.is_in_focus = true;
276 cur_entry.is_connected = false;
277 cur_entry.is_connected_result = ResultSuccess;
278 cur_entry.is_enabled = false;
279 cur_entry.is_polling_mode = false;
280 std::memcpy(system.Kernel().GetHidBusSharedMem().GetPointer(), &hidbus_status,
281 sizeof(hidbus_status));
282
283 IPC::ResponseBuilder rb{ctx, 2};
284 rb.Push(ResultSuccess);
285 return;
286 }
287
288 LOG_ERROR(Service_HID, "Invalid handle");
289 IPC::ResponseBuilder rb{ctx, 2};
290 rb.Push(ResultUnknown);
291 return;
292}
293
294void HidBus::EnableExternalDevice(Kernel::HLERequestContext& ctx) {
295 IPC::RequestParser rp{ctx};
296 struct Parameters {
297 bool enable;
298 INSERT_PADDING_BYTES_NOINIT(7);
299 BusHandle bus_handle;
300 u64 inval;
301 u64 applet_resource_user_id;
302 };
303 static_assert(sizeof(Parameters) == 0x20, "Parameters has incorrect size.");
304
305 const auto parameters{rp.PopRaw<Parameters>()};
306
307 LOG_INFO(Service_HID,
308 "called, enable={}, abstracted_pad_id={}, bus_type={}, internal_index={}, "
309 "player_number={}, is_valid={}, inval={}, applet_resource_user_id{}",
310 parameters.enable, parameters.bus_handle.abstracted_pad_id,
311 parameters.bus_handle.bus_type, parameters.bus_handle.internal_index,
312 parameters.bus_handle.player_number, parameters.bus_handle.is_valid, parameters.inval,
313 parameters.applet_resource_user_id);
314
315 const auto device_index = GetDeviceIndexFromHandle(parameters.bus_handle);
316
317 if (device_index) {
318 auto& device = devices[device_index.value()].device;
319 device->Enable(parameters.enable);
320
321 IPC::ResponseBuilder rb{ctx, 2};
322 rb.Push(ResultSuccess);
323 return;
324 }
325
326 LOG_ERROR(Service_HID, "Invalid handle");
327 IPC::ResponseBuilder rb{ctx, 2};
328 rb.Push(ResultUnknown);
329 return;
330}
331
332void HidBus::GetExternalDeviceId(Kernel::HLERequestContext& ctx) {
333 IPC::RequestParser rp{ctx};
334 const auto bus_handle_{rp.PopRaw<BusHandle>()};
335
336 LOG_INFO(Service_HID,
337 "called, abstracted_pad_id={}, bus_type={}, internal_index={}, player_number={}, "
338 "is_valid={}",
339 bus_handle_.abstracted_pad_id, bus_handle_.bus_type, bus_handle_.internal_index,
340 bus_handle_.player_number, bus_handle_.is_valid);
341
342 const auto device_index = GetDeviceIndexFromHandle(bus_handle_);
343
344 if (device_index) {
345 const auto& device = devices[device_index.value()].device;
346 u32 device_id = device->GetDeviceId();
347 IPC::ResponseBuilder rb{ctx, 3};
348 rb.Push(ResultSuccess);
349 rb.Push<u32>(device_id);
350 return;
351 }
352
353 LOG_ERROR(Service_HID, "Invalid handle");
354 IPC::ResponseBuilder rb{ctx, 2};
355 rb.Push(ResultUnknown);
356 return;
357}
358
359void HidBus::SendCommandAsync(Kernel::HLERequestContext& ctx) {
360 IPC::RequestParser rp{ctx};
361 const auto data = ctx.ReadBuffer();
362 const auto bus_handle_{rp.PopRaw<BusHandle>()};
363
364 LOG_DEBUG(Service_HID,
365 "called, data_size={}, abstracted_pad_id={}, bus_type={}, internal_index={}, "
366 "player_number={}, is_valid={}",
367 data.size(), bus_handle_.abstracted_pad_id, bus_handle_.bus_type,
368 bus_handle_.internal_index, bus_handle_.player_number, bus_handle_.is_valid);
369
370 const auto device_index = GetDeviceIndexFromHandle(bus_handle_);
371
372 if (device_index) {
373 auto& device = devices[device_index.value()].device;
374 device->SetCommand(data);
375
376 IPC::ResponseBuilder rb{ctx, 2};
377 rb.Push(ResultSuccess);
378 return;
379 }
380
381 LOG_ERROR(Service_HID, "Invalid handle");
382 IPC::ResponseBuilder rb{ctx, 2};
383 rb.Push(ResultUnknown);
384 return;
385};
386
387void HidBus::GetSendCommandAsynceResult(Kernel::HLERequestContext& ctx) {
388 IPC::RequestParser rp{ctx};
389 const auto bus_handle_{rp.PopRaw<BusHandle>()};
390
391 LOG_DEBUG(Service_HID,
392 "called, abstracted_pad_id={}, bus_type={}, internal_index={}, player_number={}, "
393 "is_valid={}",
394 bus_handle_.abstracted_pad_id, bus_handle_.bus_type, bus_handle_.internal_index,
395 bus_handle_.player_number, bus_handle_.is_valid);
396
397 const auto device_index = GetDeviceIndexFromHandle(bus_handle_);
398
399 if (device_index) {
400 const auto& device = devices[device_index.value()].device;
401 const std::vector<u8> data = device->GetReply();
402 const u64 data_size = ctx.WriteBuffer(data);
403
404 IPC::ResponseBuilder rb{ctx, 4};
405 rb.Push(ResultSuccess);
406 rb.Push<u64>(data_size);
407 return;
408 }
409
410 LOG_ERROR(Service_HID, "Invalid handle");
411 IPC::ResponseBuilder rb{ctx, 2};
412 rb.Push(ResultUnknown);
413 return;
414};
415
416void HidBus::SetEventForSendCommandAsycResult(Kernel::HLERequestContext& ctx) {
417 IPC::RequestParser rp{ctx};
418 const auto bus_handle_{rp.PopRaw<BusHandle>()};
419
420 LOG_INFO(Service_HID,
421 "called, abstracted_pad_id={}, bus_type={}, internal_index={}, player_number={}, "
422 "is_valid={}",
423 bus_handle_.abstracted_pad_id, bus_handle_.bus_type, bus_handle_.internal_index,
424 bus_handle_.player_number, bus_handle_.is_valid);
425
426 const auto device_index = GetDeviceIndexFromHandle(bus_handle_);
427
428 if (device_index) {
429 const auto& device = devices[device_index.value()].device;
430 IPC::ResponseBuilder rb{ctx, 2, 1};
431 rb.Push(ResultSuccess);
432 rb.PushCopyObjects(device->GetSendCommandAsycEvent());
433 return;
434 }
435
436 LOG_ERROR(Service_HID, "Invalid handle");
437 IPC::ResponseBuilder rb{ctx, 2};
438 rb.Push(ResultUnknown);
439 return;
440};
441
442void HidBus::GetSharedMemoryHandle(Kernel::HLERequestContext& ctx) {
443 LOG_DEBUG(Service_HID, "called");
444
445 IPC::ResponseBuilder rb{ctx, 2, 1};
446 rb.Push(ResultSuccess);
447 rb.PushCopyObjects(&system.Kernel().GetHidBusSharedMem());
448}
449
450void HidBus::EnableJoyPollingReceiveMode(Kernel::HLERequestContext& ctx) {
451 IPC::RequestParser rp{ctx};
452 const auto t_mem_size{rp.Pop<u32>()};
453 const auto t_mem_handle{ctx.GetCopyHandle(0)};
454 const auto polling_mode_{rp.PopEnum<JoyPollingMode>()};
455 const auto bus_handle_{rp.PopRaw<BusHandle>()};
456
457 ASSERT_MSG(t_mem_size == 0x1000, "t_mem_size is not 0x1000 bytes");
458
459 auto t_mem =
460 system.CurrentProcess()->GetHandleTable().GetObject<Kernel::KTransferMemory>(t_mem_handle);
461
462 if (t_mem.IsNull()) {
463 LOG_ERROR(Service_HID, "t_mem is a nullptr for handle=0x{:08X}", t_mem_handle);
464 IPC::ResponseBuilder rb{ctx, 2};
465 rb.Push(ResultUnknown);
466 return;
467 }
468
469 ASSERT_MSG(t_mem->GetSize() == 0x1000, "t_mem has incorrect size");
470
471 LOG_INFO(Service_HID,
472 "called, t_mem_handle=0x{:08X}, polling_mode={}, abstracted_pad_id={}, bus_type={}, "
473 "internal_index={}, player_number={}, is_valid={}",
474 t_mem_handle, polling_mode_, bus_handle_.abstracted_pad_id, bus_handle_.bus_type,
475 bus_handle_.internal_index, bus_handle_.player_number, bus_handle_.is_valid);
476
477 const auto device_index = GetDeviceIndexFromHandle(bus_handle_);
478
479 if (device_index) {
480 auto& device = devices[device_index.value()].device;
481 device->SetPollingMode(polling_mode_);
482 device->SetTransferMemoryPointer(system.Memory().GetPointer(t_mem->GetSourceAddress()));
483
484 IPC::ResponseBuilder rb{ctx, 2};
485 rb.Push(ResultSuccess);
486 return;
487 }
488
489 LOG_ERROR(Service_HID, "Invalid handle");
490 IPC::ResponseBuilder rb{ctx, 2};
491 rb.Push(ResultUnknown);
492 return;
493}
494
495void HidBus::DisableJoyPollingReceiveMode(Kernel::HLERequestContext& ctx) {
496 IPC::RequestParser rp{ctx};
497 const auto bus_handle_{rp.PopRaw<BusHandle>()};
498
499 LOG_INFO(Service_HID,
500 "called, abstracted_pad_id={}, bus_type={}, internal_index={}, player_number={}, "
501 "is_valid={}",
502 bus_handle_.abstracted_pad_id, bus_handle_.bus_type, bus_handle_.internal_index,
503 bus_handle_.player_number, bus_handle_.is_valid);
504
505 const auto device_index = GetDeviceIndexFromHandle(bus_handle_);
506
507 if (device_index) {
508 auto& device = devices[device_index.value()].device;
509 device->DisablePollingMode();
510
511 IPC::ResponseBuilder rb{ctx, 2};
512 rb.Push(ResultSuccess);
513 return;
514 }
515
516 LOG_ERROR(Service_HID, "Invalid handle");
517 IPC::ResponseBuilder rb{ctx, 2};
518 rb.Push(ResultUnknown);
519 return;
520}
521
522void HidBus::SetStatusManagerType(Kernel::HLERequestContext& ctx) {
523 IPC::RequestParser rp{ctx};
524 const auto manager_type{rp.PopEnum<StatusManagerType>()};
525
526 LOG_WARNING(Service_HID, "(STUBBED) called, manager_type={}", manager_type);
527
528 IPC::ResponseBuilder rb{ctx, 2};
529 rb.Push(ResultSuccess);
530};
531} // namespace Service::HID
diff --git a/src/core/hle/service/hid/hidbus.h b/src/core/hle/service/hid/hidbus.h
new file mode 100644
index 000000000..b10d5156a
--- /dev/null
+++ b/src/core/hle/service/hid/hidbus.h
@@ -0,0 +1,131 @@
1// Copyright 2021 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <functional>
8
9#include "core/hle/service/hid/hidbus/hidbus_base.h"
10#include "core/hle/service/kernel_helpers.h"
11#include "core/hle/service/service.h"
12
13namespace Core::Timing {
14struct EventType;
15} // namespace Core::Timing
16
17namespace Core {
18class System;
19} // namespace Core
20
21namespace Service::HID {
22
23class HidBus final : public ServiceFramework<HidBus> {
24public:
25 explicit HidBus(Core::System& system_);
26 ~HidBus() override;
27
28private:
29 static const std::size_t max_number_of_handles = 0x13;
30
31 enum class HidBusDeviceId : std::size_t {
32 RingController = 0x20,
33 FamicomRight = 0x21,
34 Starlink = 0x28,
35 };
36
37 // This is nn::hidbus::detail::StatusManagerType
38 enum class StatusManagerType : u32 {
39 None,
40 Type16,
41 Type32,
42 };
43
44 // This is nn::hidbus::BusType
45 enum class BusType : u8 {
46 LeftJoyRail,
47 RightJoyRail,
48 InternalBus, // Lark microphone
49
50 MaxBusType,
51 };
52
53 // This is nn::hidbus::BusHandle
54 struct BusHandle {
55 u32 abstracted_pad_id;
56 u8 internal_index;
57 u8 player_number;
58 BusType bus_type;
59 bool is_valid;
60 };
61 static_assert(sizeof(BusHandle) == 0x8, "BusHandle is an invalid size");
62
63 // This is nn::hidbus::JoyPollingReceivedData
64 struct JoyPollingReceivedData {
65 std::array<u8, 0x30> data;
66 u64 out_size;
67 u64 sampling_number;
68 };
69 static_assert(sizeof(JoyPollingReceivedData) == 0x40,
70 "JoyPollingReceivedData is an invalid size");
71
72 struct HidbusStatusManagerEntry {
73 u8 is_connected{};
74 INSERT_PADDING_BYTES(0x3);
75 ResultCode is_connected_result{0};
76 u8 is_enabled{};
77 u8 is_in_focus{};
78 u8 is_polling_mode{};
79 u8 reserved{};
80 JoyPollingMode polling_mode{};
81 INSERT_PADDING_BYTES(0x70); // Unknown
82 };
83 static_assert(sizeof(HidbusStatusManagerEntry) == 0x80,
84 "HidbusStatusManagerEntry is an invalid size");
85
86 struct HidbusStatusManager {
87 std::array<HidbusStatusManagerEntry, max_number_of_handles> entries{};
88 INSERT_PADDING_BYTES(0x680); // Unused
89 };
90 static_assert(sizeof(HidbusStatusManager) <= 0x1000, "HidbusStatusManager is an invalid size");
91
92 struct HidbusDevice {
93 bool is_device_initializated{};
94 BusHandle handle{};
95 std::unique_ptr<HidbusBase> device{nullptr};
96 };
97
98 void GetBusHandle(Kernel::HLERequestContext& ctx);
99 void IsExternalDeviceConnected(Kernel::HLERequestContext& ctx);
100 void Initialize(Kernel::HLERequestContext& ctx);
101 void Finalize(Kernel::HLERequestContext& ctx);
102 void EnableExternalDevice(Kernel::HLERequestContext& ctx);
103 void GetExternalDeviceId(Kernel::HLERequestContext& ctx);
104 void SendCommandAsync(Kernel::HLERequestContext& ctx);
105 void GetSendCommandAsynceResult(Kernel::HLERequestContext& ctx);
106 void SetEventForSendCommandAsycResult(Kernel::HLERequestContext& ctx);
107 void GetSharedMemoryHandle(Kernel::HLERequestContext& ctx);
108 void EnableJoyPollingReceiveMode(Kernel::HLERequestContext& ctx);
109 void DisableJoyPollingReceiveMode(Kernel::HLERequestContext& ctx);
110 void SetStatusManagerType(Kernel::HLERequestContext& ctx);
111
112 void UpdateHidbus(std::uintptr_t user_data, std::chrono::nanoseconds ns_late);
113 std::optional<std::size_t> GetDeviceIndexFromHandle(BusHandle handle) const;
114
115 template <typename T>
116 void MakeDevice(BusHandle handle) {
117 const auto device_index = GetDeviceIndexFromHandle(handle);
118 if (device_index) {
119 devices[device_index.value()].device =
120 std::make_unique<T>(system.HIDCore(), service_context);
121 }
122 }
123
124 bool is_hidbus_enabled{false};
125 HidbusStatusManager hidbus_status{};
126 std::array<HidbusDevice, max_number_of_handles> devices{};
127 std::shared_ptr<Core::Timing::EventType> hidbus_update_event;
128 KernelHelpers::ServiceContext service_context;
129};
130
131} // namespace Service::HID
diff --git a/src/core/hle/service/hid/hidbus/hidbus_base.cpp b/src/core/hle/service/hid/hidbus/hidbus_base.cpp
new file mode 100644
index 000000000..09bff10e5
--- /dev/null
+++ b/src/core/hle/service/hid/hidbus/hidbus_base.cpp
@@ -0,0 +1,72 @@
1// Copyright 2021 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include "core/hid/hid_core.h"
6#include "core/hle/kernel/k_event.h"
7#include "core/hle/kernel/k_readable_event.h"
8#include "core/hle/service/hid/hidbus/hidbus_base.h"
9#include "core/hle/service/kernel_helpers.h"
10
11namespace Service::HID {
12
13HidbusBase::HidbusBase(KernelHelpers::ServiceContext& service_context_)
14 : service_context(service_context_) {
15 send_command_async_event = service_context.CreateEvent("hidbus:SendCommandAsyncEvent");
16}
17HidbusBase::~HidbusBase() = default;
18
19void HidbusBase::ActivateDevice() {
20 if (is_activated) {
21 return;
22 }
23 is_activated = true;
24 OnInit();
25}
26
27void HidbusBase::DeactivateDevice() {
28 if (is_activated) {
29 OnRelease();
30 }
31 is_activated = false;
32}
33
34bool HidbusBase::IsDeviceActivated() const {
35 return is_activated;
36}
37
38void HidbusBase::Enable(bool enable) {
39 device_enabled = enable;
40}
41
42bool HidbusBase::IsEnabled() const {
43 return device_enabled;
44}
45
46bool HidbusBase::IsPollingMode() const {
47 return polling_mode_enabled;
48}
49
50JoyPollingMode HidbusBase::GetPollingMode() const {
51 return polling_mode;
52}
53
54void HidbusBase::SetPollingMode(JoyPollingMode mode) {
55 polling_mode = mode;
56 polling_mode_enabled = true;
57}
58
59void HidbusBase::DisablePollingMode() {
60 polling_mode_enabled = false;
61}
62
63void HidbusBase::SetTransferMemoryPointer(u8* t_mem) {
64 is_transfer_memory_set = true;
65 transfer_memory = t_mem;
66}
67
68Kernel::KReadableEvent& HidbusBase::GetSendCommandAsycEvent() const {
69 return send_command_async_event->GetReadableEvent();
70}
71
72} // namespace Service::HID
diff --git a/src/core/hle/service/hid/hidbus/hidbus_base.h b/src/core/hle/service/hid/hidbus/hidbus_base.h
new file mode 100644
index 000000000..13d073a3d
--- /dev/null
+++ b/src/core/hle/service/hid/hidbus/hidbus_base.h
@@ -0,0 +1,179 @@
1// Copyright 2021 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <array>
8#include "common/common_types.h"
9#include "core/hle/result.h"
10
11namespace Kernel {
12class KEvent;
13class KReadableEvent;
14} // namespace Kernel
15
16namespace Service::KernelHelpers {
17class ServiceContext;
18}
19
20namespace Service::HID {
21
22// This is nn::hidbus::JoyPollingMode
23enum class JoyPollingMode : u32 {
24 SixAxisSensorDisable,
25 SixAxisSensorEnable,
26 ButtonOnly,
27};
28
29struct DataAccessorHeader {
30 ResultCode result{ResultUnknown};
31 INSERT_PADDING_WORDS(0x1);
32 std::array<u8, 0x18> unused{};
33 u64 latest_entry{};
34 u64 total_entries{};
35};
36static_assert(sizeof(DataAccessorHeader) == 0x30, "DataAccessorHeader is an invalid size");
37
38struct JoyDisableSixAxisPollingData {
39 std::array<u8, 0x26> data;
40 u8 out_size;
41 INSERT_PADDING_BYTES(0x1);
42 u64 sampling_number;
43};
44static_assert(sizeof(JoyDisableSixAxisPollingData) == 0x30,
45 "JoyDisableSixAxisPollingData is an invalid size");
46
47struct JoyEnableSixAxisPollingData {
48 std::array<u8, 0x8> data;
49 u8 out_size;
50 INSERT_PADDING_BYTES(0x7);
51 u64 sampling_number;
52};
53static_assert(sizeof(JoyEnableSixAxisPollingData) == 0x18,
54 "JoyEnableSixAxisPollingData is an invalid size");
55
56struct JoyButtonOnlyPollingData {
57 std::array<u8, 0x2c> data;
58 u8 out_size;
59 INSERT_PADDING_BYTES(0x3);
60 u64 sampling_number;
61};
62static_assert(sizeof(JoyButtonOnlyPollingData) == 0x38,
63 "JoyButtonOnlyPollingData is an invalid size");
64
65struct JoyDisableSixAxisPollingEntry {
66 u64 sampling_number;
67 JoyDisableSixAxisPollingData polling_data;
68};
69static_assert(sizeof(JoyDisableSixAxisPollingEntry) == 0x38,
70 "JoyDisableSixAxisPollingEntry is an invalid size");
71
72struct JoyEnableSixAxisPollingEntry {
73 u64 sampling_number;
74 JoyEnableSixAxisPollingData polling_data;
75};
76static_assert(sizeof(JoyEnableSixAxisPollingEntry) == 0x20,
77 "JoyEnableSixAxisPollingEntry is an invalid size");
78
79struct JoyButtonOnlyPollingEntry {
80 u64 sampling_number;
81 JoyButtonOnlyPollingData polling_data;
82};
83static_assert(sizeof(JoyButtonOnlyPollingEntry) == 0x40,
84 "JoyButtonOnlyPollingEntry is an invalid size");
85
86struct JoyDisableSixAxisDataAccessor {
87 DataAccessorHeader header{};
88 std::array<JoyDisableSixAxisPollingEntry, 0xb> entries{};
89};
90static_assert(sizeof(JoyDisableSixAxisDataAccessor) == 0x298,
91 "JoyDisableSixAxisDataAccessor is an invalid size");
92
93struct JoyEnableSixAxisDataAccessor {
94 DataAccessorHeader header{};
95 std::array<JoyEnableSixAxisPollingEntry, 0xb> entries{};
96};
97static_assert(sizeof(JoyEnableSixAxisDataAccessor) == 0x190,
98 "JoyEnableSixAxisDataAccessor is an invalid size");
99
100struct ButtonOnlyPollingDataAccessor {
101 DataAccessorHeader header;
102 std::array<JoyButtonOnlyPollingEntry, 0xb> entries;
103};
104static_assert(sizeof(ButtonOnlyPollingDataAccessor) == 0x2F0,
105 "ButtonOnlyPollingDataAccessor is an invalid size");
106
107class HidbusBase {
108public:
109 explicit HidbusBase(KernelHelpers::ServiceContext& service_context_);
110 virtual ~HidbusBase();
111
112 void ActivateDevice();
113
114 void DeactivateDevice();
115
116 bool IsDeviceActivated() const;
117
118 // Enables/disables the device
119 void Enable(bool enable);
120
121 // returns true if device is enabled
122 bool IsEnabled() const;
123
124 // returns true if polling mode is enabled
125 bool IsPollingMode() const;
126
127 // returns polling mode
128 JoyPollingMode GetPollingMode() const;
129
130 // Sets and enables JoyPollingMode
131 void SetPollingMode(JoyPollingMode mode);
132
133 // Disables JoyPollingMode
134 void DisablePollingMode();
135
136 // Called on EnableJoyPollingReceiveMode
137 void SetTransferMemoryPointer(u8* t_mem);
138
139 Kernel::KReadableEvent& GetSendCommandAsycEvent() const;
140
141 virtual void OnInit() {}
142
143 virtual void OnRelease() {}
144
145 // Updates device transfer memory
146 virtual void OnUpdate() {}
147
148 // Returns the device ID of the joycon
149 virtual u8 GetDeviceId() const {
150 return {};
151 }
152
153 // Assigns a command from data
154 virtual bool SetCommand(const std::vector<u8>& data) {
155 return {};
156 }
157
158 // Returns a reply from a command
159 virtual std::vector<u8> GetReply() const {
160 return {};
161 }
162
163protected:
164 bool is_activated{};
165 bool device_enabled{};
166 bool polling_mode_enabled{};
167 JoyPollingMode polling_mode = {};
168 // TODO(German77): All data accessors need to be replaced with a ring lifo object
169 JoyDisableSixAxisDataAccessor disable_sixaxis_data{};
170 JoyEnableSixAxisDataAccessor enable_sixaxis_data{};
171 ButtonOnlyPollingDataAccessor button_only_data{};
172
173 u8* transfer_memory{nullptr};
174 bool is_transfer_memory_set{};
175
176 Kernel::KEvent* send_command_async_event;
177 KernelHelpers::ServiceContext& service_context;
178};
179} // namespace Service::HID
diff --git a/src/core/hle/service/hid/hidbus/ringcon.cpp b/src/core/hle/service/hid/hidbus/ringcon.cpp
new file mode 100644
index 000000000..5ec3cc83c
--- /dev/null
+++ b/src/core/hle/service/hid/hidbus/ringcon.cpp
@@ -0,0 +1,286 @@
1// Copyright 2021 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include "core/hid/emulated_devices.h"
6#include "core/hid/hid_core.h"
7#include "core/hle/kernel/k_event.h"
8#include "core/hle/kernel/k_readable_event.h"
9#include "core/hle/service/hid/hidbus/ringcon.h"
10
11namespace Service::HID {
12
13RingController::RingController(Core::HID::HIDCore& hid_core_,
14 KernelHelpers::ServiceContext& service_context_)
15 : HidbusBase(service_context_) {
16 input = hid_core_.GetEmulatedDevices();
17}
18
19RingController::~RingController() = default;
20
21void RingController::OnInit() {
22 return;
23}
24
25void RingController::OnRelease() {
26 return;
27};
28
29void RingController::OnUpdate() {
30 if (!is_activated) {
31 return;
32 }
33
34 if (!device_enabled) {
35 return;
36 }
37
38 if (!polling_mode_enabled || !is_transfer_memory_set) {
39 return;
40 }
41
42 // TODO: Increment multitasking counters from motion and sensor data
43
44 switch (polling_mode) {
45 case JoyPollingMode::SixAxisSensorEnable: {
46 enable_sixaxis_data.header.total_entries = 10;
47 enable_sixaxis_data.header.result = ResultSuccess;
48 const auto& last_entry =
49 enable_sixaxis_data.entries[enable_sixaxis_data.header.latest_entry];
50
51 enable_sixaxis_data.header.latest_entry =
52 (enable_sixaxis_data.header.latest_entry + 1) % 10;
53 auto& curr_entry = enable_sixaxis_data.entries[enable_sixaxis_data.header.latest_entry];
54
55 curr_entry.sampling_number = last_entry.sampling_number + 1;
56 curr_entry.polling_data.sampling_number = curr_entry.sampling_number;
57
58 const RingConData ringcon_value = GetSensorValue();
59 curr_entry.polling_data.out_size = sizeof(ringcon_value);
60 std::memcpy(curr_entry.polling_data.data.data(), &ringcon_value, sizeof(ringcon_value));
61
62 std::memcpy(transfer_memory, &enable_sixaxis_data, sizeof(enable_sixaxis_data));
63 break;
64 }
65 default:
66 LOG_ERROR(Service_HID, "Polling mode not supported {}", polling_mode);
67 break;
68 }
69}
70
71RingController::RingConData RingController::GetSensorValue() const {
72 RingConData ringcon_sensor_value{
73 .status = DataValid::Valid,
74 .data = 0,
75 };
76
77 const f32 force_value = input->GetRingSensorForce().force * range;
78 ringcon_sensor_value.data = static_cast<s16>(force_value) + idle_value;
79
80 return ringcon_sensor_value;
81}
82
83u8 RingController::GetDeviceId() const {
84 return device_id;
85}
86
87std::vector<u8> RingController::GetReply() const {
88 const RingConCommands current_command = command;
89
90 switch (current_command) {
91 case RingConCommands::GetFirmwareVersion:
92 return GetFirmwareVersionReply();
93 case RingConCommands::ReadId:
94 return GetReadIdReply();
95 case RingConCommands::c20105:
96 return GetC020105Reply();
97 case RingConCommands::ReadUnkCal:
98 return GetReadUnkCalReply();
99 case RingConCommands::ReadFactoryCal:
100 return GetReadFactoryCalReply();
101 case RingConCommands::ReadUserCal:
102 return GetReadUserCalReply();
103 case RingConCommands::ReadRepCount:
104 return GetReadRepCountReply();
105 case RingConCommands::ReadTotalPushCount:
106 return GetReadTotalPushCountReply();
107 case RingConCommands::ResetRepCount:
108 return GetResetRepCountReply();
109 case RingConCommands::SaveCalData:
110 return GetSaveDataReply();
111 default:
112 return GetErrorReply();
113 }
114}
115
116bool RingController::SetCommand(const std::vector<u8>& data) {
117 if (data.size() < 4) {
118 LOG_ERROR(Service_HID, "Command size not supported {}", data.size());
119 command = RingConCommands::Error;
120 return false;
121 }
122
123 std::memcpy(&command, data.data(), sizeof(RingConCommands));
124
125 switch (command) {
126 case RingConCommands::GetFirmwareVersion:
127 case RingConCommands::ReadId:
128 case RingConCommands::c20105:
129 case RingConCommands::ReadUnkCal:
130 case RingConCommands::ReadFactoryCal:
131 case RingConCommands::ReadUserCal:
132 case RingConCommands::ReadRepCount:
133 case RingConCommands::ReadTotalPushCount:
134 ASSERT_MSG(data.size() == 0x4, "data.size is not 0x4 bytes");
135 send_command_async_event->GetWritableEvent().Signal();
136 return true;
137 case RingConCommands::ResetRepCount:
138 ASSERT_MSG(data.size() == 0x4, "data.size is not 0x4 bytes");
139 total_rep_count = 0;
140 send_command_async_event->GetWritableEvent().Signal();
141 return true;
142 case RingConCommands::SaveCalData: {
143 ASSERT_MSG(data.size() == 0x14, "data.size is not 0x14 bytes");
144
145 SaveCalData save_info{};
146 std::memcpy(&save_info, data.data(), sizeof(SaveCalData));
147 user_calibration = save_info.calibration;
148 send_command_async_event->GetWritableEvent().Signal();
149 return true;
150 }
151 default:
152 LOG_ERROR(Service_HID, "Command not implemented {}", command);
153 command = RingConCommands::Error;
154 // Signal a reply to avoid softlocking the game
155 send_command_async_event->GetWritableEvent().Signal();
156 return false;
157 }
158}
159
160std::vector<u8> RingController::GetFirmwareVersionReply() const {
161 const FirmwareVersionReply reply{
162 .status = DataValid::Valid,
163 .firmware = version,
164 };
165
166 return GetDataVector(reply);
167}
168
169std::vector<u8> RingController::GetReadIdReply() const {
170 // The values are hardcoded from a real joycon
171 const ReadIdReply reply{
172 .status = DataValid::Valid,
173 .id_l_x0 = 8,
174 .id_l_x0_2 = 41,
175 .id_l_x4 = 22294,
176 .id_h_x0 = 19777,
177 .id_h_x0_2 = 13621,
178 .id_h_x4 = 8245,
179 };
180
181 return GetDataVector(reply);
182}
183
184std::vector<u8> RingController::GetC020105Reply() const {
185 const Cmd020105Reply reply{
186 .status = DataValid::Valid,
187 .data = 1,
188 };
189
190 return GetDataVector(reply);
191}
192
193std::vector<u8> RingController::GetReadUnkCalReply() const {
194 const ReadUnkCalReply reply{
195 .status = DataValid::Valid,
196 .data = 0,
197 };
198
199 return GetDataVector(reply);
200}
201
202std::vector<u8> RingController::GetReadFactoryCalReply() const {
203 const ReadFactoryCalReply reply{
204 .status = DataValid::Valid,
205 .calibration = factory_calibration,
206 };
207
208 return GetDataVector(reply);
209}
210
211std::vector<u8> RingController::GetReadUserCalReply() const {
212 const ReadUserCalReply reply{
213 .status = DataValid::Valid,
214 .calibration = user_calibration,
215 };
216
217 return GetDataVector(reply);
218}
219
220std::vector<u8> RingController::GetReadRepCountReply() const {
221 const GetThreeByteReply reply{
222 .status = DataValid::Valid,
223 .data = {total_rep_count, 0, 0},
224 .crc = GetCrcValue({total_rep_count, 0, 0, 0}),
225 };
226
227 return GetDataVector(reply);
228}
229
230std::vector<u8> RingController::GetReadTotalPushCountReply() const {
231 const GetThreeByteReply reply{
232 .status = DataValid::Valid,
233 .data = {total_push_count, 0, 0},
234 .crc = GetCrcValue({total_push_count, 0, 0, 0}),
235 };
236
237 return GetDataVector(reply);
238}
239
240std::vector<u8> RingController::GetResetRepCountReply() const {
241 return GetReadRepCountReply();
242}
243
244std::vector<u8> RingController::GetSaveDataReply() const {
245 const StatusReply reply{
246 .status = DataValid::Valid,
247 };
248
249 return GetDataVector(reply);
250}
251
252std::vector<u8> RingController::GetErrorReply() const {
253 const ErrorReply reply{
254 .status = DataValid::BadCRC,
255 };
256
257 return GetDataVector(reply);
258}
259
260u8 RingController::GetCrcValue(const std::vector<u8>& data) const {
261 u8 crc = 0;
262 for (std::size_t index = 0; index < data.size(); index++) {
263 for (u8 i = 0x80; i > 0; i >>= 1) {
264 bool bit = (crc & 0x80) != 0;
265 if ((data[index] & i) != 0) {
266 bit = !bit;
267 }
268 crc <<= 1;
269 if (bit) {
270 crc ^= 0x8d;
271 }
272 }
273 }
274 return crc;
275}
276
277template <typename T>
278std::vector<u8> RingController::GetDataVector(const T& reply) const {
279 static_assert(std::is_trivially_copyable_v<T>);
280 std::vector<u8> data;
281 data.resize(sizeof(reply));
282 std::memcpy(data.data(), &reply, sizeof(reply));
283 return data;
284}
285
286} // namespace Service::HID
diff --git a/src/core/hle/service/hid/hidbus/ringcon.h b/src/core/hle/service/hid/hidbus/ringcon.h
new file mode 100644
index 000000000..2dbc6150e
--- /dev/null
+++ b/src/core/hle/service/hid/hidbus/ringcon.h
@@ -0,0 +1,254 @@
1// Copyright 2021 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <array>
8
9#include "common/common_types.h"
10#include "core/hle/service/hid/hidbus/hidbus_base.h"
11
12namespace Core::HID {
13class EmulatedDevices;
14} // namespace Core::HID
15
16namespace Service::HID {
17
18class RingController final : public HidbusBase {
19public:
20 explicit RingController(Core::HID::HIDCore& hid_core_,
21 KernelHelpers::ServiceContext& service_context_);
22 ~RingController() override;
23
24 void OnInit() override;
25
26 void OnRelease() override;
27
28 // Updates ringcon transfer memory
29 void OnUpdate() override;
30
31 // Returns the device ID of the joycon
32 u8 GetDeviceId() const override;
33
34 // Assigns a command from data
35 bool SetCommand(const std::vector<u8>& data) override;
36
37 // Returns a reply from a command
38 std::vector<u8> GetReply() const override;
39
40private:
41 // These values are obtained from a real ring controller
42 static constexpr s16 idle_value = 2280;
43 static constexpr s16 idle_deadzone = 120;
44 static constexpr s16 range = 2500;
45
46 // Most missing command names are leftovers from other firmware versions
47 enum class RingConCommands : u32 {
48 GetFirmwareVersion = 0x00020000,
49 ReadId = 0x00020100,
50 JoyPolling = 0x00020101,
51 Unknown1 = 0x00020104,
52 c20105 = 0x00020105,
53 Unknown2 = 0x00020204,
54 Unknown3 = 0x00020304,
55 Unknown4 = 0x00020404,
56 ReadUnkCal = 0x00020504,
57 ReadFactoryCal = 0x00020A04,
58 Unknown5 = 0x00021104,
59 Unknown6 = 0x00021204,
60 Unknown7 = 0x00021304,
61 ReadUserCal = 0x00021A04,
62 ReadRepCount = 0x00023104,
63 ReadTotalPushCount = 0x00023204,
64 ResetRepCount = 0x04013104,
65 Unknown8 = 0x04011104,
66 Unknown9 = 0x04011204,
67 Unknown10 = 0x04011304,
68 SaveCalData = 0x10011A04,
69 Error = 0xFFFFFFFF,
70 };
71
72 enum class DataValid : u32 {
73 Valid,
74 BadCRC,
75 Cal,
76 };
77
78 struct FirmwareVersion {
79 u8 sub;
80 u8 main;
81 };
82 static_assert(sizeof(FirmwareVersion) == 0x2, "FirmwareVersion is an invalid size");
83
84 struct FactoryCalibration {
85 s32_le os_max;
86 s32_le hk_max;
87 s32_le zero_min;
88 s32_le zero_max;
89 };
90 static_assert(sizeof(FactoryCalibration) == 0x10, "FactoryCalibration is an invalid size");
91
92 struct CalibrationValue {
93 s16 value;
94 u16 crc;
95 };
96 static_assert(sizeof(CalibrationValue) == 0x4, "CalibrationValue is an invalid size");
97
98 struct UserCalibration {
99 CalibrationValue os_max;
100 CalibrationValue hk_max;
101 CalibrationValue zero;
102 };
103 static_assert(sizeof(UserCalibration) == 0xC, "UserCalibration is an invalid size");
104
105 struct SaveCalData {
106 RingConCommands command;
107 UserCalibration calibration;
108 INSERT_PADDING_BYTES_NOINIT(4);
109 };
110 static_assert(sizeof(SaveCalData) == 0x14, "SaveCalData is an invalid size");
111 static_assert(std::is_trivially_copyable_v<SaveCalData>,
112 "SaveCalData must be trivially copyable");
113
114 struct FirmwareVersionReply {
115 DataValid status;
116 FirmwareVersion firmware;
117 INSERT_PADDING_BYTES(0x2);
118 };
119 static_assert(sizeof(FirmwareVersionReply) == 0x8, "FirmwareVersionReply is an invalid size");
120
121 struct Cmd020105Reply {
122 DataValid status;
123 u8 data;
124 INSERT_PADDING_BYTES(0x3);
125 };
126 static_assert(sizeof(Cmd020105Reply) == 0x8, "Cmd020105Reply is an invalid size");
127
128 struct StatusReply {
129 DataValid status;
130 };
131 static_assert(sizeof(StatusReply) == 0x4, "StatusReply is an invalid size");
132
133 struct GetThreeByteReply {
134 DataValid status;
135 std::array<u8, 3> data;
136 u8 crc;
137 };
138 static_assert(sizeof(GetThreeByteReply) == 0x8, "GetThreeByteReply is an invalid size");
139
140 struct ReadUnkCalReply {
141 DataValid status;
142 u16 data;
143 INSERT_PADDING_BYTES(0x2);
144 };
145 static_assert(sizeof(ReadUnkCalReply) == 0x8, "ReadUnkCalReply is an invalid size");
146
147 struct ReadFactoryCalReply {
148 DataValid status;
149 FactoryCalibration calibration;
150 };
151 static_assert(sizeof(ReadFactoryCalReply) == 0x14, "ReadFactoryCalReply is an invalid size");
152
153 struct ReadUserCalReply {
154 DataValid status;
155 UserCalibration calibration;
156 INSERT_PADDING_BYTES(0x4);
157 };
158 static_assert(sizeof(ReadUserCalReply) == 0x14, "ReadUserCalReply is an invalid size");
159
160 struct ReadIdReply {
161 DataValid status;
162 u16 id_l_x0;
163 u16 id_l_x0_2;
164 u16 id_l_x4;
165 u16 id_h_x0;
166 u16 id_h_x0_2;
167 u16 id_h_x4;
168 };
169 static_assert(sizeof(ReadIdReply) == 0x10, "ReadIdReply is an invalid size");
170
171 struct ErrorReply {
172 DataValid status;
173 INSERT_PADDING_BYTES(0x3);
174 };
175 static_assert(sizeof(ErrorReply) == 0x8, "ErrorReply is an invalid size");
176
177 struct RingConData {
178 DataValid status;
179 s16_le data;
180 INSERT_PADDING_BYTES(0x2);
181 };
182 static_assert(sizeof(RingConData) == 0x8, "RingConData is an invalid size");
183
184 // Returns RingConData struct with pressure sensor values
185 RingConData GetSensorValue() const;
186
187 // Returns 8 byte reply with firmware version
188 std::vector<u8> GetFirmwareVersionReply() const;
189
190 // Returns 16 byte reply with ID values
191 std::vector<u8> GetReadIdReply() const;
192
193 // (STUBBED) Returns 8 byte reply
194 std::vector<u8> GetC020105Reply() const;
195
196 // (STUBBED) Returns 8 byte empty reply
197 std::vector<u8> GetReadUnkCalReply() const;
198
199 // Returns 20 byte reply with factory calibration values
200 std::vector<u8> GetReadFactoryCalReply() const;
201
202 // Returns 20 byte reply with user calibration values
203 std::vector<u8> GetReadUserCalReply() const;
204
205 // Returns 8 byte reply
206 std::vector<u8> GetReadRepCountReply() const;
207
208 // Returns 8 byte reply
209 std::vector<u8> GetReadTotalPushCountReply() const;
210
211 // Returns 8 byte reply
212 std::vector<u8> GetResetRepCountReply() const;
213
214 // Returns 4 byte save data reply
215 std::vector<u8> GetSaveDataReply() const;
216
217 // Returns 8 byte error reply
218 std::vector<u8> GetErrorReply() const;
219
220 // Returns 8 bit redundancy check from provided data
221 u8 GetCrcValue(const std::vector<u8>& data) const;
222
223 // Converts structs to an u8 vector equivalent
224 template <typename T>
225 std::vector<u8> GetDataVector(const T& reply) const;
226
227 RingConCommands command{RingConCommands::Error};
228
229 // These counters are used in multitasking mode while the switch is sleeping
230 // Total steps taken
231 u8 total_rep_count = 0;
232 // Total times the ring was pushed
233 u8 total_push_count = 0;
234
235 const u8 device_id = 0x20;
236 const FirmwareVersion version = {
237 .sub = 0x0,
238 .main = 0x2c,
239 };
240 const FactoryCalibration factory_calibration = {
241 .os_max = idle_value + range + idle_deadzone,
242 .hk_max = idle_value - range - idle_deadzone,
243 .zero_min = idle_value - idle_deadzone,
244 .zero_max = idle_value + idle_deadzone,
245 };
246 UserCalibration user_calibration = {
247 .os_max = {.value = range, .crc = 228},
248 .hk_max = {.value = -range, .crc = 239},
249 .zero = {.value = idle_value, .crc = 225},
250 };
251
252 Core::HID::EmulatedDevices* input;
253};
254} // namespace Service::HID
diff --git a/src/core/hle/service/hid/hidbus/starlink.cpp b/src/core/hle/service/hid/hidbus/starlink.cpp
new file mode 100644
index 000000000..3175c48da
--- /dev/null
+++ b/src/core/hle/service/hid/hidbus/starlink.cpp
@@ -0,0 +1,51 @@
1// Copyright 2021 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include "core/hid/emulated_controller.h"
6#include "core/hid/hid_core.h"
7#include "core/hle/service/hid/hidbus/starlink.h"
8
9namespace Service::HID {
10constexpr u8 DEVICE_ID = 0x28;
11
12Starlink::Starlink(Core::HID::HIDCore& hid_core_, KernelHelpers::ServiceContext& service_context_)
13 : HidbusBase(service_context_) {}
14Starlink::~Starlink() = default;
15
16void Starlink::OnInit() {
17 return;
18}
19
20void Starlink::OnRelease() {
21 return;
22};
23
24void Starlink::OnUpdate() {
25 if (!is_activated) {
26 return;
27 }
28 if (!device_enabled) {
29 return;
30 }
31 if (!polling_mode_enabled || !is_transfer_memory_set) {
32 return;
33 }
34
35 LOG_ERROR(Service_HID, "Polling mode not supported {}", polling_mode);
36}
37
38u8 Starlink::GetDeviceId() const {
39 return DEVICE_ID;
40}
41
42std::vector<u8> Starlink::GetReply() const {
43 return {};
44}
45
46bool Starlink::SetCommand(const std::vector<u8>& data) {
47 LOG_ERROR(Service_HID, "Command not implemented");
48 return false;
49}
50
51} // namespace Service::HID
diff --git a/src/core/hle/service/hid/hidbus/starlink.h b/src/core/hle/service/hid/hidbus/starlink.h
new file mode 100644
index 000000000..79770b68e
--- /dev/null
+++ b/src/core/hle/service/hid/hidbus/starlink.h
@@ -0,0 +1,39 @@
1// Copyright 2021 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include "common/common_types.h"
8#include "core/hle/service/hid/hidbus/hidbus_base.h"
9
10namespace Core::HID {
11class EmulatedController;
12} // namespace Core::HID
13
14namespace Service::HID {
15
16class Starlink final : public HidbusBase {
17public:
18 explicit Starlink(Core::HID::HIDCore& hid_core_,
19 KernelHelpers::ServiceContext& service_context_);
20 ~Starlink() override;
21
22 void OnInit() override;
23
24 void OnRelease() override;
25
26 // Updates ringcon transfer memory
27 void OnUpdate() override;
28
29 // Returns the device ID of the joycon
30 u8 GetDeviceId() const override;
31
32 // Assigns a command from data
33 bool SetCommand(const std::vector<u8>& data) override;
34
35 // Returns a reply from a command
36 std::vector<u8> GetReply() const override;
37};
38
39} // namespace Service::HID
diff --git a/src/core/hle/service/hid/hidbus/stubbed.cpp b/src/core/hle/service/hid/hidbus/stubbed.cpp
new file mode 100644
index 000000000..5474657be
--- /dev/null
+++ b/src/core/hle/service/hid/hidbus/stubbed.cpp
@@ -0,0 +1,52 @@
1// Copyright 2021 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include "core/hid/emulated_controller.h"
6#include "core/hid/hid_core.h"
7#include "core/hle/service/hid/hidbus/stubbed.h"
8
9namespace Service::HID {
10constexpr u8 DEVICE_ID = 0xFF;
11
12HidbusStubbed::HidbusStubbed(Core::HID::HIDCore& hid_core_,
13 KernelHelpers::ServiceContext& service_context_)
14 : HidbusBase(service_context_) {}
15HidbusStubbed::~HidbusStubbed() = default;
16
17void HidbusStubbed::OnInit() {
18 return;
19}
20
21void HidbusStubbed::OnRelease() {
22 return;
23};
24
25void HidbusStubbed::OnUpdate() {
26 if (!is_activated) {
27 return;
28 }
29 if (!device_enabled) {
30 return;
31 }
32 if (!polling_mode_enabled || !is_transfer_memory_set) {
33 return;
34 }
35
36 LOG_ERROR(Service_HID, "Polling mode not supported {}", polling_mode);
37}
38
39u8 HidbusStubbed::GetDeviceId() const {
40 return DEVICE_ID;
41}
42
43std::vector<u8> HidbusStubbed::GetReply() const {
44 return {};
45}
46
47bool HidbusStubbed::SetCommand(const std::vector<u8>& data) {
48 LOG_ERROR(Service_HID, "Command not implemented");
49 return false;
50}
51
52} // namespace Service::HID
diff --git a/src/core/hle/service/hid/hidbus/stubbed.h b/src/core/hle/service/hid/hidbus/stubbed.h
new file mode 100644
index 000000000..40acdfe8f
--- /dev/null
+++ b/src/core/hle/service/hid/hidbus/stubbed.h
@@ -0,0 +1,39 @@
1// Copyright 2021 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include "common/common_types.h"
8#include "core/hle/service/hid/hidbus/hidbus_base.h"
9
10namespace Core::HID {
11class EmulatedController;
12} // namespace Core::HID
13
14namespace Service::HID {
15
16class HidbusStubbed final : public HidbusBase {
17public:
18 explicit HidbusStubbed(Core::HID::HIDCore& hid_core_,
19 KernelHelpers::ServiceContext& service_context_);
20 ~HidbusStubbed() override;
21
22 void OnInit() override;
23
24 void OnRelease() override;
25
26 // Updates ringcon transfer memory
27 void OnUpdate() override;
28
29 // Returns the device ID of the joycon
30 u8 GetDeviceId() const override;
31
32 // Assigns a command from data
33 bool SetCommand(const std::vector<u8>& data) override;
34
35 // Returns a reply from a command
36 std::vector<u8> GetReply() const override;
37};
38
39} // namespace Service::HID
diff --git a/src/core/hle/service/jit/jit.cpp b/src/core/hle/service/jit/jit.cpp
new file mode 100644
index 000000000..0f9e33ef6
--- /dev/null
+++ b/src/core/hle/service/jit/jit.cpp
@@ -0,0 +1,332 @@
1// Copyright 2022 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include "core/arm/symbols.h"
6#include "core/core.h"
7#include "core/hle/ipc_helpers.h"
8#include "core/hle/kernel/k_code_memory.h"
9#include "core/hle/kernel/k_transfer_memory.h"
10#include "core/hle/result.h"
11#include "core/hle/service/jit/jit.h"
12#include "core/hle/service/jit/jit_context.h"
13#include "core/hle/service/service.h"
14#include "core/memory.h"
15
16namespace Service::JIT {
17
18struct CodeRange {
19 u64 offset;
20 u64 size;
21};
22
23class IJitEnvironment final : public ServiceFramework<IJitEnvironment> {
24public:
25 explicit IJitEnvironment(Core::System& system_, CodeRange user_rx, CodeRange user_ro)
26 : ServiceFramework{system_, "IJitEnvironment", ServiceThreadType::CreateNew},
27 context{system_.Memory()} {
28 // clang-format off
29 static const FunctionInfo functions[] = {
30 {0, &IJitEnvironment::GenerateCode, "GenerateCode"},
31 {1, &IJitEnvironment::Control, "Control"},
32 {1000, &IJitEnvironment::LoadPlugin, "LoadPlugin"},
33 {1001, &IJitEnvironment::GetCodeAddress, "GetCodeAddress"},
34 };
35 // clang-format on
36
37 RegisterHandlers(functions);
38
39 // Identity map user code range into sysmodule context
40 configuration.user_ro_memory = user_ro;
41 configuration.user_rx_memory = user_rx;
42 configuration.sys_ro_memory = user_ro;
43 configuration.sys_rx_memory = user_rx;
44 }
45
46 void GenerateCode(Kernel::HLERequestContext& ctx) {
47 struct Parameters {
48 u32 data_size;
49 u64 command;
50 CodeRange cr1;
51 CodeRange cr2;
52 Struct32 data;
53 };
54
55 IPC::RequestParser rp{ctx};
56 const auto parameters{rp.PopRaw<Parameters>()};
57 std::vector<u8> input_buffer{ctx.CanReadBuffer() ? ctx.ReadBuffer() : std::vector<u8>()};
58 std::vector<u8> output_buffer(ctx.CanWriteBuffer() ? ctx.GetWriteBufferSize() : 0);
59
60 const VAddr return_ptr{context.AddHeap(0u)};
61 const VAddr cr1_in_ptr{context.AddHeap(parameters.cr1)};
62 const VAddr cr2_in_ptr{context.AddHeap(parameters.cr2)};
63 const VAddr cr1_out_ptr{
64 context.AddHeap(CodeRange{.offset = parameters.cr1.offset, .size = 0})};
65 const VAddr cr2_out_ptr{
66 context.AddHeap(CodeRange{.offset = parameters.cr2.offset, .size = 0})};
67 const VAddr input_ptr{context.AddHeap(input_buffer.data(), input_buffer.size())};
68 const VAddr output_ptr{context.AddHeap(output_buffer.data(), output_buffer.size())};
69 const VAddr data_ptr{context.AddHeap(parameters.data)};
70 const VAddr configuration_ptr{context.AddHeap(configuration)};
71
72 context.CallFunction(callbacks.GenerateCode, return_ptr, cr1_out_ptr, cr2_out_ptr,
73 configuration_ptr, parameters.command, input_ptr, input_buffer.size(),
74 cr1_in_ptr, cr2_in_ptr, data_ptr, parameters.data_size, output_ptr,
75 output_buffer.size());
76
77 const s32 return_value{context.GetHeap<s32>(return_ptr)};
78
79 if (return_value == 0) {
80 system.InvalidateCpuInstructionCacheRange(configuration.user_rx_memory.offset,
81 configuration.user_rx_memory.size);
82
83 if (ctx.CanWriteBuffer()) {
84 context.GetHeap(output_ptr, output_buffer.data(), output_buffer.size());
85 ctx.WriteBuffer(output_buffer.data(), output_buffer.size());
86 }
87 const auto cr1_out{context.GetHeap<CodeRange>(cr1_out_ptr)};
88 const auto cr2_out{context.GetHeap<CodeRange>(cr2_out_ptr)};
89
90 IPC::ResponseBuilder rb{ctx, 8};
91 rb.Push(ResultSuccess);
92 rb.Push<u64>(return_value);
93 rb.PushRaw(cr1_out);
94 rb.PushRaw(cr2_out);
95 } else {
96 LOG_WARNING(Service_JIT, "plugin GenerateCode callback failed");
97 IPC::ResponseBuilder rb{ctx, 2};
98 rb.Push(ResultUnknown);
99 }
100 };
101
102 void Control(Kernel::HLERequestContext& ctx) {
103 IPC::RequestParser rp{ctx};
104 const auto command{rp.PopRaw<u64>()};
105 const auto input_buffer{ctx.ReadBuffer()};
106 std::vector<u8> output_buffer(ctx.CanWriteBuffer() ? ctx.GetWriteBufferSize() : 0);
107
108 const VAddr return_ptr{context.AddHeap(0u)};
109 const VAddr configuration_ptr{context.AddHeap(configuration)};
110 const VAddr input_ptr{context.AddHeap(input_buffer.data(), input_buffer.size())};
111 const VAddr output_ptr{context.AddHeap(output_buffer.data(), output_buffer.size())};
112 const u64 wrapper_value{
113 context.CallFunction(callbacks.Control, return_ptr, configuration_ptr, command,
114 input_ptr, input_buffer.size(), output_ptr, output_buffer.size())};
115 const s32 return_value{context.GetHeap<s32>(return_ptr)};
116
117 if (wrapper_value == 0 && return_value == 0) {
118 if (ctx.CanWriteBuffer()) {
119 context.GetHeap(output_ptr, output_buffer.data(), output_buffer.size());
120 ctx.WriteBuffer(output_buffer.data(), output_buffer.size());
121 }
122 IPC::ResponseBuilder rb{ctx, 3};
123 rb.Push(ResultSuccess);
124 rb.Push(return_value);
125 } else {
126 LOG_WARNING(Service_JIT, "plugin Control callback failed");
127 IPC::ResponseBuilder rb{ctx, 2};
128 rb.Push(ResultUnknown);
129 }
130 }
131
132 void LoadPlugin(Kernel::HLERequestContext& ctx) {
133 IPC::RequestParser rp{ctx};
134 const auto tmem_size{rp.PopRaw<u64>()};
135 if (tmem_size == 0) {
136 LOG_ERROR(Service_JIT, "attempted to load plugin with empty transfer memory");
137 IPC::ResponseBuilder rb{ctx, 2};
138 rb.Push(ResultUnknown);
139 return;
140 }
141
142 const auto tmem_handle{ctx.GetCopyHandle(0)};
143 auto tmem{system.CurrentProcess()->GetHandleTable().GetObject<Kernel::KTransferMemory>(
144 tmem_handle)};
145 if (tmem.IsNull()) {
146 LOG_ERROR(Service_JIT, "attempted to load plugin with invalid transfer memory handle");
147 IPC::ResponseBuilder rb{ctx, 2};
148 rb.Push(ResultUnknown);
149 return;
150 }
151
152 configuration.work_memory.offset = tmem->GetSourceAddress();
153 configuration.work_memory.size = tmem_size;
154
155 const auto nro_plugin{ctx.ReadBuffer(1)};
156 auto symbols{Core::Symbols::GetSymbols(nro_plugin, true)};
157 const auto GetSymbol{[&](std::string name) { return symbols[name].first; }};
158
159 callbacks =
160 GuestCallbacks{.rtld_fini = GetSymbol("_fini"),
161 .rtld_init = GetSymbol("_init"),
162 .Control = GetSymbol("nnjitpluginControl"),
163 .ResolveBasicSymbols = GetSymbol("nnjitpluginResolveBasicSymbols"),
164 .SetupDiagnostics = GetSymbol("nnjitpluginSetupDiagnostics"),
165 .Configure = GetSymbol("nnjitpluginConfigure"),
166 .GenerateCode = GetSymbol("nnjitpluginGenerateCode"),
167 .GetVersion = GetSymbol("nnjitpluginGetVersion"),
168 .Keeper = GetSymbol("nnjitpluginKeeper"),
169 .OnPrepared = GetSymbol("nnjitpluginOnPrepared")};
170
171 if (callbacks.GetVersion == 0 || callbacks.Configure == 0 || callbacks.GenerateCode == 0 ||
172 callbacks.OnPrepared == 0) {
173 LOG_ERROR(Service_JIT, "plugin does not implement all necessary functionality");
174 IPC::ResponseBuilder rb{ctx, 2};
175 rb.Push(ResultUnknown);
176 return;
177 }
178
179 if (!context.LoadNRO(nro_plugin)) {
180 LOG_ERROR(Service_JIT, "failed to load plugin");
181 IPC::ResponseBuilder rb{ctx, 2};
182 rb.Push(ResultUnknown);
183 return;
184 }
185
186 context.MapProcessMemory(configuration.sys_ro_memory.offset,
187 configuration.sys_ro_memory.size);
188 context.MapProcessMemory(configuration.sys_rx_memory.offset,
189 configuration.sys_rx_memory.size);
190 context.MapProcessMemory(configuration.work_memory.offset, configuration.work_memory.size);
191
192 if (callbacks.rtld_init != 0) {
193 context.CallFunction(callbacks.rtld_init);
194 }
195
196 const auto version{context.CallFunction(callbacks.GetVersion)};
197 if (version != 1) {
198 LOG_ERROR(Service_JIT, "unknown plugin version {}", version);
199 IPC::ResponseBuilder rb{ctx, 2};
200 rb.Push(ResultUnknown);
201 return;
202 }
203
204 const auto resolve{context.GetHelper("_resolve")};
205 if (callbacks.ResolveBasicSymbols != 0) {
206 context.CallFunction(callbacks.ResolveBasicSymbols, resolve);
207 }
208 const auto resolve_ptr{context.AddHeap(resolve)};
209 if (callbacks.SetupDiagnostics != 0) {
210 context.CallFunction(callbacks.SetupDiagnostics, 0u, resolve_ptr);
211 }
212
213 context.CallFunction(callbacks.Configure, 0u);
214 const auto configuration_ptr{context.AddHeap(configuration)};
215 context.CallFunction(callbacks.OnPrepared, configuration_ptr);
216
217 IPC::ResponseBuilder rb{ctx, 2};
218 rb.Push(ResultSuccess);
219 }
220
221 void GetCodeAddress(Kernel::HLERequestContext& ctx) {
222 IPC::ResponseBuilder rb{ctx, 6};
223 rb.Push(ResultSuccess);
224 rb.Push(configuration.user_rx_memory.offset);
225 rb.Push(configuration.user_ro_memory.offset);
226 }
227
228private:
229 using Struct32 = std::array<u8, 32>;
230
231 struct GuestCallbacks {
232 VAddr rtld_fini;
233 VAddr rtld_init;
234 VAddr Control;
235 VAddr ResolveBasicSymbols;
236 VAddr SetupDiagnostics;
237 VAddr Configure;
238 VAddr GenerateCode;
239 VAddr GetVersion;
240 VAddr Keeper;
241 VAddr OnPrepared;
242 };
243
244 struct JITConfiguration {
245 CodeRange user_rx_memory;
246 CodeRange user_ro_memory;
247 CodeRange work_memory;
248 CodeRange sys_rx_memory;
249 CodeRange sys_ro_memory;
250 };
251
252 GuestCallbacks callbacks;
253 JITConfiguration configuration;
254 JITContext context;
255};
256
257class JITU final : public ServiceFramework<JITU> {
258public:
259 explicit JITU(Core::System& system_) : ServiceFramework{system_, "jit:u"} {
260 // clang-format off
261 static const FunctionInfo functions[] = {
262 {0, &JITU::CreateJitEnvironment, "CreateJitEnvironment"},
263 };
264 // clang-format on
265
266 RegisterHandlers(functions);
267 }
268
269 void CreateJitEnvironment(Kernel::HLERequestContext& ctx) {
270 LOG_DEBUG(Service_JIT, "called");
271
272 struct Parameters {
273 u64 rx_size;
274 u64 ro_size;
275 };
276
277 IPC::RequestParser rp{ctx};
278 const auto parameters{rp.PopRaw<Parameters>()};
279 const auto executable_mem_handle{ctx.GetCopyHandle(1)};
280 const auto readable_mem_handle{ctx.GetCopyHandle(2)};
281
282 if (parameters.rx_size == 0 || parameters.ro_size == 0) {
283 LOG_ERROR(Service_JIT, "attempted to init with empty code regions");
284 IPC::ResponseBuilder rb{ctx, 2};
285 rb.Push(ResultUnknown);
286 return;
287 }
288
289 // The copy handle at index 0 is the process handle, but handle tables are
290 // per-process, so there is no point reading it here until we are multiprocess
291 const auto& process{*system.CurrentProcess()};
292
293 auto executable_mem{
294 process.GetHandleTable().GetObject<Kernel::KCodeMemory>(executable_mem_handle)};
295 if (executable_mem.IsNull()) {
296 LOG_ERROR(Service_JIT, "executable_mem is null for handle=0x{:08X}",
297 executable_mem_handle);
298 IPC::ResponseBuilder rb{ctx, 2};
299 rb.Push(ResultUnknown);
300 return;
301 }
302
303 auto readable_mem{
304 process.GetHandleTable().GetObject<Kernel::KCodeMemory>(readable_mem_handle)};
305 if (readable_mem.IsNull()) {
306 LOG_ERROR(Service_JIT, "readable_mem is null for handle=0x{:08X}", readable_mem_handle);
307 IPC::ResponseBuilder rb{ctx, 2};
308 rb.Push(ResultUnknown);
309 return;
310 }
311
312 const CodeRange user_rx{
313 .offset = executable_mem->GetSourceAddress(),
314 .size = parameters.rx_size,
315 };
316
317 const CodeRange user_ro{
318 .offset = readable_mem->GetSourceAddress(),
319 .size = parameters.ro_size,
320 };
321
322 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
323 rb.Push(ResultSuccess);
324 rb.PushIpcInterface<IJitEnvironment>(system, user_rx, user_ro);
325 }
326};
327
328void InstallInterfaces(SM::ServiceManager& sm, Core::System& system) {
329 std::make_shared<JITU>(system)->InstallAsService(sm);
330}
331
332} // namespace Service::JIT
diff --git a/src/core/hle/service/jit/jit.h b/src/core/hle/service/jit/jit.h
new file mode 100644
index 000000000..8fbf504a1
--- /dev/null
+++ b/src/core/hle/service/jit/jit.h
@@ -0,0 +1,20 @@
1// Copyright 2022 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7namespace Core {
8class System;
9}
10
11namespace Service::SM {
12class ServiceManager;
13}
14
15namespace Service::JIT {
16
17/// Registers all JIT services with the specified service manager.
18void InstallInterfaces(SM::ServiceManager& sm, Core::System& system);
19
20} // namespace Service::JIT
diff --git a/src/core/hle/service/jit/jit_context.cpp b/src/core/hle/service/jit/jit_context.cpp
new file mode 100644
index 000000000..630368fb3
--- /dev/null
+++ b/src/core/hle/service/jit/jit_context.cpp
@@ -0,0 +1,424 @@
1// Copyright 2022 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <array>
6#include <map>
7#include <span>
8#include <boost/icl/interval_set.hpp>
9#include <dynarmic/interface/A64/a64.h>
10#include <dynarmic/interface/A64/config.h>
11
12#include "common/alignment.h"
13#include "common/common_funcs.h"
14#include "common/div_ceil.h"
15#include "common/logging/log.h"
16#include "core/hle/service/jit/jit_context.h"
17#include "core/memory.h"
18
19namespace Service::JIT {
20
21constexpr std::array<u8, 4> STOP_ARM64 = {
22 0x01, 0x00, 0x00, 0xd4, // svc #0
23};
24
25constexpr std::array<u8, 8> RESOLVE_ARM64 = {
26 0x21, 0x00, 0x00, 0xd4, // svc #1
27 0xc0, 0x03, 0x5f, 0xd6, // ret
28};
29
30constexpr std::array<u8, 4> PANIC_ARM64 = {
31 0x41, 0x00, 0x00, 0xd4, // svc #2
32};
33
34constexpr std::array<u8, 60> MEMMOVE_ARM64 = {
35 0x1f, 0x00, 0x01, 0xeb, // cmp x0, x1
36 0x83, 0x01, 0x00, 0x54, // b.lo #+34
37 0x42, 0x04, 0x00, 0xd1, // sub x2, x2, 1
38 0x22, 0x01, 0xf8, 0xb7, // tbnz x2, #63, #+36
39 0x23, 0x68, 0x62, 0x38, // ldrb w3, [x1, x2]
40 0x03, 0x68, 0x22, 0x38, // strb w3, [x0, x2]
41 0xfc, 0xff, 0xff, 0x17, // b #-16
42 0x24, 0x68, 0x63, 0x38, // ldrb w4, [x1, x3]
43 0x04, 0x68, 0x23, 0x38, // strb w4, [x0, x3]
44 0x63, 0x04, 0x00, 0x91, // add x3, x3, 1
45 0x7f, 0x00, 0x02, 0xeb, // cmp x3, x2
46 0x8b, 0xff, 0xff, 0x54, // b.lt #-16
47 0xc0, 0x03, 0x5f, 0xd6, // ret
48 0x03, 0x00, 0x80, 0xd2, // mov x3, 0
49 0xfc, 0xff, 0xff, 0x17, // b #-16
50};
51
52constexpr std::array<u8, 28> MEMSET_ARM64 = {
53 0x03, 0x00, 0x80, 0xd2, // mov x3, 0
54 0x7f, 0x00, 0x02, 0xeb, // cmp x3, x2
55 0x4b, 0x00, 0x00, 0x54, // b.lt #+8
56 0xc0, 0x03, 0x5f, 0xd6, // ret
57 0x01, 0x68, 0x23, 0x38, // strb w1, [x0, x3]
58 0x63, 0x04, 0x00, 0x91, // add x3, x3, 1
59 0xfb, 0xff, 0xff, 0x17, // b #-20
60};
61
62struct HelperFunction {
63 const char* name;
64 const std::span<const u8> data;
65};
66
67constexpr std::array<HelperFunction, 6> HELPER_FUNCTIONS{{
68 {"_stop", STOP_ARM64},
69 {"_resolve", RESOLVE_ARM64},
70 {"_panic", PANIC_ARM64},
71 {"memcpy", MEMMOVE_ARM64},
72 {"memmove", MEMMOVE_ARM64},
73 {"memset", MEMSET_ARM64},
74}};
75
76struct Elf64_Dyn {
77 u64 d_tag;
78 u64 d_un;
79};
80
81struct Elf64_Rela {
82 u64 r_offset;
83 u64 r_info;
84 s64 r_addend;
85};
86
87static constexpr u32 Elf64_RelaType(const Elf64_Rela* rela) {
88 return static_cast<u32>(rela->r_info);
89}
90
91constexpr int DT_RELA = 7; /* Address of Rela relocs */
92constexpr int DT_RELASZ = 8; /* Total size of Rela relocs */
93constexpr int R_AARCH64_RELATIVE = 1027; /* Adjust by program base. */
94
95constexpr size_t STACK_ALIGN = 16;
96
97class JITContextImpl;
98
99using IntervalSet = boost::icl::interval_set<VAddr>::type;
100using IntervalType = boost::icl::interval_set<VAddr>::interval_type;
101
102class DynarmicCallbacks64 : public Dynarmic::A64::UserCallbacks {
103public:
104 explicit DynarmicCallbacks64(Core::Memory::Memory& memory_, std::vector<u8>& local_memory_,
105 IntervalSet& mapped_ranges_, JITContextImpl& parent_)
106 : memory{memory_}, local_memory{local_memory_},
107 mapped_ranges{mapped_ranges_}, parent{parent_} {}
108
109 u8 MemoryRead8(u64 vaddr) override {
110 return ReadMemory<u8>(vaddr);
111 }
112 u16 MemoryRead16(u64 vaddr) override {
113 return ReadMemory<u16>(vaddr);
114 }
115 u32 MemoryRead32(u64 vaddr) override {
116 return ReadMemory<u32>(vaddr);
117 }
118 u64 MemoryRead64(u64 vaddr) override {
119 return ReadMemory<u64>(vaddr);
120 }
121 u128 MemoryRead128(u64 vaddr) override {
122 return ReadMemory<u128>(vaddr);
123 }
124 std::string MemoryReadCString(u64 vaddr) {
125 std::string result;
126 u8 next;
127
128 while ((next = MemoryRead8(vaddr++)) != 0) {
129 result += next;
130 }
131
132 return result;
133 }
134
135 void MemoryWrite8(u64 vaddr, u8 value) override {
136 WriteMemory<u8>(vaddr, value);
137 }
138 void MemoryWrite16(u64 vaddr, u16 value) override {
139 WriteMemory<u16>(vaddr, value);
140 }
141 void MemoryWrite32(u64 vaddr, u32 value) override {
142 WriteMemory<u32>(vaddr, value);
143 }
144 void MemoryWrite64(u64 vaddr, u64 value) override {
145 WriteMemory<u64>(vaddr, value);
146 }
147 void MemoryWrite128(u64 vaddr, u128 value) override {
148 WriteMemory<u128>(vaddr, value);
149 }
150
151 bool MemoryWriteExclusive8(u64 vaddr, u8 value, u8) override {
152 return WriteMemory<u8>(vaddr, value);
153 }
154 bool MemoryWriteExclusive16(u64 vaddr, u16 value, u16) override {
155 return WriteMemory<u16>(vaddr, value);
156 }
157 bool MemoryWriteExclusive32(u64 vaddr, u32 value, u32) override {
158 return WriteMemory<u32>(vaddr, value);
159 }
160 bool MemoryWriteExclusive64(u64 vaddr, u64 value, u64) override {
161 return WriteMemory<u64>(vaddr, value);
162 }
163 bool MemoryWriteExclusive128(u64 vaddr, u128 value, u128) override {
164 return WriteMemory<u128>(vaddr, value);
165 }
166
167 void CallSVC(u32 swi) override;
168 void ExceptionRaised(u64 pc, Dynarmic::A64::Exception exception) override;
169 void InterpreterFallback(u64 pc, size_t num_instructions) override;
170
171 void AddTicks(u64 ticks) override {}
172 u64 GetTicksRemaining() override {
173 return std::numeric_limits<u32>::max();
174 }
175 u64 GetCNTPCT() override {
176 return 0;
177 }
178
179 template <class T>
180 T ReadMemory(u64 vaddr) {
181 T ret{};
182 if (boost::icl::contains(mapped_ranges, vaddr)) {
183 memory.ReadBlock(vaddr, &ret, sizeof(T));
184 } else if (vaddr + sizeof(T) > local_memory.size()) {
185 LOG_CRITICAL(Service_JIT, "plugin: unmapped read @ 0x{:016x}", vaddr);
186 } else {
187 std::memcpy(&ret, local_memory.data() + vaddr, sizeof(T));
188 }
189 return ret;
190 }
191
192 template <class T>
193 bool WriteMemory(u64 vaddr, const T value) {
194 if (boost::icl::contains(mapped_ranges, vaddr)) {
195 memory.WriteBlock(vaddr, &value, sizeof(T));
196 } else if (vaddr + sizeof(T) > local_memory.size()) {
197 LOG_CRITICAL(Service_JIT, "plugin: unmapped write @ 0x{:016x}", vaddr);
198 } else {
199 std::memcpy(local_memory.data() + vaddr, &value, sizeof(T));
200 }
201 return true;
202 }
203
204private:
205 Core::Memory::Memory& memory;
206 std::vector<u8>& local_memory;
207 IntervalSet& mapped_ranges;
208 JITContextImpl& parent;
209};
210
211class JITContextImpl {
212public:
213 explicit JITContextImpl(Core::Memory::Memory& memory_) : memory{memory_} {
214 callbacks =
215 std::make_unique<DynarmicCallbacks64>(memory, local_memory, mapped_ranges, *this);
216 user_config.callbacks = callbacks.get();
217 jit = std::make_unique<Dynarmic::A64::Jit>(user_config);
218 }
219
220 bool LoadNRO(std::span<const u8> data) {
221 local_memory.clear();
222 local_memory.insert(local_memory.end(), data.begin(), data.end());
223
224 if (FixupRelocations()) {
225 InsertHelperFunctions();
226 InsertStack();
227 return true;
228 } else {
229 return false;
230 }
231 }
232
233 bool FixupRelocations() {
234 const VAddr mod_offset{callbacks->MemoryRead32(4)};
235 if (callbacks->MemoryRead32(mod_offset) != Common::MakeMagic('M', 'O', 'D', '0')) {
236 return false;
237 }
238
239 VAddr dynamic_offset{mod_offset + callbacks->MemoryRead32(mod_offset + 4)};
240 VAddr rela_dyn = 0;
241 size_t num_rela = 0;
242 while (true) {
243 const auto dyn{callbacks->ReadMemory<Elf64_Dyn>(dynamic_offset)};
244 dynamic_offset += sizeof(Elf64_Dyn);
245
246 if (!dyn.d_tag) {
247 break;
248 }
249 if (dyn.d_tag == DT_RELA) {
250 rela_dyn = dyn.d_un;
251 }
252 if (dyn.d_tag == DT_RELASZ) {
253 num_rela = dyn.d_un / sizeof(Elf64_Rela);
254 }
255 }
256
257 for (size_t i = 0; i < num_rela; i++) {
258 const auto rela{callbacks->ReadMemory<Elf64_Rela>(rela_dyn + i * sizeof(Elf64_Rela))};
259 if (Elf64_RelaType(&rela) != R_AARCH64_RELATIVE) {
260 continue;
261 }
262 const VAddr contents{callbacks->MemoryRead64(rela.r_offset)};
263 callbacks->MemoryWrite64(rela.r_offset, contents + rela.r_addend);
264 }
265
266 return true;
267 }
268
269 void InsertHelperFunctions() {
270 for (const auto& [name, contents] : HELPER_FUNCTIONS) {
271 helpers[name] = local_memory.size();
272 local_memory.insert(local_memory.end(), contents.begin(), contents.end());
273 }
274 }
275
276 void InsertStack() {
277 const u64 pad_amount{Common::AlignUp(local_memory.size(), STACK_ALIGN) -
278 local_memory.size()};
279 local_memory.insert(local_memory.end(), 0x10000 + pad_amount, 0);
280 top_of_stack = local_memory.size();
281 heap_pointer = top_of_stack;
282 }
283
284 void MapProcessMemory(VAddr dest_address, std::size_t size) {
285 mapped_ranges.add(IntervalType{dest_address, dest_address + size});
286 }
287
288 void PushArgument(const void* data, size_t size) {
289 const size_t num_words = Common::DivCeil(size, sizeof(u64));
290 const size_t current_pos = argument_stack.size();
291 argument_stack.insert(argument_stack.end(), num_words, 0);
292 std::memcpy(argument_stack.data() + current_pos, data, size);
293 }
294
295 void SetupArguments() {
296 for (size_t i = 0; i < 8 && i < argument_stack.size(); i++) {
297 jit->SetRegister(i, argument_stack[i]);
298 }
299 if (argument_stack.size() > 8) {
300 const VAddr new_sp = Common::AlignDown(
301 top_of_stack - (argument_stack.size() - 8) * sizeof(u64), STACK_ALIGN);
302 for (size_t i = 8; i < argument_stack.size(); i++) {
303 callbacks->MemoryWrite64(new_sp + (i - 8) * sizeof(u64), argument_stack[i]);
304 }
305 jit->SetSP(new_sp);
306 }
307 argument_stack.clear();
308 heap_pointer = top_of_stack;
309 }
310
311 u64 CallFunction(VAddr func) {
312 jit->SetRegister(30, helpers["_stop"]);
313 jit->SetSP(top_of_stack);
314 SetupArguments();
315
316 jit->SetPC(func);
317 jit->Run();
318 return jit->GetRegister(0);
319 }
320
321 VAddr GetHelper(const std::string& name) {
322 return helpers[name];
323 }
324
325 VAddr AddHeap(const void* data, size_t size) {
326 const size_t num_bytes{Common::AlignUp(size, STACK_ALIGN)};
327 if (heap_pointer + num_bytes > local_memory.size()) {
328 local_memory.insert(local_memory.end(),
329 (heap_pointer + num_bytes) - local_memory.size(), 0);
330 }
331 const VAddr location{heap_pointer};
332 std::memcpy(local_memory.data() + location, data, size);
333 heap_pointer += num_bytes;
334 return location;
335 }
336
337 void GetHeap(VAddr location, void* data, size_t size) {
338 std::memcpy(data, local_memory.data() + location, size);
339 }
340
341 std::unique_ptr<DynarmicCallbacks64> callbacks;
342 std::vector<u8> local_memory;
343 std::vector<u64> argument_stack;
344 IntervalSet mapped_ranges;
345 Dynarmic::A64::UserConfig user_config;
346 std::unique_ptr<Dynarmic::A64::Jit> jit;
347 std::map<std::string, VAddr, std::less<>> helpers;
348 Core::Memory::Memory& memory;
349 VAddr top_of_stack;
350 VAddr heap_pointer;
351};
352
353void DynarmicCallbacks64::CallSVC(u32 swi) {
354 switch (swi) {
355 case 0:
356 parent.jit->HaltExecution();
357 break;
358
359 case 1: {
360 // X0 contains a char* for a symbol to resolve
361 std::string name{MemoryReadCString(parent.jit->GetRegister(0))};
362 const auto helper{parent.helpers[name]};
363
364 if (helper != 0) {
365 parent.jit->SetRegister(0, helper);
366 } else {
367 LOG_WARNING(Service_JIT, "plugin requested unknown function {}", name);
368 parent.jit->SetRegister(0, parent.helpers["_panic"]);
369 }
370 break;
371 }
372
373 case 2:
374 default:
375 LOG_CRITICAL(Service_JIT, "plugin panicked!");
376 parent.jit->HaltExecution();
377 break;
378 }
379}
380
381void DynarmicCallbacks64::ExceptionRaised(u64 pc, Dynarmic::A64::Exception exception) {
382 LOG_CRITICAL(Service_JIT, "Illegal operation PC @ {:08x}", pc);
383 parent.jit->HaltExecution();
384}
385
386void DynarmicCallbacks64::InterpreterFallback(u64 pc, size_t num_instructions) {
387 LOG_CRITICAL(Service_JIT, "Unimplemented instruction PC @ {:08x}", pc);
388 parent.jit->HaltExecution();
389}
390
391JITContext::JITContext(Core::Memory::Memory& memory)
392 : impl{std::make_unique<JITContextImpl>(memory)} {}
393
394JITContext::~JITContext() {}
395
396bool JITContext::LoadNRO(std::span<const u8> data) {
397 return impl->LoadNRO(data);
398}
399
400void JITContext::MapProcessMemory(VAddr dest_address, std::size_t size) {
401 impl->MapProcessMemory(dest_address, size);
402}
403
404u64 JITContext::CallFunction(VAddr func) {
405 return impl->CallFunction(func);
406}
407
408void JITContext::PushArgument(const void* data, size_t size) {
409 impl->PushArgument(data, size);
410}
411
412VAddr JITContext::GetHelper(const std::string& name) {
413 return impl->GetHelper(name);
414}
415
416VAddr JITContext::AddHeap(const void* data, size_t size) {
417 return impl->AddHeap(data, size);
418}
419
420void JITContext::GetHeap(VAddr location, void* data, size_t size) {
421 impl->GetHeap(location, data, size);
422}
423
424} // namespace Service::JIT
diff --git a/src/core/hle/service/jit/jit_context.h b/src/core/hle/service/jit/jit_context.h
new file mode 100644
index 000000000..d8bf76cff
--- /dev/null
+++ b/src/core/hle/service/jit/jit_context.h
@@ -0,0 +1,65 @@
1// Copyright 2022 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <memory>
8#include <span>
9#include <string>
10
11#include "common/common_types.h"
12
13namespace Core::Memory {
14class Memory;
15}
16
17namespace Service::JIT {
18
19class JITContextImpl;
20
21class JITContext {
22public:
23 explicit JITContext(Core::Memory::Memory& memory);
24 ~JITContext();
25
26 [[nodiscard]] bool LoadNRO(std::span<const u8> data);
27 void MapProcessMemory(VAddr dest_address, std::size_t size);
28
29 template <typename T, typename... Ts>
30 u64 CallFunction(VAddr func, T argument, Ts... rest) {
31 static_assert(std::is_trivially_copyable_v<T>);
32 PushArgument(&argument, sizeof(argument));
33
34 if constexpr (sizeof...(rest) > 0) {
35 return CallFunction(func, rest...);
36 } else {
37 return CallFunction(func);
38 }
39 }
40
41 u64 CallFunction(VAddr func);
42 VAddr GetHelper(const std::string& name);
43
44 template <typename T>
45 VAddr AddHeap(T argument) {
46 return AddHeap(&argument, sizeof(argument));
47 }
48 VAddr AddHeap(const void* data, size_t size);
49
50 template <typename T>
51 T GetHeap(VAddr location) {
52 static_assert(std::is_trivially_copyable_v<T>);
53 T result;
54 GetHeap(location, &result, sizeof(result));
55 return result;
56 }
57 void GetHeap(VAddr location, void* data, size_t size);
58
59private:
60 std::unique_ptr<JITContextImpl> impl;
61
62 void PushArgument(const void* data, size_t size);
63};
64
65} // namespace Service::JIT
diff --git a/src/core/hle/service/ldr/ldr.cpp b/src/core/hle/service/ldr/ldr.cpp
index 2477c5612..42f9cf811 100644
--- a/src/core/hle/service/ldr/ldr.cpp
+++ b/src/core/hle/service/ldr/ldr.cpp
@@ -160,7 +160,8 @@ public:
160 160
161class RelocatableObject final : public ServiceFramework<RelocatableObject> { 161class RelocatableObject final : public ServiceFramework<RelocatableObject> {
162public: 162public:
163 explicit RelocatableObject(Core::System& system_) : ServiceFramework{system_, "ldr:ro"} { 163 explicit RelocatableObject(Core::System& system_)
164 : ServiceFramework{system_, "ldr:ro", ServiceThreadType::CreateNew} {
164 // clang-format off 165 // clang-format off
165 static const FunctionInfo functions[] = { 166 static const FunctionInfo functions[] = {
166 {0, &RelocatableObject::LoadModule, "LoadModule"}, 167 {0, &RelocatableObject::LoadModule, "LoadModule"},
@@ -389,8 +390,12 @@ public:
389 390
390 if (bss_size) { 391 if (bss_size) {
391 auto block_guard = detail::ScopeExit([&] { 392 auto block_guard = detail::ScopeExit([&] {
392 page_table.UnmapCodeMemory(addr + nro_size, bss_addr, bss_size); 393 page_table.UnmapCodeMemory(
393 page_table.UnmapCodeMemory(addr, nro_addr, nro_size); 394 addr + nro_size, bss_addr, bss_size,
395 Kernel::KPageTable::ICacheInvalidationStrategy::InvalidateRange);
396 page_table.UnmapCodeMemory(
397 addr, nro_addr, nro_size,
398 Kernel::KPageTable::ICacheInvalidationStrategy::InvalidateRange);
394 }); 399 });
395 400
396 const ResultCode result{ 401 const ResultCode result{
@@ -570,17 +575,21 @@ public:
570 auto& page_table{system.CurrentProcess()->PageTable()}; 575 auto& page_table{system.CurrentProcess()->PageTable()};
571 576
572 if (info.bss_size != 0) { 577 if (info.bss_size != 0) {
573 CASCADE_CODE(page_table.UnmapCodeMemory(info.nro_address + info.text_size + 578 CASCADE_CODE(page_table.UnmapCodeMemory(
574 info.ro_size + info.data_size, 579 info.nro_address + info.text_size + info.ro_size + info.data_size, info.bss_address,
575 info.bss_address, info.bss_size)); 580 info.bss_size, Kernel::KPageTable::ICacheInvalidationStrategy::InvalidateRange));
576 } 581 }
577 582
578 CASCADE_CODE(page_table.UnmapCodeMemory(info.nro_address + info.text_size + info.ro_size, 583 CASCADE_CODE(page_table.UnmapCodeMemory(
579 info.src_addr + info.text_size + info.ro_size, 584 info.nro_address + info.text_size + info.ro_size,
580 info.data_size)); 585 info.src_addr + info.text_size + info.ro_size, info.data_size,
581 CASCADE_CODE(page_table.UnmapCodeMemory(info.nro_address + info.text_size, 586 Kernel::KPageTable::ICacheInvalidationStrategy::InvalidateRange));
582 info.src_addr + info.text_size, info.ro_size)); 587 CASCADE_CODE(page_table.UnmapCodeMemory(
583 CASCADE_CODE(page_table.UnmapCodeMemory(info.nro_address, info.src_addr, info.text_size)); 588 info.nro_address + info.text_size, info.src_addr + info.text_size, info.ro_size,
589 Kernel::KPageTable::ICacheInvalidationStrategy::InvalidateRange));
590 CASCADE_CODE(page_table.UnmapCodeMemory(
591 info.nro_address, info.src_addr, info.text_size,
592 Kernel::KPageTable::ICacheInvalidationStrategy::InvalidateRange));
584 return ResultSuccess; 593 return ResultSuccess;
585 } 594 }
586 595
diff --git a/src/core/hle/service/mii/mii_manager.cpp b/src/core/hle/service/mii/mii_manager.cpp
index 0a57c3cde..188231615 100644
--- a/src/core/hle/service/mii/mii_manager.cpp
+++ b/src/core/hle/service/mii/mii_manager.cpp
@@ -1,4 +1,4 @@
1// Copyright 2020 yuzu emulator team 1// Copyright 2020 yuzu Emulator Project
2// Licensed under GPLv2 or any later version 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
@@ -12,7 +12,6 @@
12#include "core/hle/service/acc/profile_manager.h" 12#include "core/hle/service/acc/profile_manager.h"
13#include "core/hle/service/mii/mii_manager.h" 13#include "core/hle/service/mii/mii_manager.h"
14#include "core/hle/service/mii/raw_data.h" 14#include "core/hle/service/mii/raw_data.h"
15#include "core/hle/service/mii/types.h"
16 15
17namespace Service::Mii { 16namespace Service::Mii {
18 17
diff --git a/src/core/hle/service/mii/mii_manager.h b/src/core/hle/service/mii/mii_manager.h
index 6999d15b1..5d134c425 100644
--- a/src/core/hle/service/mii/mii_manager.h
+++ b/src/core/hle/service/mii/mii_manager.h
@@ -1,315 +1,16 @@
1// Copyright 2020 yuzu emulator team 1// Copyright 2020 yuzu Emulator Project
2// Licensed under GPLv2 or any later version 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#pragma once 5#pragma once
6 6
7#include <array>
8#include <vector> 7#include <vector>
9#include "common/bit_field.h" 8
10#include "common/common_funcs.h"
11#include "common/uuid.h"
12#include "core/hle/result.h" 9#include "core/hle/result.h"
13#include "core/hle/service/mii/types.h" 10#include "core/hle/service/mii/types.h"
14 11
15namespace Service::Mii { 12namespace Service::Mii {
16 13
17enum class Source : u32 {
18 Database = 0,
19 Default = 1,
20 Account = 2,
21 Friend = 3,
22};
23
24enum class SourceFlag : u32 {
25 None = 0,
26 Database = 1 << 0,
27 Default = 1 << 1,
28};
29DECLARE_ENUM_FLAG_OPERATORS(SourceFlag);
30
31struct MiiInfo {
32 Common::UUID uuid;
33 std::array<char16_t, 11> name;
34 u8 font_region;
35 u8 favorite_color;
36 u8 gender;
37 u8 height;
38 u8 build;
39 u8 type;
40 u8 region_move;
41 u8 faceline_type;
42 u8 faceline_color;
43 u8 faceline_wrinkle;
44 u8 faceline_make;
45 u8 hair_type;
46 u8 hair_color;
47 u8 hair_flip;
48 u8 eye_type;
49 u8 eye_color;
50 u8 eye_scale;
51 u8 eye_aspect;
52 u8 eye_rotate;
53 u8 eye_x;
54 u8 eye_y;
55 u8 eyebrow_type;
56 u8 eyebrow_color;
57 u8 eyebrow_scale;
58 u8 eyebrow_aspect;
59 u8 eyebrow_rotate;
60 u8 eyebrow_x;
61 u8 eyebrow_y;
62 u8 nose_type;
63 u8 nose_scale;
64 u8 nose_y;
65 u8 mouth_type;
66 u8 mouth_color;
67 u8 mouth_scale;
68 u8 mouth_aspect;
69 u8 mouth_y;
70 u8 beard_color;
71 u8 beard_type;
72 u8 mustache_type;
73 u8 mustache_scale;
74 u8 mustache_y;
75 u8 glasses_type;
76 u8 glasses_color;
77 u8 glasses_scale;
78 u8 glasses_y;
79 u8 mole_type;
80 u8 mole_scale;
81 u8 mole_x;
82 u8 mole_y;
83 u8 padding;
84
85 std::u16string Name() const;
86};
87static_assert(sizeof(MiiInfo) == 0x58, "MiiInfo has incorrect size.");
88static_assert(std::has_unique_object_representations_v<MiiInfo>,
89 "All bits of MiiInfo must contribute to its value.");
90
91#pragma pack(push, 4)
92
93struct MiiInfoElement {
94 MiiInfoElement(const MiiInfo& info_, Source source_) : info{info_}, source{source_} {}
95
96 MiiInfo info{};
97 Source source{};
98};
99static_assert(sizeof(MiiInfoElement) == 0x5c, "MiiInfoElement has incorrect size.");
100
101struct MiiStoreBitFields {
102 union {
103 u32 word_0{};
104
105 BitField<0, 8, u32> hair_type;
106 BitField<8, 7, u32> height;
107 BitField<15, 1, u32> mole_type;
108 BitField<16, 7, u32> build;
109 BitField<23, 1, HairFlip> hair_flip;
110 BitField<24, 7, u32> hair_color;
111 BitField<31, 1, u32> type;
112 };
113
114 union {
115 u32 word_1{};
116
117 BitField<0, 7, u32> eye_color;
118 BitField<7, 1, Gender> gender;
119 BitField<8, 7, u32> eyebrow_color;
120 BitField<16, 7, u32> mouth_color;
121 BitField<24, 7, u32> beard_color;
122 };
123
124 union {
125 u32 word_2{};
126
127 BitField<0, 7, u32> glasses_color;
128 BitField<8, 6, u32> eye_type;
129 BitField<14, 2, u32> region_move;
130 BitField<16, 6, u32> mouth_type;
131 BitField<22, 2, FontRegion> font_region;
132 BitField<24, 5, u32> eye_y;
133 BitField<29, 3, u32> glasses_scale;
134 };
135
136 union {
137 u32 word_3{};
138
139 BitField<0, 5, u32> eyebrow_type;
140 BitField<5, 3, MustacheType> mustache_type;
141 BitField<8, 5, u32> nose_type;
142 BitField<13, 3, BeardType> beard_type;
143 BitField<16, 5, u32> nose_y;
144 BitField<21, 3, u32> mouth_aspect;
145 BitField<24, 5, u32> mouth_y;
146 BitField<29, 3, u32> eyebrow_aspect;
147 };
148
149 union {
150 u32 word_4{};
151
152 BitField<0, 5, u32> mustache_y;
153 BitField<5, 3, u32> eye_rotate;
154 BitField<8, 5, u32> glasses_y;
155 BitField<13, 3, u32> eye_aspect;
156 BitField<16, 5, u32> mole_x;
157 BitField<21, 3, u32> eye_scale;
158 BitField<24, 5, u32> mole_y;
159 };
160
161 union {
162 u32 word_5{};
163
164 BitField<0, 5, u32> glasses_type;
165 BitField<8, 4, u32> favorite_color;
166 BitField<12, 4, u32> faceline_type;
167 BitField<16, 4, u32> faceline_color;
168 BitField<20, 4, u32> faceline_wrinkle;
169 BitField<24, 4, u32> faceline_makeup;
170 BitField<28, 4, u32> eye_x;
171 };
172
173 union {
174 u32 word_6{};
175
176 BitField<0, 4, u32> eyebrow_scale;
177 BitField<4, 4, u32> eyebrow_rotate;
178 BitField<8, 4, u32> eyebrow_x;
179 BitField<12, 4, u32> eyebrow_y;
180 BitField<16, 4, u32> nose_scale;
181 BitField<20, 4, u32> mouth_scale;
182 BitField<24, 4, u32> mustache_scale;
183 BitField<28, 4, u32> mole_scale;
184 };
185};
186static_assert(sizeof(MiiStoreBitFields) == 0x1c, "MiiStoreBitFields has incorrect size.");
187static_assert(std::is_trivially_copyable_v<MiiStoreBitFields>,
188 "MiiStoreBitFields is not trivially copyable.");
189
190struct MiiStoreData {
191 using Name = std::array<char16_t, 10>;
192
193 MiiStoreData();
194 MiiStoreData(const Name& name, const MiiStoreBitFields& bit_fields,
195 const Common::UUID& user_id);
196
197 // This corresponds to the above structure MiiStoreBitFields. I did it like this because the
198 // BitField<> type makes this (and any thing that contains it) not trivially copyable, which is
199 // not suitable for our uses.
200 struct {
201 std::array<u8, 0x1C> data{};
202 static_assert(sizeof(MiiStoreBitFields) == sizeof(data), "data field has incorrect size.");
203
204 Name name{};
205 Common::UUID uuid{};
206 } data;
207
208 u16 data_crc{};
209 u16 device_crc{};
210};
211static_assert(sizeof(MiiStoreData) == 0x44, "MiiStoreData has incorrect size.");
212
213struct MiiStoreDataElement {
214 MiiStoreData data{};
215 Source source{};
216};
217static_assert(sizeof(MiiStoreDataElement) == 0x48, "MiiStoreDataElement has incorrect size.");
218
219struct MiiDatabase {
220 u32 magic{}; // 'NFDB'
221 std::array<MiiStoreData, 0x64> miis{};
222 INSERT_PADDING_BYTES(1);
223 u8 count{};
224 u16 crc{};
225};
226static_assert(sizeof(MiiDatabase) == 0x1A98, "MiiDatabase has incorrect size.");
227
228struct RandomMiiValues {
229 std::array<u8, 0xbc> values{};
230};
231static_assert(sizeof(RandomMiiValues) == 0xbc, "RandomMiiValues has incorrect size.");
232
233struct RandomMiiData4 {
234 Gender gender{};
235 Age age{};
236 Race race{};
237 u32 values_count{};
238 std::array<u32, 47> values{};
239};
240static_assert(sizeof(RandomMiiData4) == 0xcc, "RandomMiiData4 has incorrect size.");
241
242struct RandomMiiData3 {
243 u32 arg_1;
244 u32 arg_2;
245 u32 values_count;
246 std::array<u32, 47> values{};
247};
248static_assert(sizeof(RandomMiiData3) == 0xc8, "RandomMiiData3 has incorrect size.");
249
250struct RandomMiiData2 {
251 u32 arg_1;
252 u32 values_count;
253 std::array<u32, 47> values{};
254};
255static_assert(sizeof(RandomMiiData2) == 0xc4, "RandomMiiData2 has incorrect size.");
256
257struct DefaultMii {
258 u32 face_type{};
259 u32 face_color{};
260 u32 face_wrinkle{};
261 u32 face_makeup{};
262 u32 hair_type{};
263 u32 hair_color{};
264 u32 hair_flip{};
265 u32 eye_type{};
266 u32 eye_color{};
267 u32 eye_scale{};
268 u32 eye_aspect{};
269 u32 eye_rotate{};
270 u32 eye_x{};
271 u32 eye_y{};
272 u32 eyebrow_type{};
273 u32 eyebrow_color{};
274 u32 eyebrow_scale{};
275 u32 eyebrow_aspect{};
276 u32 eyebrow_rotate{};
277 u32 eyebrow_x{};
278 u32 eyebrow_y{};
279 u32 nose_type{};
280 u32 nose_scale{};
281 u32 nose_y{};
282 u32 mouth_type{};
283 u32 mouth_color{};
284 u32 mouth_scale{};
285 u32 mouth_aspect{};
286 u32 mouth_y{};
287 u32 mustache_type{};
288 u32 beard_type{};
289 u32 beard_color{};
290 u32 mustache_scale{};
291 u32 mustache_y{};
292 u32 glasses_type{};
293 u32 glasses_color{};
294 u32 glasses_scale{};
295 u32 glasses_y{};
296 u32 mole_type{};
297 u32 mole_scale{};
298 u32 mole_x{};
299 u32 mole_y{};
300 u32 height{};
301 u32 weight{};
302 Gender gender{};
303 u32 favorite_color{};
304 u32 region{};
305 FontRegion font_region{};
306 u32 type{};
307 INSERT_PADDING_WORDS(5);
308};
309static_assert(sizeof(DefaultMii) == 0xd8, "MiiStoreData has incorrect size.");
310
311#pragma pack(pop)
312
313// The Mii manager is responsible for loading and storing the Miis to the database in NAND along 14// The Mii manager is responsible for loading and storing the Miis to the database in NAND along
314// with providing an easy interface for HLE emulation of the mii service. 15// with providing an easy interface for HLE emulation of the mii service.
315class MiiManager { 16class MiiManager {
diff --git a/src/core/hle/service/mii/raw_data.h b/src/core/hle/service/mii/raw_data.h
index bd90c2162..2e39c0d4f 100644
--- a/src/core/hle/service/mii/raw_data.h
+++ b/src/core/hle/service/mii/raw_data.h
@@ -6,7 +6,7 @@
6 6
7#include <array> 7#include <array>
8 8
9#include "core/hle/service/mii/mii_manager.h" 9#include "core/hle/service/mii/types.h"
10 10
11namespace Service::Mii::RawData { 11namespace Service::Mii::RawData {
12 12
diff --git a/src/core/hle/service/mii/types.h b/src/core/hle/service/mii/types.h
index d65a1055e..5580b8c6a 100644
--- a/src/core/hle/service/mii/types.h
+++ b/src/core/hle/service/mii/types.h
@@ -1,11 +1,16 @@
1// Copyright 2020 yuzu emulator team 1// Copyright 2020 yuzu Emulator Project
2// Licensed under GPLv2 or any later version 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#pragma once 5#pragma once
6 6
7#include <array>
8#include <type_traits>
9
10#include "common/bit_field.h"
7#include "common/common_funcs.h" 11#include "common/common_funcs.h"
8#include "common/common_types.h" 12#include "common/common_types.h"
13#include "common/uuid.h"
9 14
10namespace Service::Mii { 15namespace Service::Mii {
11 16
@@ -25,7 +30,11 @@ enum class BeardType : u32 {
25 Beard5, 30 Beard5,
26}; 31};
27 32
28enum class BeardAndMustacheFlag : u32 { Beard = 1, Mustache, All = Beard | Mustache }; 33enum class BeardAndMustacheFlag : u32 {
34 Beard = 1,
35 Mustache,
36 All = Beard | Mustache,
37};
29DECLARE_ENUM_FLAG_OPERATORS(BeardAndMustacheFlag); 38DECLARE_ENUM_FLAG_OPERATORS(BeardAndMustacheFlag);
30 39
31enum class FontRegion : u32 { 40enum class FontRegion : u32 {
@@ -64,4 +73,298 @@ enum class Race : u32 {
64 All, 73 All,
65}; 74};
66 75
76enum class Source : u32 {
77 Database = 0,
78 Default = 1,
79 Account = 2,
80 Friend = 3,
81};
82
83enum class SourceFlag : u32 {
84 None = 0,
85 Database = 1 << 0,
86 Default = 1 << 1,
87};
88DECLARE_ENUM_FLAG_OPERATORS(SourceFlag);
89
90struct MiiInfo {
91 Common::UUID uuid;
92 std::array<char16_t, 11> name;
93 u8 font_region;
94 u8 favorite_color;
95 u8 gender;
96 u8 height;
97 u8 build;
98 u8 type;
99 u8 region_move;
100 u8 faceline_type;
101 u8 faceline_color;
102 u8 faceline_wrinkle;
103 u8 faceline_make;
104 u8 hair_type;
105 u8 hair_color;
106 u8 hair_flip;
107 u8 eye_type;
108 u8 eye_color;
109 u8 eye_scale;
110 u8 eye_aspect;
111 u8 eye_rotate;
112 u8 eye_x;
113 u8 eye_y;
114 u8 eyebrow_type;
115 u8 eyebrow_color;
116 u8 eyebrow_scale;
117 u8 eyebrow_aspect;
118 u8 eyebrow_rotate;
119 u8 eyebrow_x;
120 u8 eyebrow_y;
121 u8 nose_type;
122 u8 nose_scale;
123 u8 nose_y;
124 u8 mouth_type;
125 u8 mouth_color;
126 u8 mouth_scale;
127 u8 mouth_aspect;
128 u8 mouth_y;
129 u8 beard_color;
130 u8 beard_type;
131 u8 mustache_type;
132 u8 mustache_scale;
133 u8 mustache_y;
134 u8 glasses_type;
135 u8 glasses_color;
136 u8 glasses_scale;
137 u8 glasses_y;
138 u8 mole_type;
139 u8 mole_scale;
140 u8 mole_x;
141 u8 mole_y;
142 u8 padding;
143};
144static_assert(sizeof(MiiInfo) == 0x58, "MiiInfo has incorrect size.");
145static_assert(std::has_unique_object_representations_v<MiiInfo>,
146 "All bits of MiiInfo must contribute to its value.");
147
148#pragma pack(push, 4)
149
150struct MiiInfoElement {
151 MiiInfoElement(const MiiInfo& info_, Source source_) : info{info_}, source{source_} {}
152
153 MiiInfo info{};
154 Source source{};
155};
156static_assert(sizeof(MiiInfoElement) == 0x5c, "MiiInfoElement has incorrect size.");
157
158struct MiiStoreBitFields {
159 union {
160 u32 word_0{};
161
162 BitField<0, 8, u32> hair_type;
163 BitField<8, 7, u32> height;
164 BitField<15, 1, u32> mole_type;
165 BitField<16, 7, u32> build;
166 BitField<23, 1, HairFlip> hair_flip;
167 BitField<24, 7, u32> hair_color;
168 BitField<31, 1, u32> type;
169 };
170
171 union {
172 u32 word_1{};
173
174 BitField<0, 7, u32> eye_color;
175 BitField<7, 1, Gender> gender;
176 BitField<8, 7, u32> eyebrow_color;
177 BitField<16, 7, u32> mouth_color;
178 BitField<24, 7, u32> beard_color;
179 };
180
181 union {
182 u32 word_2{};
183
184 BitField<0, 7, u32> glasses_color;
185 BitField<8, 6, u32> eye_type;
186 BitField<14, 2, u32> region_move;
187 BitField<16, 6, u32> mouth_type;
188 BitField<22, 2, FontRegion> font_region;
189 BitField<24, 5, u32> eye_y;
190 BitField<29, 3, u32> glasses_scale;
191 };
192
193 union {
194 u32 word_3{};
195
196 BitField<0, 5, u32> eyebrow_type;
197 BitField<5, 3, MustacheType> mustache_type;
198 BitField<8, 5, u32> nose_type;
199 BitField<13, 3, BeardType> beard_type;
200 BitField<16, 5, u32> nose_y;
201 BitField<21, 3, u32> mouth_aspect;
202 BitField<24, 5, u32> mouth_y;
203 BitField<29, 3, u32> eyebrow_aspect;
204 };
205
206 union {
207 u32 word_4{};
208
209 BitField<0, 5, u32> mustache_y;
210 BitField<5, 3, u32> eye_rotate;
211 BitField<8, 5, u32> glasses_y;
212 BitField<13, 3, u32> eye_aspect;
213 BitField<16, 5, u32> mole_x;
214 BitField<21, 3, u32> eye_scale;
215 BitField<24, 5, u32> mole_y;
216 };
217
218 union {
219 u32 word_5{};
220
221 BitField<0, 5, u32> glasses_type;
222 BitField<8, 4, u32> favorite_color;
223 BitField<12, 4, u32> faceline_type;
224 BitField<16, 4, u32> faceline_color;
225 BitField<20, 4, u32> faceline_wrinkle;
226 BitField<24, 4, u32> faceline_makeup;
227 BitField<28, 4, u32> eye_x;
228 };
229
230 union {
231 u32 word_6{};
232
233 BitField<0, 4, u32> eyebrow_scale;
234 BitField<4, 4, u32> eyebrow_rotate;
235 BitField<8, 4, u32> eyebrow_x;
236 BitField<12, 4, u32> eyebrow_y;
237 BitField<16, 4, u32> nose_scale;
238 BitField<20, 4, u32> mouth_scale;
239 BitField<24, 4, u32> mustache_scale;
240 BitField<28, 4, u32> mole_scale;
241 };
242};
243static_assert(sizeof(MiiStoreBitFields) == 0x1c, "MiiStoreBitFields has incorrect size.");
244static_assert(std::is_trivially_copyable_v<MiiStoreBitFields>,
245 "MiiStoreBitFields is not trivially copyable.");
246
247struct MiiStoreData {
248 using Name = std::array<char16_t, 10>;
249
250 MiiStoreData();
251 MiiStoreData(const Name& name, const MiiStoreBitFields& bit_fields,
252 const Common::UUID& user_id);
253
254 // This corresponds to the above structure MiiStoreBitFields. I did it like this because the
255 // BitField<> type makes this (and any thing that contains it) not trivially copyable, which is
256 // not suitable for our uses.
257 struct {
258 std::array<u8, 0x1C> data{};
259 static_assert(sizeof(MiiStoreBitFields) == sizeof(data), "data field has incorrect size.");
260
261 Name name{};
262 Common::UUID uuid{};
263 } data;
264
265 u16 data_crc{};
266 u16 device_crc{};
267};
268static_assert(sizeof(MiiStoreData) == 0x44, "MiiStoreData has incorrect size.");
269
270struct MiiStoreDataElement {
271 MiiStoreData data{};
272 Source source{};
273};
274static_assert(sizeof(MiiStoreDataElement) == 0x48, "MiiStoreDataElement has incorrect size.");
275
276struct MiiDatabase {
277 u32 magic{}; // 'NFDB'
278 std::array<MiiStoreData, 0x64> miis{};
279 INSERT_PADDING_BYTES(1);
280 u8 count{};
281 u16 crc{};
282};
283static_assert(sizeof(MiiDatabase) == 0x1A98, "MiiDatabase has incorrect size.");
284
285struct RandomMiiValues {
286 std::array<u8, 0xbc> values{};
287};
288static_assert(sizeof(RandomMiiValues) == 0xbc, "RandomMiiValues has incorrect size.");
289
290struct RandomMiiData4 {
291 Gender gender{};
292 Age age{};
293 Race race{};
294 u32 values_count{};
295 std::array<u32, 47> values{};
296};
297static_assert(sizeof(RandomMiiData4) == 0xcc, "RandomMiiData4 has incorrect size.");
298
299struct RandomMiiData3 {
300 u32 arg_1;
301 u32 arg_2;
302 u32 values_count;
303 std::array<u32, 47> values{};
304};
305static_assert(sizeof(RandomMiiData3) == 0xc8, "RandomMiiData3 has incorrect size.");
306
307struct RandomMiiData2 {
308 u32 arg_1;
309 u32 values_count;
310 std::array<u32, 47> values{};
311};
312static_assert(sizeof(RandomMiiData2) == 0xc4, "RandomMiiData2 has incorrect size.");
313
314struct DefaultMii {
315 u32 face_type{};
316 u32 face_color{};
317 u32 face_wrinkle{};
318 u32 face_makeup{};
319 u32 hair_type{};
320 u32 hair_color{};
321 u32 hair_flip{};
322 u32 eye_type{};
323 u32 eye_color{};
324 u32 eye_scale{};
325 u32 eye_aspect{};
326 u32 eye_rotate{};
327 u32 eye_x{};
328 u32 eye_y{};
329 u32 eyebrow_type{};
330 u32 eyebrow_color{};
331 u32 eyebrow_scale{};
332 u32 eyebrow_aspect{};
333 u32 eyebrow_rotate{};
334 u32 eyebrow_x{};
335 u32 eyebrow_y{};
336 u32 nose_type{};
337 u32 nose_scale{};
338 u32 nose_y{};
339 u32 mouth_type{};
340 u32 mouth_color{};
341 u32 mouth_scale{};
342 u32 mouth_aspect{};
343 u32 mouth_y{};
344 u32 mustache_type{};
345 u32 beard_type{};
346 u32 beard_color{};
347 u32 mustache_scale{};
348 u32 mustache_y{};
349 u32 glasses_type{};
350 u32 glasses_color{};
351 u32 glasses_scale{};
352 u32 glasses_y{};
353 u32 mole_type{};
354 u32 mole_scale{};
355 u32 mole_x{};
356 u32 mole_y{};
357 u32 height{};
358 u32 weight{};
359 Gender gender{};
360 u32 favorite_color{};
361 u32 region{};
362 FontRegion font_region{};
363 u32 type{};
364 INSERT_PADDING_WORDS(5);
365};
366static_assert(sizeof(DefaultMii) == 0xd8, "MiiStoreData has incorrect size.");
367
368#pragma pack(pop)
369
67} // namespace Service::Mii 370} // namespace Service::Mii
diff --git a/src/core/hle/service/nfp/nfp.cpp b/src/core/hle/service/nfp/nfp.cpp
index 513107715..dab99b675 100644
--- a/src/core/hle/service/nfp/nfp.cpp
+++ b/src/core/hle/service/nfp/nfp.cpp
@@ -12,6 +12,7 @@
12#include "core/hid/hid_types.h" 12#include "core/hid/hid_types.h"
13#include "core/hle/ipc_helpers.h" 13#include "core/hle/ipc_helpers.h"
14#include "core/hle/kernel/k_event.h" 14#include "core/hle/kernel/k_event.h"
15#include "core/hle/service/mii/mii_manager.h"
15#include "core/hle/service/nfp/nfp.h" 16#include "core/hle/service/nfp/nfp.h"
16#include "core/hle/service/nfp/nfp_user.h" 17#include "core/hle/service/nfp/nfp_user.h"
17 18
diff --git a/src/core/hle/service/nfp/nfp.h b/src/core/hle/service/nfp/nfp.h
index 022f13b29..ab652f635 100644
--- a/src/core/hle/service/nfp/nfp.h
+++ b/src/core/hle/service/nfp/nfp.h
@@ -9,7 +9,7 @@
9 9
10#include "common/common_funcs.h" 10#include "common/common_funcs.h"
11#include "core/hle/service/kernel_helpers.h" 11#include "core/hle/service/kernel_helpers.h"
12#include "core/hle/service/mii/mii_manager.h" 12#include "core/hle/service/mii/types.h"
13#include "core/hle/service/service.h" 13#include "core/hle/service/service.h"
14 14
15namespace Kernel { 15namespace Kernel {
diff --git a/src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp b/src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp
index 68f1e9060..9fad45fe1 100644
--- a/src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp
@@ -38,18 +38,16 @@ NvResult nvdisp_disp0::Ioctl3(DeviceFD fd, Ioctl command, const std::vector<u8>&
38void nvdisp_disp0::OnOpen(DeviceFD fd) {} 38void nvdisp_disp0::OnOpen(DeviceFD fd) {}
39void nvdisp_disp0::OnClose(DeviceFD fd) {} 39void nvdisp_disp0::OnClose(DeviceFD fd) {}
40 40
41void nvdisp_disp0::flip(u32 buffer_handle, u32 offset, u32 format, u32 width, u32 height, 41void nvdisp_disp0::flip(u32 buffer_handle, u32 offset, android::PixelFormat format, u32 width,
42 u32 stride, NVFlinger::BufferQueue::BufferTransformFlags transform, 42 u32 height, u32 stride, android::BufferTransformFlags transform,
43 const Common::Rectangle<int>& crop_rect) { 43 const Common::Rectangle<int>& crop_rect) {
44 const VAddr addr = nvmap_dev->GetObjectAddress(buffer_handle); 44 const VAddr addr = nvmap_dev->GetObjectAddress(buffer_handle);
45 LOG_TRACE(Service, 45 LOG_TRACE(Service,
46 "Drawing from address {:X} offset {:08X} Width {} Height {} Stride {} Format {}", 46 "Drawing from address {:X} offset {:08X} Width {} Height {} Stride {} Format {}",
47 addr, offset, width, height, stride, format); 47 addr, offset, width, height, stride, format);
48 48
49 const auto pixel_format = static_cast<Tegra::FramebufferConfig::PixelFormat>(format); 49 const Tegra::FramebufferConfig framebuffer{addr, offset, width, height,
50 const auto transform_flags = static_cast<Tegra::FramebufferConfig::TransformFlags>(transform); 50 stride, format, transform, crop_rect};
51 const Tegra::FramebufferConfig framebuffer{addr, offset, width, height,
52 stride, pixel_format, transform_flags, crop_rect};
53 51
54 system.GetPerfStats().EndSystemFrame(); 52 system.GetPerfStats().EndSystemFrame();
55 system.GPU().SwapBuffers(&framebuffer); 53 system.GPU().SwapBuffers(&framebuffer);
diff --git a/src/core/hle/service/nvdrv/devices/nvdisp_disp0.h b/src/core/hle/service/nvdrv/devices/nvdisp_disp0.h
index de01e1d5f..30b5da429 100644
--- a/src/core/hle/service/nvdrv/devices/nvdisp_disp0.h
+++ b/src/core/hle/service/nvdrv/devices/nvdisp_disp0.h
@@ -9,7 +9,8 @@
9#include "common/common_types.h" 9#include "common/common_types.h"
10#include "common/math_util.h" 10#include "common/math_util.h"
11#include "core/hle/service/nvdrv/devices/nvdevice.h" 11#include "core/hle/service/nvdrv/devices/nvdevice.h"
12#include "core/hle/service/nvflinger/buffer_queue.h" 12#include "core/hle/service/nvflinger/buffer_transform_flags.h"
13#include "core/hle/service/nvflinger/pixel_format.h"
13 14
14namespace Service::Nvidia::Devices { 15namespace Service::Nvidia::Devices {
15 16
@@ -31,8 +32,8 @@ public:
31 void OnClose(DeviceFD fd) override; 32 void OnClose(DeviceFD fd) override;
32 33
33 /// Performs a screen flip, drawing the buffer pointed to by the handle. 34 /// Performs a screen flip, drawing the buffer pointed to by the handle.
34 void flip(u32 buffer_handle, u32 offset, u32 format, u32 width, u32 height, u32 stride, 35 void flip(u32 buffer_handle, u32 offset, android::PixelFormat format, u32 width, u32 height,
35 NVFlinger::BufferQueue::BufferTransformFlags transform, 36 u32 stride, android::BufferTransformFlags transform,
36 const Common::Rectangle<int>& crop_rect); 37 const Common::Rectangle<int>& crop_rect);
37 38
38private: 39private:
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp b/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp
index f9b82b504..44c54c665 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp
@@ -134,7 +134,7 @@ NvResult nvhost_ctrl::IocCtrlEventWait(const std::vector<u8>& input, std::vector
134 } 134 }
135 135
136 EventState status = events_interface.status[event_id]; 136 EventState status = events_interface.status[event_id];
137 const bool bad_parameter = status != EventState::Free && status != EventState::Registered; 137 const bool bad_parameter = status == EventState::Busy;
138 if (bad_parameter) { 138 if (bad_parameter) {
139 std::memcpy(output.data(), &params, sizeof(params)); 139 std::memcpy(output.data(), &params, sizeof(params));
140 return NvResult::BadParameter; 140 return NvResult::BadParameter;
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp b/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp
index 0a043e386..dde5b1507 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp
@@ -187,7 +187,7 @@ NvResult nvhost_gpu::AllocateObjectContext(const std::vector<u8>& input, std::ve
187 return NvResult::Success; 187 return NvResult::Success;
188} 188}
189 189
190static std::vector<Tegra::CommandHeader> BuildWaitCommandList(Fence fence) { 190static std::vector<Tegra::CommandHeader> BuildWaitCommandList(NvFence fence) {
191 return { 191 return {
192 Tegra::BuildCommandHeader(Tegra::BufferMethods::FenceValue, 1, 192 Tegra::BuildCommandHeader(Tegra::BufferMethods::FenceValue, 1,
193 Tegra::SubmissionMode::Increasing), 193 Tegra::SubmissionMode::Increasing),
@@ -198,7 +198,8 @@ static std::vector<Tegra::CommandHeader> BuildWaitCommandList(Fence fence) {
198 }; 198 };
199} 199}
200 200
201static std::vector<Tegra::CommandHeader> BuildIncrementCommandList(Fence fence, u32 add_increment) { 201static std::vector<Tegra::CommandHeader> BuildIncrementCommandList(NvFence fence,
202 u32 add_increment) {
202 std::vector<Tegra::CommandHeader> result{ 203 std::vector<Tegra::CommandHeader> result{
203 Tegra::BuildCommandHeader(Tegra::BufferMethods::FenceValue, 1, 204 Tegra::BuildCommandHeader(Tegra::BufferMethods::FenceValue, 1,
204 Tegra::SubmissionMode::Increasing), 205 Tegra::SubmissionMode::Increasing),
@@ -213,7 +214,7 @@ static std::vector<Tegra::CommandHeader> BuildIncrementCommandList(Fence fence,
213 return result; 214 return result;
214} 215}
215 216
216static std::vector<Tegra::CommandHeader> BuildIncrementWithWfiCommandList(Fence fence, 217static std::vector<Tegra::CommandHeader> BuildIncrementWithWfiCommandList(NvFence fence,
217 u32 add_increment) { 218 u32 add_increment) {
218 std::vector<Tegra::CommandHeader> result{ 219 std::vector<Tegra::CommandHeader> result{
219 Tegra::BuildCommandHeader(Tegra::BufferMethods::WaitForInterrupt, 1, 220 Tegra::BuildCommandHeader(Tegra::BufferMethods::WaitForInterrupt, 1,
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_gpu.h b/src/core/hle/service/nvdrv/devices/nvhost_gpu.h
index 3e4f3b6a7..b2e943e45 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_gpu.h
+++ b/src/core/hle/service/nvdrv/devices/nvhost_gpu.h
@@ -109,7 +109,7 @@ private:
109 static_assert(sizeof(IoctlGetErrorNotification) == 16, 109 static_assert(sizeof(IoctlGetErrorNotification) == 16,
110 "IoctlGetErrorNotification is incorrect size"); 110 "IoctlGetErrorNotification is incorrect size");
111 111
112 static_assert(sizeof(Fence) == 8, "Fence is incorrect size"); 112 static_assert(sizeof(NvFence) == 8, "Fence is incorrect size");
113 113
114 struct IoctlAllocGpfifoEx { 114 struct IoctlAllocGpfifoEx {
115 u32_le num_entries{}; 115 u32_le num_entries{};
@@ -127,7 +127,7 @@ private:
127 u32_le num_entries{}; // in 127 u32_le num_entries{}; // in
128 u32_le flags{}; // in 128 u32_le flags{}; // in
129 u32_le unk0{}; // in (1 works) 129 u32_le unk0{}; // in (1 works)
130 Fence fence_out{}; // out 130 NvFence fence_out{}; // out
131 u32_le unk1{}; // in 131 u32_le unk1{}; // in
132 u32_le unk2{}; // in 132 u32_le unk2{}; // in
133 u32_le unk3{}; // in 133 u32_le unk3{}; // in
@@ -153,13 +153,13 @@ private:
153 BitField<4, 1, u32_le> suppress_wfi; // suppress wait for interrupt 153 BitField<4, 1, u32_le> suppress_wfi; // suppress wait for interrupt
154 BitField<8, 1, u32_le> increment; // increment the returned fence 154 BitField<8, 1, u32_le> increment; // increment the returned fence
155 } flags; 155 } flags;
156 Fence fence_out{}; // returned new fence object for others to wait on 156 NvFence fence_out{}; // returned new fence object for others to wait on
157 157
158 u32 AddIncrementValue() const { 158 u32 AddIncrementValue() const {
159 return flags.add_increment.Value() << 1; 159 return flags.add_increment.Value() << 1;
160 } 160 }
161 }; 161 };
162 static_assert(sizeof(IoctlSubmitGpfifo) == 16 + sizeof(Fence), 162 static_assert(sizeof(IoctlSubmitGpfifo) == 16 + sizeof(NvFence),
163 "IoctlSubmitGpfifo is incorrect size"); 163 "IoctlSubmitGpfifo is incorrect size");
164 164
165 struct IoctlGetWaitbase { 165 struct IoctlGetWaitbase {
@@ -194,7 +194,7 @@ private:
194 194
195 std::shared_ptr<nvmap> nvmap_dev; 195 std::shared_ptr<nvmap> nvmap_dev;
196 SyncpointManager& syncpoint_manager; 196 SyncpointManager& syncpoint_manager;
197 Fence channel_fence; 197 NvFence channel_fence;
198}; 198};
199 199
200} // namespace Service::Nvidia::Devices 200} // namespace Service::Nvidia::Devices
diff --git a/src/core/hle/service/nvdrv/nvdata.h b/src/core/hle/service/nvdrv/nvdata.h
index 5ab221fc1..3069c3c80 100644
--- a/src/core/hle/service/nvdrv/nvdata.h
+++ b/src/core/hle/service/nvdrv/nvdata.h
@@ -16,17 +16,11 @@ using DeviceFD = s32;
16 16
17constexpr DeviceFD INVALID_NVDRV_FD = -1; 17constexpr DeviceFD INVALID_NVDRV_FD = -1;
18 18
19struct Fence { 19struct NvFence {
20 s32 id; 20 s32 id;
21 u32 value; 21 u32 value;
22}; 22};
23 23static_assert(sizeof(NvFence) == 8, "NvFence has wrong size");
24static_assert(sizeof(Fence) == 8, "Fence has wrong size");
25
26struct MultiFence {
27 u32 num_fences;
28 std::array<Fence, 4> fences;
29};
30 24
31enum class NvResult : u32 { 25enum class NvResult : u32 {
32 Success = 0x0, 26 Success = 0x0,
diff --git a/src/core/hle/service/nvdrv/nvdrv.h b/src/core/hle/service/nvdrv/nvdrv.h
index a5af5b785..11cf6c0d1 100644
--- a/src/core/hle/service/nvdrv/nvdrv.h
+++ b/src/core/hle/service/nvdrv/nvdrv.h
@@ -12,6 +12,7 @@
12#include "core/hle/service/kernel_helpers.h" 12#include "core/hle/service/kernel_helpers.h"
13#include "core/hle/service/nvdrv/nvdata.h" 13#include "core/hle/service/nvdrv/nvdata.h"
14#include "core/hle/service/nvdrv/syncpoint_manager.h" 14#include "core/hle/service/nvdrv/syncpoint_manager.h"
15#include "core/hle/service/nvflinger/ui/fence.h"
15#include "core/hle/service/service.h" 16#include "core/hle/service/service.h"
16 17
17namespace Core { 18namespace Core {
@@ -37,7 +38,7 @@ class nvdevice;
37/// Represents an Nvidia event 38/// Represents an Nvidia event
38struct NvEvent { 39struct NvEvent {
39 Kernel::KEvent* event{}; 40 Kernel::KEvent* event{};
40 Fence fence{}; 41 NvFence fence{};
41}; 42};
42 43
43struct EventInterface { 44struct EventInterface {
diff --git a/src/core/hle/service/nvdrv/nvdrv_interface.cpp b/src/core/hle/service/nvdrv/nvdrv_interface.cpp
index c16babe14..8467b50e4 100644
--- a/src/core/hle/service/nvdrv/nvdrv_interface.cpp
+++ b/src/core/hle/service/nvdrv/nvdrv_interface.cpp
@@ -26,7 +26,7 @@ void NVDRV::Open(Kernel::HLERequestContext& ctx) {
26 rb.Push<DeviceFD>(0); 26 rb.Push<DeviceFD>(0);
27 rb.PushEnum(NvResult::NotInitialized); 27 rb.PushEnum(NvResult::NotInitialized);
28 28
29 LOG_ERROR(Service_NVDRV, "NvServices is not initalized!"); 29 LOG_ERROR(Service_NVDRV, "NvServices is not initialized!");
30 return; 30 return;
31 } 31 }
32 32
@@ -61,7 +61,7 @@ void NVDRV::Ioctl1(Kernel::HLERequestContext& ctx) {
61 61
62 if (!is_initialized) { 62 if (!is_initialized) {
63 ServiceError(ctx, NvResult::NotInitialized); 63 ServiceError(ctx, NvResult::NotInitialized);
64 LOG_ERROR(Service_NVDRV, "NvServices is not initalized!"); 64 LOG_ERROR(Service_NVDRV, "NvServices is not initialized!");
65 return; 65 return;
66 } 66 }
67 67
@@ -87,7 +87,7 @@ void NVDRV::Ioctl2(Kernel::HLERequestContext& ctx) {
87 87
88 if (!is_initialized) { 88 if (!is_initialized) {
89 ServiceError(ctx, NvResult::NotInitialized); 89 ServiceError(ctx, NvResult::NotInitialized);
90 LOG_ERROR(Service_NVDRV, "NvServices is not initalized!"); 90 LOG_ERROR(Service_NVDRV, "NvServices is not initialized!");
91 return; 91 return;
92 } 92 }
93 93
@@ -114,7 +114,7 @@ void NVDRV::Ioctl3(Kernel::HLERequestContext& ctx) {
114 114
115 if (!is_initialized) { 115 if (!is_initialized) {
116 ServiceError(ctx, NvResult::NotInitialized); 116 ServiceError(ctx, NvResult::NotInitialized);
117 LOG_ERROR(Service_NVDRV, "NvServices is not initalized!"); 117 LOG_ERROR(Service_NVDRV, "NvServices is not initialized!");
118 return; 118 return;
119 } 119 }
120 120
@@ -139,7 +139,7 @@ void NVDRV::Close(Kernel::HLERequestContext& ctx) {
139 139
140 if (!is_initialized) { 140 if (!is_initialized) {
141 ServiceError(ctx, NvResult::NotInitialized); 141 ServiceError(ctx, NvResult::NotInitialized);
142 LOG_ERROR(Service_NVDRV, "NvServices is not initalized!"); 142 LOG_ERROR(Service_NVDRV, "NvServices is not initialized!");
143 return; 143 return;
144 } 144 }
145 145
@@ -170,7 +170,7 @@ void NVDRV::QueryEvent(Kernel::HLERequestContext& ctx) {
170 170
171 if (!is_initialized) { 171 if (!is_initialized) {
172 ServiceError(ctx, NvResult::NotInitialized); 172 ServiceError(ctx, NvResult::NotInitialized);
173 LOG_ERROR(Service_NVDRV, "NvServices is not initalized!"); 173 LOG_ERROR(Service_NVDRV, "NvServices is not initialized!");
174 return; 174 return;
175 } 175 }
176 176
@@ -230,7 +230,7 @@ void NVDRV::DumpGraphicsMemoryInfo(Kernel::HLERequestContext& ctx) {
230} 230}
231 231
232NVDRV::NVDRV(Core::System& system_, std::shared_ptr<Module> nvdrv_, const char* name) 232NVDRV::NVDRV(Core::System& system_, std::shared_ptr<Module> nvdrv_, const char* name)
233 : ServiceFramework{system_, name}, nvdrv{std::move(nvdrv_)} { 233 : ServiceFramework{system_, name, ServiceThreadType::CreateNew}, nvdrv{std::move(nvdrv_)} {
234 static const FunctionInfo functions[] = { 234 static const FunctionInfo functions[] = {
235 {0, &NVDRV::Open, "Open"}, 235 {0, &NVDRV::Open, "Open"},
236 {1, &NVDRV::Ioctl1, "Ioctl"}, 236 {1, &NVDRV::Ioctl1, "Ioctl"},
diff --git a/src/core/hle/service/nvflinger/binder.h b/src/core/hle/service/nvflinger/binder.h
new file mode 100644
index 000000000..7d0d4d819
--- /dev/null
+++ b/src/core/hle/service/nvflinger/binder.h
@@ -0,0 +1,42 @@
1// SPDX-License-Identifier: GPL-3.0-or-later
2// Copyright 2021 yuzu Emulator Project
3// Copyright 2014 The Android Open Source Project
4// Parts of this implementation were base on:
5// https://cs.android.com/android/platform/superproject/+/android-5.1.1_r38:frameworks/native/include/binder/IBinder.h
6
7#pragma once
8
9#include "common/common_types.h"
10
11namespace Kernel {
12class HLERequestContext;
13class KReadableEvent;
14} // namespace Kernel
15
16namespace Service::android {
17
18enum class TransactionId {
19 RequestBuffer = 1,
20 SetBufferCount = 2,
21 DequeueBuffer = 3,
22 DetachBuffer = 4,
23 DetachNextBuffer = 5,
24 AttachBuffer = 6,
25 QueueBuffer = 7,
26 CancelBuffer = 8,
27 Query = 9,
28 Connect = 10,
29 Disconnect = 11,
30 AllocateBuffers = 13,
31 SetPreallocatedBuffer = 14,
32 GetBufferHistory = 17,
33};
34
35class IBinder {
36public:
37 virtual void Transact(Kernel::HLERequestContext& ctx, android::TransactionId code,
38 u32 flags) = 0;
39 virtual Kernel::KReadableEvent& GetNativeHandle() = 0;
40};
41
42} // namespace Service::android
diff --git a/src/core/hle/service/nvflinger/buffer_item.h b/src/core/hle/service/nvflinger/buffer_item.h
new file mode 100644
index 000000000..64b82b851
--- /dev/null
+++ b/src/core/hle/service/nvflinger/buffer_item.h
@@ -0,0 +1,46 @@
1// SPDX-License-Identifier: GPL-3.0-or-later
2// Copyright 2021 yuzu Emulator Project
3// Copyright 2014 The Android Open Source Project
4// Parts of this implementation were base on:
5// https://cs.android.com/android/platform/superproject/+/android-5.1.1_r38:frameworks/native/include/gui/BufferItem.h
6
7#pragma once
8
9#include <memory>
10
11#include "common/common_types.h"
12#include "common/math_util.h"
13#include "core/hle/service/nvflinger/ui/fence.h"
14#include "core/hle/service/nvflinger/window.h"
15
16namespace Service::android {
17
18class GraphicBuffer;
19
20class BufferItem final {
21public:
22 constexpr BufferItem() = default;
23
24 std::shared_ptr<GraphicBuffer> graphic_buffer;
25 Fence fence;
26 Common::Rectangle<s32> crop;
27 NativeWindowTransform transform{};
28 u32 scaling_mode{};
29 s64 timestamp{};
30 bool is_auto_timestamp{};
31 u64 frame_number{};
32
33 // The default value for buf, used to indicate this doesn't correspond to a slot.
34 static constexpr s32 INVALID_BUFFER_SLOT = -1;
35 union {
36 s32 slot{INVALID_BUFFER_SLOT};
37 s32 buf;
38 };
39
40 bool is_droppable{};
41 bool acquire_called{};
42 bool transform_to_display_inverse{};
43 s32 swap_interval{};
44};
45
46} // namespace Service::android
diff --git a/src/core/hle/service/nvflinger/buffer_item_consumer.cpp b/src/core/hle/service/nvflinger/buffer_item_consumer.cpp
new file mode 100644
index 000000000..d7ee5362b
--- /dev/null
+++ b/src/core/hle/service/nvflinger/buffer_item_consumer.cpp
@@ -0,0 +1,59 @@
1// SPDX-License-Identifier: GPL-3.0-or-later
2// Copyright 2021 yuzu Emulator Project
3// Copyright 2012 The Android Open Source Project
4// Parts of this implementation were base on:
5// https://cs.android.com/android/platform/superproject/+/android-5.1.1_r38:frameworks/native/libs/gui/BufferItemConsumer.cpp
6
7#include "common/assert.h"
8#include "common/logging/log.h"
9#include "core/hle/service/nvflinger/buffer_item.h"
10#include "core/hle/service/nvflinger/buffer_item_consumer.h"
11#include "core/hle/service/nvflinger/buffer_queue_consumer.h"
12
13namespace Service::android {
14
15BufferItemConsumer::BufferItemConsumer(std::unique_ptr<BufferQueueConsumer> consumer_)
16 : ConsumerBase{std::move(consumer_)} {}
17
18Status BufferItemConsumer::AcquireBuffer(BufferItem* item, std::chrono::nanoseconds present_when,
19 bool wait_for_fence) {
20 if (!item) {
21 return Status::BadValue;
22 }
23
24 std::scoped_lock lock{mutex};
25
26 if (const auto status = AcquireBufferLocked(item, present_when); status != Status::NoError) {
27 if (status != Status::NoBufferAvailable) {
28 LOG_ERROR(Service_NVFlinger, "Failed to acquire buffer: {}", status);
29 }
30 return status;
31 }
32
33 if (wait_for_fence) {
34 UNIMPLEMENTED();
35 }
36
37 item->graphic_buffer = slots[item->slot].graphic_buffer;
38
39 return Status::NoError;
40}
41
42Status BufferItemConsumer::ReleaseBuffer(const BufferItem& item, Fence& release_fence) {
43 std::scoped_lock lock{mutex};
44
45 if (const auto status = AddReleaseFenceLocked(item.buf, item.graphic_buffer, release_fence);
46 status != Status::NoError) {
47 LOG_ERROR(Service_NVFlinger, "Failed to add fence: {}", status);
48 }
49
50 if (const auto status = ReleaseBufferLocked(item.buf, item.graphic_buffer);
51 status != Status::NoError) {
52 LOG_WARNING(Service_NVFlinger, "Failed to release buffer: {}", status);
53 return status;
54 }
55
56 return Status::NoError;
57}
58
59} // namespace Service::android
diff --git a/src/core/hle/service/nvflinger/buffer_item_consumer.h b/src/core/hle/service/nvflinger/buffer_item_consumer.h
new file mode 100644
index 000000000..536db81e2
--- /dev/null
+++ b/src/core/hle/service/nvflinger/buffer_item_consumer.h
@@ -0,0 +1,28 @@
1// SPDX-License-Identifier: GPL-3.0-or-later
2// Copyright 2021 yuzu Emulator Project
3// Copyright 2012 The Android Open Source Project
4// Parts of this implementation were base on:
5// https://cs.android.com/android/platform/superproject/+/android-5.1.1_r38:frameworks/native/include/gui/BufferItemConsumer.h
6
7#pragma once
8
9#include <chrono>
10#include <memory>
11
12#include "common/common_types.h"
13#include "core/hle/service/nvflinger/consumer_base.h"
14#include "core/hle/service/nvflinger/status.h"
15
16namespace Service::android {
17
18class BufferItem;
19
20class BufferItemConsumer final : public ConsumerBase {
21public:
22 explicit BufferItemConsumer(std::unique_ptr<BufferQueueConsumer> consumer);
23 Status AcquireBuffer(BufferItem* item, std::chrono::nanoseconds present_when,
24 bool wait_for_fence = true);
25 Status ReleaseBuffer(const BufferItem& item, Fence& release_fence);
26};
27
28} // namespace Service::android
diff --git a/src/core/hle/service/nvflinger/buffer_queue.cpp b/src/core/hle/service/nvflinger/buffer_queue.cpp
deleted file mode 100644
index 5fead6d1b..000000000
--- a/src/core/hle/service/nvflinger/buffer_queue.cpp
+++ /dev/null
@@ -1,206 +0,0 @@
1// Copyright 2018 yuzu emulator team
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <algorithm>
6
7#include "common/assert.h"
8#include "common/logging/log.h"
9#include "core/core.h"
10#include "core/hle/kernel/k_writable_event.h"
11#include "core/hle/kernel/kernel.h"
12#include "core/hle/service/kernel_helpers.h"
13#include "core/hle/service/nvflinger/buffer_queue.h"
14
15namespace Service::NVFlinger {
16
17BufferQueue::BufferQueue(Kernel::KernelCore& kernel, u32 id_, u64 layer_id_,
18 KernelHelpers::ServiceContext& service_context_)
19 : id(id_), layer_id(layer_id_), service_context{service_context_} {
20 buffer_wait_event = service_context.CreateEvent("BufferQueue:WaitEvent");
21}
22
23BufferQueue::~BufferQueue() {
24 service_context.CloseEvent(buffer_wait_event);
25}
26
27void BufferQueue::SetPreallocatedBuffer(u32 slot, const IGBPBuffer& igbp_buffer) {
28 ASSERT(slot < buffer_slots);
29 LOG_WARNING(Service, "Adding graphics buffer {}", slot);
30
31 {
32 std::unique_lock lock{free_buffers_mutex};
33 free_buffers.push_back(slot);
34 }
35 free_buffers_condition.notify_one();
36
37 buffers[slot] = {
38 .slot = slot,
39 .status = Buffer::Status::Free,
40 .igbp_buffer = igbp_buffer,
41 .transform = {},
42 .crop_rect = {},
43 .swap_interval = 0,
44 .multi_fence = {},
45 };
46
47 buffer_wait_event->GetWritableEvent().Signal();
48}
49
50std::optional<std::pair<u32, Service::Nvidia::MultiFence*>> BufferQueue::DequeueBuffer(u32 width,
51 u32 height) {
52 // Wait for first request before trying to dequeue
53 {
54 std::unique_lock lock{free_buffers_mutex};
55 free_buffers_condition.wait(lock, [this] { return !free_buffers.empty() || !is_connect; });
56 }
57
58 if (!is_connect) {
59 // Buffer was disconnected while the thread was blocked, this is most likely due to
60 // emulation being stopped
61 return std::nullopt;
62 }
63
64 std::unique_lock lock{free_buffers_mutex};
65
66 auto f_itr = free_buffers.begin();
67 auto slot = buffers.size();
68
69 while (f_itr != free_buffers.end()) {
70 const Buffer& buffer = buffers[*f_itr];
71 if (buffer.status == Buffer::Status::Free && buffer.igbp_buffer.width == width &&
72 buffer.igbp_buffer.height == height) {
73 slot = *f_itr;
74 free_buffers.erase(f_itr);
75 break;
76 }
77 ++f_itr;
78 }
79 if (slot == buffers.size()) {
80 return std::nullopt;
81 }
82 buffers[slot].status = Buffer::Status::Dequeued;
83 return {{buffers[slot].slot, &buffers[slot].multi_fence}};
84}
85
86const IGBPBuffer& BufferQueue::RequestBuffer(u32 slot) const {
87 ASSERT(slot < buffers.size());
88 ASSERT(buffers[slot].status == Buffer::Status::Dequeued);
89 ASSERT(buffers[slot].slot == slot);
90
91 return buffers[slot].igbp_buffer;
92}
93
94void BufferQueue::QueueBuffer(u32 slot, BufferTransformFlags transform,
95 const Common::Rectangle<int>& crop_rect, u32 swap_interval,
96 Service::Nvidia::MultiFence& multi_fence) {
97 ASSERT(slot < buffers.size());
98 ASSERT(buffers[slot].status == Buffer::Status::Dequeued);
99 ASSERT(buffers[slot].slot == slot);
100
101 buffers[slot].status = Buffer::Status::Queued;
102 buffers[slot].transform = transform;
103 buffers[slot].crop_rect = crop_rect;
104 buffers[slot].swap_interval = swap_interval;
105 buffers[slot].multi_fence = multi_fence;
106 std::unique_lock lock{queue_sequence_mutex};
107 queue_sequence.push_back(slot);
108}
109
110void BufferQueue::CancelBuffer(u32 slot, const Service::Nvidia::MultiFence& multi_fence) {
111 ASSERT(slot < buffers.size());
112 ASSERT(buffers[slot].status != Buffer::Status::Free);
113 ASSERT(buffers[slot].slot == slot);
114
115 buffers[slot].status = Buffer::Status::Free;
116 buffers[slot].multi_fence = multi_fence;
117 buffers[slot].swap_interval = 0;
118
119 {
120 std::unique_lock lock{free_buffers_mutex};
121 free_buffers.push_back(slot);
122 }
123 free_buffers_condition.notify_one();
124
125 buffer_wait_event->GetWritableEvent().Signal();
126}
127
128std::optional<std::reference_wrapper<const BufferQueue::Buffer>> BufferQueue::AcquireBuffer() {
129 std::unique_lock lock{queue_sequence_mutex};
130 std::size_t buffer_slot = buffers.size();
131 // Iterate to find a queued buffer matching the requested slot.
132 while (buffer_slot == buffers.size() && !queue_sequence.empty()) {
133 const auto slot = static_cast<std::size_t>(queue_sequence.front());
134 ASSERT(slot < buffers.size());
135 if (buffers[slot].status == Buffer::Status::Queued) {
136 ASSERT(buffers[slot].slot == slot);
137 buffer_slot = slot;
138 }
139 queue_sequence.pop_front();
140 }
141 if (buffer_slot == buffers.size()) {
142 return std::nullopt;
143 }
144 buffers[buffer_slot].status = Buffer::Status::Acquired;
145 return {{buffers[buffer_slot]}};
146}
147
148void BufferQueue::ReleaseBuffer(u32 slot) {
149 ASSERT(slot < buffers.size());
150 ASSERT(buffers[slot].status == Buffer::Status::Acquired);
151 ASSERT(buffers[slot].slot == slot);
152
153 buffers[slot].status = Buffer::Status::Free;
154 {
155 std::unique_lock lock{free_buffers_mutex};
156 free_buffers.push_back(slot);
157 }
158 free_buffers_condition.notify_one();
159
160 buffer_wait_event->GetWritableEvent().Signal();
161}
162
163void BufferQueue::Connect() {
164 std::unique_lock lock{queue_sequence_mutex};
165 queue_sequence.clear();
166 is_connect = true;
167}
168
169void BufferQueue::Disconnect() {
170 buffers.fill({});
171 {
172 std::unique_lock lock{queue_sequence_mutex};
173 queue_sequence.clear();
174 }
175 buffer_wait_event->GetWritableEvent().Signal();
176 is_connect = false;
177 free_buffers_condition.notify_one();
178}
179
180u32 BufferQueue::Query(QueryType type) {
181 LOG_WARNING(Service, "(STUBBED) called type={}", type);
182
183 switch (type) {
184 case QueryType::NativeWindowFormat:
185 return static_cast<u32>(PixelFormat::RGBA8888);
186 case QueryType::NativeWindowWidth:
187 case QueryType::NativeWindowHeight:
188 break;
189 case QueryType::NativeWindowMinUndequeuedBuffers:
190 return 0;
191 case QueryType::NativeWindowConsumerUsageBits:
192 return 0;
193 }
194 UNIMPLEMENTED_MSG("Unimplemented query type={}", type);
195 return 0;
196}
197
198Kernel::KWritableEvent& BufferQueue::GetWritableBufferWaitEvent() {
199 return buffer_wait_event->GetWritableEvent();
200}
201
202Kernel::KReadableEvent& BufferQueue::GetBufferWaitEvent() {
203 return buffer_wait_event->GetReadableEvent();
204}
205
206} // namespace Service::NVFlinger
diff --git a/src/core/hle/service/nvflinger/buffer_queue.h b/src/core/hle/service/nvflinger/buffer_queue.h
deleted file mode 100644
index f2a579133..000000000
--- a/src/core/hle/service/nvflinger/buffer_queue.h
+++ /dev/null
@@ -1,154 +0,0 @@
1// Copyright 2018 yuzu emulator team
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <condition_variable>
8#include <list>
9#include <mutex>
10#include <optional>
11
12#include "common/common_funcs.h"
13#include "common/math_util.h"
14#include "common/swap.h"
15#include "core/hle/kernel/k_event.h"
16#include "core/hle/kernel/k_readable_event.h"
17#include "core/hle/service/nvdrv/nvdata.h"
18
19namespace Kernel {
20class KernelCore;
21class KEvent;
22class KReadableEvent;
23class KWritableEvent;
24} // namespace Kernel
25
26namespace Service::KernelHelpers {
27class ServiceContext;
28} // namespace Service::KernelHelpers
29
30namespace Service::NVFlinger {
31
32constexpr u32 buffer_slots = 0x40;
33struct IGBPBuffer {
34 u32_le magic;
35 u32_le width;
36 u32_le height;
37 u32_le stride;
38 u32_le format;
39 u32_le usage;
40 INSERT_PADDING_WORDS(1);
41 u32_le index;
42 INSERT_PADDING_WORDS(3);
43 u32_le gpu_buffer_id;
44 INSERT_PADDING_WORDS(6);
45 u32_le external_format;
46 INSERT_PADDING_WORDS(10);
47 u32_le nvmap_handle;
48 u32_le offset;
49 INSERT_PADDING_WORDS(60);
50};
51
52static_assert(sizeof(IGBPBuffer) == 0x16C, "IGBPBuffer has wrong size");
53
54class BufferQueue final {
55public:
56 enum class QueryType {
57 NativeWindowWidth = 0,
58 NativeWindowHeight = 1,
59 NativeWindowFormat = 2,
60 /// The minimum number of buffers that must remain un-dequeued after a buffer has been
61 /// queued
62 NativeWindowMinUndequeuedBuffers = 3,
63 /// The consumer gralloc usage bits currently set by the consumer
64 NativeWindowConsumerUsageBits = 10,
65 };
66
67 explicit BufferQueue(Kernel::KernelCore& kernel, u32 id_, u64 layer_id_,
68 KernelHelpers::ServiceContext& service_context_);
69 ~BufferQueue();
70
71 enum class BufferTransformFlags : u32 {
72 /// No transform flags are set
73 Unset = 0x00,
74 /// Flip source image horizontally (around the vertical axis)
75 FlipH = 0x01,
76 /// Flip source image vertically (around the horizontal axis)
77 FlipV = 0x02,
78 /// Rotate source image 90 degrees clockwise
79 Rotate90 = 0x04,
80 /// Rotate source image 180 degrees
81 Rotate180 = 0x03,
82 /// Rotate source image 270 degrees clockwise
83 Rotate270 = 0x07,
84 };
85
86 enum class PixelFormat : u32 {
87 RGBA8888 = 1,
88 RGBX8888 = 2,
89 RGB888 = 3,
90 RGB565 = 4,
91 BGRA8888 = 5,
92 RGBA5551 = 6,
93 RRGBA4444 = 7,
94 };
95
96 struct Buffer {
97 enum class Status { Free = 0, Queued = 1, Dequeued = 2, Acquired = 3 };
98
99 u32 slot;
100 Status status = Status::Free;
101 IGBPBuffer igbp_buffer;
102 BufferTransformFlags transform;
103 Common::Rectangle<int> crop_rect;
104 u32 swap_interval;
105 Service::Nvidia::MultiFence multi_fence;
106 };
107
108 void SetPreallocatedBuffer(u32 slot, const IGBPBuffer& igbp_buffer);
109 std::optional<std::pair<u32, Service::Nvidia::MultiFence*>> DequeueBuffer(u32 width,
110 u32 height);
111 const IGBPBuffer& RequestBuffer(u32 slot) const;
112 void QueueBuffer(u32 slot, BufferTransformFlags transform,
113 const Common::Rectangle<int>& crop_rect, u32 swap_interval,
114 Service::Nvidia::MultiFence& multi_fence);
115 void CancelBuffer(u32 slot, const Service::Nvidia::MultiFence& multi_fence);
116 std::optional<std::reference_wrapper<const Buffer>> AcquireBuffer();
117 void ReleaseBuffer(u32 slot);
118 void Connect();
119 void Disconnect();
120 u32 Query(QueryType type);
121
122 u32 GetId() const {
123 return id;
124 }
125
126 bool IsConnected() const {
127 return is_connect;
128 }
129
130 Kernel::KWritableEvent& GetWritableBufferWaitEvent();
131
132 Kernel::KReadableEvent& GetBufferWaitEvent();
133
134private:
135 BufferQueue(const BufferQueue&) = delete;
136
137 u32 id{};
138 u64 layer_id{};
139 std::atomic_bool is_connect{};
140
141 std::list<u32> free_buffers;
142 std::array<Buffer, buffer_slots> buffers;
143 std::list<u32> queue_sequence;
144 Kernel::KEvent* buffer_wait_event{};
145
146 std::mutex free_buffers_mutex;
147 std::condition_variable free_buffers_condition;
148
149 std::mutex queue_sequence_mutex;
150
151 KernelHelpers::ServiceContext& service_context;
152};
153
154} // namespace Service::NVFlinger
diff --git a/src/core/hle/service/nvflinger/buffer_queue_consumer.cpp b/src/core/hle/service/nvflinger/buffer_queue_consumer.cpp
new file mode 100644
index 000000000..3ab9a8c05
--- /dev/null
+++ b/src/core/hle/service/nvflinger/buffer_queue_consumer.cpp
@@ -0,0 +1,231 @@
1// SPDX-License-Identifier: GPL-3.0-or-later
2// Copyright 2021 yuzu Emulator Project
3// Copyright 2014 The Android Open Source Project
4// Parts of this implementation were base on:
5// https://cs.android.com/android/platform/superproject/+/android-5.1.1_r38:frameworks/native/libs/gui/BufferQueueConsumer.cpp
6
7#include "common/logging/log.h"
8#include "core/hle/service/nvflinger/buffer_item.h"
9#include "core/hle/service/nvflinger/buffer_queue_consumer.h"
10#include "core/hle/service/nvflinger/buffer_queue_core.h"
11#include "core/hle/service/nvflinger/producer_listener.h"
12
13namespace Service::android {
14
15BufferQueueConsumer::BufferQueueConsumer(std::shared_ptr<BufferQueueCore> core_)
16 : core{std::move(core_)}, slots{core->slots} {}
17
18BufferQueueConsumer::~BufferQueueConsumer() = default;
19
20Status BufferQueueConsumer::AcquireBuffer(BufferItem* out_buffer,
21 std::chrono::nanoseconds expected_present) {
22 std::scoped_lock lock{core->mutex};
23
24 // Check that the consumer doesn't currently have the maximum number of buffers acquired.
25 const s32 num_acquired_buffers{
26 static_cast<s32>(std::count_if(slots.begin(), slots.end(), [](const auto& slot) {
27 return slot.buffer_state == BufferState::Acquired;
28 }))};
29
30 if (num_acquired_buffers >= core->max_acquired_buffer_count + 1) {
31 LOG_ERROR(Service_NVFlinger, "max acquired buffer count reached: {} (max {})",
32 num_acquired_buffers, core->max_acquired_buffer_count);
33 return Status::InvalidOperation;
34 }
35
36 // Check if the queue is empty.
37 if (core->queue.empty()) {
38 return Status::NoBufferAvailable;
39 }
40
41 auto front(core->queue.begin());
42
43 // If expected_present is specified, we may not want to return a buffer yet.
44 if (expected_present.count() != 0) {
45 constexpr auto MAX_REASONABLE_NSEC = 1000000000LL; // 1 second
46
47 // The expected_present argument indicates when the buffer is expected to be presented
48 // on-screen.
49 while (core->queue.size() > 1 && !core->queue[0].is_auto_timestamp) {
50 const auto& buffer_item{core->queue[1]};
51
52 // If entry[1] is timely, drop entry[0] (and repeat).
53 const auto desired_present = buffer_item.timestamp;
54 if (desired_present < expected_present.count() - MAX_REASONABLE_NSEC ||
55 desired_present > expected_present.count()) {
56 // This buffer is set to display in the near future, or desired_present is garbage.
57 LOG_DEBUG(Service_NVFlinger, "nodrop desire={} expect={}", desired_present,
58 expected_present.count());
59 break;
60 }
61
62 LOG_DEBUG(Service_NVFlinger, "drop desire={} expect={} size={}", desired_present,
63 expected_present.count(), core->queue.size());
64
65 if (core->StillTracking(*front)) {
66 // Front buffer is still in mSlots, so mark the slot as free
67 slots[front->slot].buffer_state = BufferState::Free;
68 }
69
70 core->queue.erase(front);
71 front = core->queue.begin();
72 }
73
74 // See if the front buffer is ready to be acquired.
75 const auto desired_present = front->timestamp;
76 if (desired_present > expected_present.count() &&
77 desired_present < expected_present.count() + MAX_REASONABLE_NSEC) {
78 LOG_DEBUG(Service_NVFlinger, "defer desire={} expect={}", desired_present,
79 expected_present.count());
80 return Status::PresentLater;
81 }
82
83 LOG_DEBUG(Service_NVFlinger, "accept desire={} expect={}", desired_present,
84 expected_present.count());
85 }
86
87 const auto slot = front->slot;
88 *out_buffer = *front;
89
90 LOG_DEBUG(Service_NVFlinger, "acquiring slot={}", slot);
91
92 // If the front buffer is still being tracked, update its slot state
93 if (core->StillTracking(*front)) {
94 slots[slot].acquire_called = true;
95 slots[slot].needs_cleanup_on_release = false;
96 slots[slot].buffer_state = BufferState::Acquired;
97 slots[slot].fence = Fence::NoFence();
98 }
99
100 // If the buffer has previously been acquired by the consumer, set graphic_buffer to nullptr to
101 // avoid unnecessarily remapping this buffer on the consumer side.
102 if (out_buffer->acquire_called) {
103 out_buffer->graphic_buffer = nullptr;
104 }
105
106 core->queue.erase(front);
107
108 // We might have freed a slot while dropping old buffers, or the producer may be blocked
109 // waiting for the number of buffers in the queue to decrease.
110 core->SignalDequeueCondition();
111
112 return Status::NoError;
113}
114
115Status BufferQueueConsumer::ReleaseBuffer(s32 slot, u64 frame_number, const Fence& release_fence) {
116 if (slot < 0 || slot >= BufferQueueDefs::NUM_BUFFER_SLOTS) {
117 LOG_ERROR(Service_NVFlinger, "slot {} out of range", slot);
118 return Status::BadValue;
119 }
120
121 std::shared_ptr<IProducerListener> listener;
122 {
123 std::scoped_lock lock{core->mutex};
124
125 // If the frame number has changed because the buffer has been reallocated, we can ignore
126 // this ReleaseBuffer for the old buffer.
127 if (frame_number != slots[slot].frame_number) {
128 return Status::StaleBufferSlot;
129 }
130
131 // Make sure this buffer hasn't been queued while acquired by the consumer.
132 auto current(core->queue.begin());
133 while (current != core->queue.end()) {
134 if (current->slot == slot) {
135 LOG_ERROR(Service_NVFlinger, "buffer slot {} pending release is currently queued",
136 slot);
137 return Status::BadValue;
138 }
139 ++current;
140 }
141
142 if (slots[slot].buffer_state == BufferState::Acquired) {
143 slots[slot].fence = release_fence;
144 slots[slot].buffer_state = BufferState::Free;
145
146 listener = core->connected_producer_listener;
147
148 LOG_DEBUG(Service_NVFlinger, "releasing slot {}", slot);
149 } else if (slots[slot].needs_cleanup_on_release) {
150 LOG_DEBUG(Service_NVFlinger, "releasing a stale buffer slot {} (state = {})", slot,
151 slots[slot].buffer_state);
152
153 slots[slot].needs_cleanup_on_release = false;
154
155 return Status::StaleBufferSlot;
156 } else {
157 LOG_ERROR(Service_NVFlinger, "attempted to release buffer slot {} but its state was {}",
158 slot, slots[slot].buffer_state);
159
160 return Status::BadValue;
161 }
162
163 core->SignalDequeueCondition();
164 }
165
166 // Call back without lock held
167 if (listener != nullptr) {
168 listener->OnBufferReleased();
169 }
170
171 return Status::NoError;
172}
173
174Status BufferQueueConsumer::Connect(std::shared_ptr<IConsumerListener> consumer_listener,
175 bool controlled_by_app) {
176 if (consumer_listener == nullptr) {
177 LOG_ERROR(Service_NVFlinger, "consumer_listener may not be nullptr");
178 return Status::BadValue;
179 }
180
181 LOG_DEBUG(Service_NVFlinger, "controlled_by_app={}", controlled_by_app);
182
183 std::scoped_lock lock{core->mutex};
184
185 if (core->is_abandoned) {
186 LOG_ERROR(Service_NVFlinger, "BufferQueue has been abandoned");
187 return Status::NoInit;
188 }
189
190 core->consumer_listener = consumer_listener;
191 core->consumer_controlled_by_app = controlled_by_app;
192
193 return Status::NoError;
194}
195
196Status BufferQueueConsumer::GetReleasedBuffers(u64* out_slot_mask) {
197 if (out_slot_mask == nullptr) {
198 LOG_ERROR(Service_NVFlinger, "out_slot_mask may not be nullptr");
199 return Status::BadValue;
200 }
201
202 std::scoped_lock lock{core->mutex};
203
204 if (core->is_abandoned) {
205 LOG_ERROR(Service_NVFlinger, "BufferQueue has been abandoned");
206 return Status::NoInit;
207 }
208
209 u64 mask = 0;
210 for (int s = 0; s < BufferQueueDefs::NUM_BUFFER_SLOTS; ++s) {
211 if (!slots[s].acquire_called) {
212 mask |= (1ULL << s);
213 }
214 }
215
216 // Remove from the mask queued buffers for which acquire has been called, since the consumer
217 // will not receive their buffer addresses and so must retain their cached information
218 auto current(core->queue.begin());
219 while (current != core->queue.end()) {
220 if (current->acquire_called) {
221 mask &= ~(1ULL << current->slot);
222 }
223 ++current;
224 }
225
226 LOG_DEBUG(Service_NVFlinger, "returning mask {}", mask);
227 *out_slot_mask = mask;
228 return Status::NoError;
229}
230
231} // namespace Service::android
diff --git a/src/core/hle/service/nvflinger/buffer_queue_consumer.h b/src/core/hle/service/nvflinger/buffer_queue_consumer.h
new file mode 100644
index 000000000..8a047fe06
--- /dev/null
+++ b/src/core/hle/service/nvflinger/buffer_queue_consumer.h
@@ -0,0 +1,37 @@
1// SPDX-License-Identifier: GPL-3.0-or-later
2// Copyright 2021 yuzu Emulator Project
3// Copyright 2014 The Android Open Source Project
4// Parts of this implementation were base on:
5// https://cs.android.com/android/platform/superproject/+/android-5.1.1_r38:frameworks/native/include/gui/BufferQueueConsumer.h
6
7#pragma once
8
9#include <chrono>
10#include <memory>
11
12#include "common/common_types.h"
13#include "core/hle/service/nvflinger/buffer_queue_defs.h"
14#include "core/hle/service/nvflinger/status.h"
15
16namespace Service::android {
17
18class BufferItem;
19class BufferQueueCore;
20class IConsumerListener;
21
22class BufferQueueConsumer final {
23public:
24 explicit BufferQueueConsumer(std::shared_ptr<BufferQueueCore> core_);
25 ~BufferQueueConsumer();
26
27 Status AcquireBuffer(BufferItem* out_buffer, std::chrono::nanoseconds expected_present);
28 Status ReleaseBuffer(s32 slot, u64 frame_number, const Fence& release_fence);
29 Status Connect(std::shared_ptr<IConsumerListener> consumer_listener, bool controlled_by_app);
30 Status GetReleasedBuffers(u64* out_slot_mask);
31
32private:
33 std::shared_ptr<BufferQueueCore> core;
34 BufferQueueDefs::SlotsType& slots;
35};
36
37} // namespace Service::android
diff --git a/src/core/hle/service/nvflinger/buffer_queue_core.cpp b/src/core/hle/service/nvflinger/buffer_queue_core.cpp
new file mode 100644
index 000000000..ec5aabaeb
--- /dev/null
+++ b/src/core/hle/service/nvflinger/buffer_queue_core.cpp
@@ -0,0 +1,117 @@
1// SPDX-License-Identifier: GPL-3.0-or-later
2// Copyright 2021 yuzu Emulator Project
3// Copyright 2014 The Android Open Source Project
4// Parts of this implementation were base on:
5// https://cs.android.com/android/platform/superproject/+/android-5.1.1_r38:frameworks/native/libs/gui/BufferQueueCore.cpp
6
7#include "common/assert.h"
8
9#include "core/hle/service/nvflinger/buffer_queue_core.h"
10
11namespace Service::android {
12
13BufferQueueCore::BufferQueueCore() = default;
14
15BufferQueueCore::~BufferQueueCore() = default;
16
17void BufferQueueCore::NotifyShutdown() {
18 std::scoped_lock lock{mutex};
19
20 is_shutting_down = true;
21
22 SignalDequeueCondition();
23}
24
25void BufferQueueCore::SignalDequeueCondition() {
26 dequeue_condition.notify_all();
27}
28
29bool BufferQueueCore::WaitForDequeueCondition() {
30 if (is_shutting_down) {
31 return false;
32 }
33
34 dequeue_condition.wait(mutex);
35
36 return true;
37}
38
39s32 BufferQueueCore::GetMinUndequeuedBufferCountLocked(bool async) const {
40 // If DequeueBuffer is allowed to error out, we don't have to add an extra buffer.
41 if (!use_async_buffer) {
42 return max_acquired_buffer_count;
43 }
44
45 if (dequeue_buffer_cannot_block || async) {
46 return max_acquired_buffer_count + 1;
47 }
48
49 return max_acquired_buffer_count;
50}
51
52s32 BufferQueueCore::GetMinMaxBufferCountLocked(bool async) const {
53 return GetMinUndequeuedBufferCountLocked(async) + 1;
54}
55
56s32 BufferQueueCore::GetMaxBufferCountLocked(bool async) const {
57 const auto min_buffer_count = GetMinMaxBufferCountLocked(async);
58 auto max_buffer_count = std::max(default_max_buffer_count, min_buffer_count);
59
60 if (override_max_buffer_count != 0) {
61 ASSERT(override_max_buffer_count >= min_buffer_count);
62 max_buffer_count = override_max_buffer_count;
63 }
64
65 // Any buffers that are dequeued by the producer or sitting in the queue waiting to be consumed
66 // need to have their slots preserved.
67 for (s32 slot = max_buffer_count; slot < BufferQueueDefs::NUM_BUFFER_SLOTS; ++slot) {
68 const auto state = slots[slot].buffer_state;
69 if (state == BufferState::Queued || state == BufferState::Dequeued) {
70 max_buffer_count = slot + 1;
71 }
72 }
73
74 return max_buffer_count;
75}
76
77s32 BufferQueueCore::GetPreallocatedBufferCountLocked() const {
78 return static_cast<s32>(std::count_if(slots.begin(), slots.end(),
79 [](const auto& slot) { return slot.is_preallocated; }));
80}
81
82void BufferQueueCore::FreeBufferLocked(s32 slot) {
83 LOG_DEBUG(Service_NVFlinger, "slot {}", slot);
84
85 slots[slot].graphic_buffer.reset();
86
87 if (slots[slot].buffer_state == BufferState::Acquired) {
88 slots[slot].needs_cleanup_on_release = true;
89 }
90
91 slots[slot].buffer_state = BufferState::Free;
92 slots[slot].frame_number = UINT32_MAX;
93 slots[slot].acquire_called = false;
94 slots[slot].fence = Fence::NoFence();
95}
96
97void BufferQueueCore::FreeAllBuffersLocked() {
98 buffer_has_been_queued = false;
99
100 for (s32 slot = 0; slot < BufferQueueDefs::NUM_BUFFER_SLOTS; ++slot) {
101 FreeBufferLocked(slot);
102 }
103}
104
105bool BufferQueueCore::StillTracking(const BufferItem& item) const {
106 const BufferSlot& slot = slots[item.slot];
107
108 return (slot.graphic_buffer != nullptr) && (item.graphic_buffer == slot.graphic_buffer);
109}
110
111void BufferQueueCore::WaitWhileAllocatingLocked() const {
112 while (is_allocating) {
113 is_allocating_condition.wait(mutex);
114 }
115}
116
117} // namespace Service::android
diff --git a/src/core/hle/service/nvflinger/buffer_queue_core.h b/src/core/hle/service/nvflinger/buffer_queue_core.h
new file mode 100644
index 000000000..e4e0937cb
--- /dev/null
+++ b/src/core/hle/service/nvflinger/buffer_queue_core.h
@@ -0,0 +1,79 @@
1// SPDX-License-Identifier: GPL-3.0-or-later
2// Copyright 2021 yuzu Emulator Project
3// Copyright 2014 The Android Open Source Project
4// Parts of this implementation were base on:
5// https://cs.android.com/android/platform/superproject/+/android-5.1.1_r38:frameworks/native/include/gui/BufferQueueCore.h
6
7#pragma once
8
9#include <condition_variable>
10#include <list>
11#include <memory>
12#include <mutex>
13#include <set>
14#include <vector>
15
16#include "core/hle/service/nvflinger/buffer_item.h"
17#include "core/hle/service/nvflinger/buffer_queue_defs.h"
18#include "core/hle/service/nvflinger/pixel_format.h"
19#include "core/hle/service/nvflinger/status.h"
20#include "core/hle/service/nvflinger/window.h"
21
22namespace Service::android {
23
24class IConsumerListener;
25class IProducerListener;
26
27class BufferQueueCore final {
28 friend class BufferQueueProducer;
29 friend class BufferQueueConsumer;
30
31public:
32 static constexpr s32 INVALID_BUFFER_SLOT = BufferItem::INVALID_BUFFER_SLOT;
33
34 BufferQueueCore();
35 ~BufferQueueCore();
36
37 void NotifyShutdown();
38
39private:
40 void SignalDequeueCondition();
41 bool WaitForDequeueCondition();
42
43 s32 GetMinUndequeuedBufferCountLocked(bool async) const;
44 s32 GetMinMaxBufferCountLocked(bool async) const;
45 s32 GetMaxBufferCountLocked(bool async) const;
46 s32 GetPreallocatedBufferCountLocked() const;
47 void FreeBufferLocked(s32 slot);
48 void FreeAllBuffersLocked();
49 bool StillTracking(const BufferItem& item) const;
50 void WaitWhileAllocatingLocked() const;
51
52private:
53 mutable std::mutex mutex;
54 bool is_abandoned{};
55 bool consumer_controlled_by_app{};
56 std::shared_ptr<IConsumerListener> consumer_listener;
57 u32 consumer_usage_bit{};
58 NativeWindowApi connected_api{NativeWindowApi::NoConnectedApi};
59 std::shared_ptr<IProducerListener> connected_producer_listener;
60 BufferQueueDefs::SlotsType slots{};
61 std::vector<BufferItem> queue;
62 s32 override_max_buffer_count{};
63 mutable std::condition_variable_any dequeue_condition;
64 const bool use_async_buffer{}; // This is always disabled on HOS
65 bool dequeue_buffer_cannot_block{};
66 PixelFormat default_buffer_format{PixelFormat::Rgba8888};
67 u32 default_width{1};
68 u32 default_height{1};
69 s32 default_max_buffer_count{2};
70 const s32 max_acquired_buffer_count{}; // This is always zero on HOS
71 bool buffer_has_been_queued{};
72 u64 frame_counter{};
73 u32 transform_hint{};
74 bool is_allocating{};
75 mutable std::condition_variable_any is_allocating_condition;
76 bool is_shutting_down{};
77};
78
79} // namespace Service::android
diff --git a/src/core/hle/service/nvflinger/buffer_queue_defs.h b/src/core/hle/service/nvflinger/buffer_queue_defs.h
new file mode 100644
index 000000000..387d3d36a
--- /dev/null
+++ b/src/core/hle/service/nvflinger/buffer_queue_defs.h
@@ -0,0 +1,21 @@
1// SPDX-License-Identifier: GPL-3.0-or-later
2// Copyright 2021 yuzu Emulator Project
3// Copyright 2014 The Android Open Source Project
4// Parts of this implementation were base on:
5// https://cs.android.com/android/platform/superproject/+/android-5.1.1_r38:frameworks/native/include/gui/BufferQueueDefs.h
6
7#pragma once
8
9#include <array>
10
11#include "common/common_types.h"
12#include "core/hle/service/nvflinger/buffer_slot.h"
13
14namespace Service::android::BufferQueueDefs {
15
16// BufferQueue will keep track of at most this value of buffers.
17constexpr s32 NUM_BUFFER_SLOTS = 64;
18
19using SlotsType = std::array<BufferSlot, NUM_BUFFER_SLOTS>;
20
21} // namespace Service::android::BufferQueueDefs
diff --git a/src/core/hle/service/nvflinger/buffer_queue_producer.cpp b/src/core/hle/service/nvflinger/buffer_queue_producer.cpp
new file mode 100644
index 000000000..6f604a88e
--- /dev/null
+++ b/src/core/hle/service/nvflinger/buffer_queue_producer.cpp
@@ -0,0 +1,923 @@
1// SPDX-License-Identifier: GPL-3.0-or-later
2// Copyright 2021 yuzu Emulator Project
3// Copyright 2014 The Android Open Source Project
4// Parts of this implementation were base on:
5// https://cs.android.com/android/platform/superproject/+/android-5.1.1_r38:frameworks/native/libs/gui/BufferQueueProducer.cpp
6
7#include "common/assert.h"
8#include "common/logging/log.h"
9#include "common/settings.h"
10#include "core/core.h"
11#include "core/hle/kernel/hle_ipc.h"
12#include "core/hle/kernel/k_event.h"
13#include "core/hle/kernel/k_readable_event.h"
14#include "core/hle/kernel/k_writable_event.h"
15#include "core/hle/kernel/kernel.h"
16#include "core/hle/service/kernel_helpers.h"
17#include "core/hle/service/nvdrv/nvdrv.h"
18#include "core/hle/service/nvflinger/buffer_queue_core.h"
19#include "core/hle/service/nvflinger/buffer_queue_producer.h"
20#include "core/hle/service/nvflinger/consumer_listener.h"
21#include "core/hle/service/nvflinger/parcel.h"
22#include "core/hle/service/nvflinger/ui/graphic_buffer.h"
23#include "core/hle/service/nvflinger/window.h"
24#include "core/hle/service/vi/vi.h"
25
26namespace Service::android {
27
28BufferQueueProducer::BufferQueueProducer(Service::KernelHelpers::ServiceContext& service_context_,
29 std::shared_ptr<BufferQueueCore> buffer_queue_core_)
30 : service_context{service_context_}, core{std::move(buffer_queue_core_)}, slots(core->slots) {
31 buffer_wait_event = service_context.CreateEvent("BufferQueue:WaitEvent");
32}
33
34BufferQueueProducer::~BufferQueueProducer() {
35 service_context.CloseEvent(buffer_wait_event);
36}
37
38Status BufferQueueProducer::RequestBuffer(s32 slot, std::shared_ptr<GraphicBuffer>* buf) {
39 LOG_DEBUG(Service_NVFlinger, "slot {}", slot);
40
41 std::scoped_lock lock{core->mutex};
42
43 if (core->is_abandoned) {
44 LOG_ERROR(Service_NVFlinger, "BufferQueue has been abandoned");
45 return Status::NoInit;
46 }
47 if (slot < 0 || slot >= BufferQueueDefs::NUM_BUFFER_SLOTS) {
48 LOG_ERROR(Service_NVFlinger, "slot index {} out of range [0, {})", slot,
49 BufferQueueDefs::NUM_BUFFER_SLOTS);
50 return Status::BadValue;
51 } else if (slots[slot].buffer_state != BufferState::Dequeued) {
52 LOG_ERROR(Service_NVFlinger, "slot {} is not owned by the producer (state = {})", slot,
53 slots[slot].buffer_state);
54 return Status::BadValue;
55 }
56
57 slots[slot].request_buffer_called = true;
58 *buf = slots[slot].graphic_buffer;
59
60 return Status::NoError;
61}
62
63Status BufferQueueProducer::SetBufferCount(s32 buffer_count) {
64 LOG_DEBUG(Service_NVFlinger, "count = {}", buffer_count);
65
66 std::shared_ptr<IConsumerListener> listener;
67 {
68 std::scoped_lock lock{core->mutex};
69 core->WaitWhileAllocatingLocked();
70
71 if (core->is_abandoned) {
72 LOG_ERROR(Service_NVFlinger, "BufferQueue has been abandoned");
73 return Status::NoInit;
74 }
75
76 if (buffer_count > BufferQueueDefs::NUM_BUFFER_SLOTS) {
77 LOG_ERROR(Service_NVFlinger, "buffer_count {} too large (max {})", buffer_count,
78 BufferQueueDefs::NUM_BUFFER_SLOTS);
79 return Status::BadValue;
80 }
81
82 // There must be no dequeued buffers when changing the buffer count.
83 for (s32 s{}; s < BufferQueueDefs::NUM_BUFFER_SLOTS; ++s) {
84 if (slots[s].buffer_state == BufferState::Dequeued) {
85 LOG_ERROR(Service_NVFlinger, "buffer owned by producer");
86 return Status::BadValue;
87 }
88 }
89
90 if (buffer_count == 0) {
91 core->override_max_buffer_count = 0;
92 core->SignalDequeueCondition();
93 return Status::NoError;
94 }
95
96 const s32 min_buffer_slots = core->GetMinMaxBufferCountLocked(false);
97 if (buffer_count < min_buffer_slots) {
98 LOG_ERROR(Service_NVFlinger, "requested buffer count {} is less than minimum {}",
99 buffer_count, min_buffer_slots);
100 return Status::BadValue;
101 }
102
103 // Here we are guaranteed that the producer doesn't have any dequeued buffers and will
104 // release all of its buffer references.
105 if (core->GetPreallocatedBufferCountLocked() <= 0) {
106 core->FreeAllBuffersLocked();
107 }
108
109 core->override_max_buffer_count = buffer_count;
110 core->SignalDequeueCondition();
111 buffer_wait_event->GetWritableEvent().Signal();
112 listener = core->consumer_listener;
113 }
114
115 // Call back without lock held
116 if (listener != nullptr) {
117 listener->OnBuffersReleased();
118 }
119
120 return Status::NoError;
121}
122
123Status BufferQueueProducer::WaitForFreeSlotThenRelock(bool async, s32* found,
124 Status* return_flags) const {
125 bool try_again = true;
126
127 while (try_again) {
128 if (core->is_abandoned) {
129 LOG_ERROR(Service_NVFlinger, "BufferQueue has been abandoned");
130 return Status::NoInit;
131 }
132
133 const s32 max_buffer_count = core->GetMaxBufferCountLocked(async);
134 if (async && core->override_max_buffer_count) {
135 if (core->override_max_buffer_count < max_buffer_count) {
136 LOG_ERROR(Service_NVFlinger, "async mode is invalid with buffer count override");
137 return Status::BadValue;
138 }
139 }
140
141 // Free up any buffers that are in slots beyond the max buffer count
142 for (s32 s = max_buffer_count; s < BufferQueueDefs::NUM_BUFFER_SLOTS; ++s) {
143 ASSERT(slots[s].buffer_state == BufferState::Free);
144 if (slots[s].graphic_buffer != nullptr) {
145 core->FreeBufferLocked(s);
146 *return_flags |= Status::ReleaseAllBuffers;
147 }
148 }
149
150 // Look for a free buffer to give to the client
151 *found = BufferQueueCore::INVALID_BUFFER_SLOT;
152 s32 dequeued_count{};
153 s32 acquired_count{};
154 for (s32 s{}; s < max_buffer_count; ++s) {
155 switch (slots[s].buffer_state) {
156 case BufferState::Dequeued:
157 ++dequeued_count;
158 break;
159 case BufferState::Acquired:
160 ++acquired_count;
161 break;
162 case BufferState::Free:
163 // We return the oldest of the free buffers to avoid stalling the producer if
164 // possible, since the consumer may still have pending reads of in-flight buffers
165 if (*found == BufferQueueCore::INVALID_BUFFER_SLOT ||
166 slots[s].frame_number < slots[*found].frame_number) {
167 *found = s;
168 }
169 break;
170 default:
171 break;
172 }
173 }
174
175 // Producers are not allowed to dequeue more than one buffer if they did not set a buffer
176 // count
177 if (!core->override_max_buffer_count && dequeued_count) {
178 LOG_ERROR(Service_NVFlinger,
179 "can't dequeue multiple buffers without setting the buffer count");
180 return Status::InvalidOperation;
181 }
182
183 // See whether a buffer has been queued since the last SetBufferCount so we know whether to
184 // perform the min undequeued buffers check below
185 if (core->buffer_has_been_queued) {
186 // Make sure the producer is not trying to dequeue more buffers than allowed
187 const s32 new_undequeued_count = max_buffer_count - (dequeued_count + 1);
188 const s32 min_undequeued_count = core->GetMinUndequeuedBufferCountLocked(async);
189 if (new_undequeued_count < min_undequeued_count) {
190 LOG_ERROR(Service_NVFlinger,
191 "min undequeued buffer count({}) exceeded (dequeued={} undequeued={})",
192 min_undequeued_count, dequeued_count, new_undequeued_count);
193 return Status::InvalidOperation;
194 }
195 }
196
197 // If we disconnect and reconnect quickly, we can be in a state where our slots are empty
198 // but we have many buffers in the queue. This can cause us to run out of memory if we
199 // outrun the consumer. Wait here if it looks like we have too many buffers queued up.
200 const bool too_many_buffers = core->queue.size() > static_cast<size_t>(max_buffer_count);
201 if (too_many_buffers) {
202 LOG_ERROR(Service_NVFlinger, "queue size is {}, waiting", core->queue.size());
203 }
204
205 // If no buffer is found, or if the queue has too many buffers outstanding, wait for a
206 // buffer to be acquired or released, or for the max buffer count to change.
207 try_again = (*found == BufferQueueCore::INVALID_BUFFER_SLOT) || too_many_buffers;
208 if (try_again) {
209 // Return an error if we're in non-blocking mode (producer and consumer are controlled
210 // by the application).
211 if (core->dequeue_buffer_cannot_block &&
212 (acquired_count <= core->max_acquired_buffer_count)) {
213 return Status::WouldBlock;
214 }
215
216 if (!core->WaitForDequeueCondition()) {
217 // We are no longer running
218 return Status::NoError;
219 }
220 }
221 }
222
223 return Status::NoError;
224}
225
226Status BufferQueueProducer::DequeueBuffer(s32* out_slot, Fence* out_fence, bool async, u32 width,
227 u32 height, PixelFormat format, u32 usage) {
228 LOG_DEBUG(Service_NVFlinger, "async={} w={} h={} format={}, usage={}", async ? "true" : "false",
229 width, height, format, usage);
230
231 if ((width != 0 && height == 0) || (width == 0 && height != 0)) {
232 LOG_ERROR(Service_NVFlinger, "invalid size: w={} h={}", width, height);
233 return Status::BadValue;
234 }
235
236 Status return_flags = Status::NoError;
237 bool attached_by_consumer = false;
238 {
239 std::scoped_lock lock{core->mutex};
240 core->WaitWhileAllocatingLocked();
241
242 if (format == PixelFormat::NoFormat) {
243 format = core->default_buffer_format;
244 }
245
246 // Enable the usage bits the consumer requested
247 usage |= core->consumer_usage_bit;
248
249 s32 found{};
250 Status status = WaitForFreeSlotThenRelock(async, &found, &return_flags);
251 if (status != Status::NoError) {
252 return status;
253 }
254
255 // This should not happen
256 if (found == BufferQueueCore::INVALID_BUFFER_SLOT) {
257 LOG_ERROR(Service_NVFlinger, "no available buffer slots");
258 return Status::Busy;
259 }
260
261 *out_slot = found;
262
263 attached_by_consumer = slots[found].attached_by_consumer;
264
265 const bool use_default_size = !width && !height;
266 if (use_default_size) {
267 width = core->default_width;
268 height = core->default_height;
269 }
270
271 slots[found].buffer_state = BufferState::Dequeued;
272
273 const std::shared_ptr<GraphicBuffer>& buffer(slots[found].graphic_buffer);
274 if ((buffer == nullptr) || (buffer->Width() != width) || (buffer->Height() != height) ||
275 (buffer->Format() != format) || ((buffer->Usage() & usage) != usage)) {
276 slots[found].acquire_called = false;
277 slots[found].graphic_buffer = nullptr;
278 slots[found].request_buffer_called = false;
279 slots[found].fence = Fence::NoFence();
280
281 return_flags |= Status::BufferNeedsReallocation;
282 }
283
284 *out_fence = slots[found].fence;
285 slots[found].fence = Fence::NoFence();
286 }
287
288 if ((return_flags & Status::BufferNeedsReallocation) != Status::None) {
289 LOG_DEBUG(Service_NVFlinger, "allocating a new buffer for slot {}", *out_slot);
290
291 auto graphic_buffer = std::make_shared<GraphicBuffer>(width, height, format, usage);
292 if (graphic_buffer == nullptr) {
293 LOG_ERROR(Service_NVFlinger, "creating GraphicBuffer failed");
294 return Status::NoMemory;
295 }
296
297 {
298 std::scoped_lock lock{core->mutex};
299
300 if (core->is_abandoned) {
301 LOG_ERROR(Service_NVFlinger, "BufferQueue has been abandoned");
302 return Status::NoInit;
303 }
304
305 slots[*out_slot].frame_number = UINT32_MAX;
306 slots[*out_slot].graphic_buffer = graphic_buffer;
307 }
308 }
309
310 if (attached_by_consumer) {
311 return_flags |= Status::BufferNeedsReallocation;
312 }
313
314 LOG_DEBUG(Service_NVFlinger, "returning slot={} frame={}, flags={}", *out_slot,
315 slots[*out_slot].frame_number, return_flags);
316
317 return return_flags;
318}
319
320Status BufferQueueProducer::DetachBuffer(s32 slot) {
321 LOG_DEBUG(Service_NVFlinger, "slot {}", slot);
322
323 std::scoped_lock lock{core->mutex};
324
325 if (core->is_abandoned) {
326 LOG_ERROR(Service_NVFlinger, "BufferQueue has been abandoned");
327 return Status::NoInit;
328 }
329
330 if (slot < 0 || slot >= BufferQueueDefs::NUM_BUFFER_SLOTS) {
331 LOG_ERROR(Service_NVFlinger, "slot {} out of range [0, {})", slot,
332 BufferQueueDefs::NUM_BUFFER_SLOTS);
333 return Status::BadValue;
334 } else if (slots[slot].buffer_state != BufferState::Dequeued) {
335 LOG_ERROR(Service_NVFlinger, "slot {} is not owned by the producer (state = {})", slot,
336 slots[slot].buffer_state);
337 return Status::BadValue;
338 } else if (!slots[slot].request_buffer_called) {
339 LOG_ERROR(Service_NVFlinger, "buffer in slot {} has not been requested", slot);
340 return Status::BadValue;
341 }
342
343 core->FreeBufferLocked(slot);
344 core->SignalDequeueCondition();
345
346 return Status::NoError;
347}
348
349Status BufferQueueProducer::DetachNextBuffer(std::shared_ptr<GraphicBuffer>* out_buffer,
350 Fence* out_fence) {
351 if (out_buffer == nullptr) {
352 LOG_ERROR(Service_NVFlinger, "out_buffer must not be nullptr");
353 return Status::BadValue;
354 } else if (out_fence == nullptr) {
355 LOG_ERROR(Service_NVFlinger, "out_fence must not be nullptr");
356 return Status::BadValue;
357 }
358
359 std::scoped_lock lock{core->mutex};
360 core->WaitWhileAllocatingLocked();
361
362 if (core->is_abandoned) {
363 LOG_ERROR(Service_NVFlinger, "BufferQueue has been abandoned");
364 return Status::NoInit;
365 }
366
367 // Find the oldest valid slot
368 int found = BufferQueueCore::INVALID_BUFFER_SLOT;
369 for (int s = 0; s < BufferQueueDefs::NUM_BUFFER_SLOTS; ++s) {
370 if (slots[s].buffer_state == BufferState::Free && slots[s].graphic_buffer != nullptr) {
371 if (found == BufferQueueCore::INVALID_BUFFER_SLOT ||
372 slots[s].frame_number < slots[found].frame_number) {
373 found = s;
374 }
375 }
376 }
377
378 if (found == BufferQueueCore::INVALID_BUFFER_SLOT) {
379 return Status::NoMemory;
380 }
381
382 LOG_DEBUG(Service_NVFlinger, "Detached slot {}", found);
383
384 *out_buffer = slots[found].graphic_buffer;
385 *out_fence = slots[found].fence;
386
387 core->FreeBufferLocked(found);
388
389 return Status::NoError;
390}
391
392Status BufferQueueProducer::AttachBuffer(s32* out_slot,
393 const std::shared_ptr<GraphicBuffer>& buffer) {
394 if (out_slot == nullptr) {
395 LOG_ERROR(Service_NVFlinger, "out_slot must not be nullptr");
396 return Status::BadValue;
397 } else if (buffer == nullptr) {
398 LOG_ERROR(Service_NVFlinger, "Cannot attach nullptr buffer");
399 return Status::BadValue;
400 }
401
402 std::scoped_lock lock{core->mutex};
403 core->WaitWhileAllocatingLocked();
404
405 Status return_flags = Status::NoError;
406 s32 found{};
407
408 const auto status = WaitForFreeSlotThenRelock(false, &found, &return_flags);
409 if (status != Status::NoError) {
410 return status;
411 }
412
413 // This should not happen
414 if (found == BufferQueueCore::INVALID_BUFFER_SLOT) {
415 LOG_ERROR(Service_NVFlinger, "No available buffer slots");
416 return Status::Busy;
417 }
418
419 *out_slot = found;
420
421 LOG_DEBUG(Service_NVFlinger, "Returning slot {} flags={}", *out_slot, return_flags);
422
423 slots[*out_slot].graphic_buffer = buffer;
424 slots[*out_slot].buffer_state = BufferState::Dequeued;
425 slots[*out_slot].fence = Fence::NoFence();
426 slots[*out_slot].request_buffer_called = true;
427
428 return return_flags;
429}
430
431Status BufferQueueProducer::QueueBuffer(s32 slot, const QueueBufferInput& input,
432 QueueBufferOutput* output) {
433 s64 timestamp{};
434 bool is_auto_timestamp{};
435 Common::Rectangle<s32> crop;
436 NativeWindowScalingMode scaling_mode{};
437 NativeWindowTransform transform;
438 u32 sticky_transform_{};
439 bool async{};
440 s32 swap_interval{};
441 Fence fence{};
442
443 input.Deflate(&timestamp, &is_auto_timestamp, &crop, &scaling_mode, &transform,
444 &sticky_transform_, &async, &swap_interval, &fence);
445
446 switch (scaling_mode) {
447 case NativeWindowScalingMode::Freeze:
448 case NativeWindowScalingMode::ScaleToWindow:
449 case NativeWindowScalingMode::ScaleCrop:
450 case NativeWindowScalingMode::NoScaleCrop:
451 break;
452 default:
453 LOG_ERROR(Service_NVFlinger, "unknown scaling mode {}", scaling_mode);
454 return Status::BadValue;
455 }
456
457 std::shared_ptr<IConsumerListener> frame_available_listener;
458 std::shared_ptr<IConsumerListener> frame_replaced_listener;
459 s32 callback_ticket{};
460 BufferItem item;
461
462 {
463 std::scoped_lock lock{core->mutex};
464
465 if (core->is_abandoned) {
466 LOG_ERROR(Service_NVFlinger, "BufferQueue has been abandoned");
467 return Status::NoInit;
468 }
469
470 const s32 max_buffer_count = core->GetMaxBufferCountLocked(async);
471 if (async && core->override_max_buffer_count) {
472 if (core->override_max_buffer_count < max_buffer_count) {
473 LOG_ERROR(Service_NVFlinger, "async mode is invalid with "
474 "buffer count override");
475 return Status::BadValue;
476 }
477 }
478
479 if (slot < 0 || slot >= max_buffer_count) {
480 LOG_ERROR(Service_NVFlinger, "slot index {} out of range [0, {})", slot,
481 max_buffer_count);
482 return Status::BadValue;
483 } else if (slots[slot].buffer_state != BufferState::Dequeued) {
484 LOG_ERROR(Service_NVFlinger,
485 "slot {} is not owned by the producer "
486 "(state = {})",
487 slot, slots[slot].buffer_state);
488 return Status::BadValue;
489 } else if (!slots[slot].request_buffer_called) {
490 LOG_ERROR(Service_NVFlinger,
491 "slot {} was queued without requesting "
492 "a buffer",
493 slot);
494 return Status::BadValue;
495 }
496
497 LOG_DEBUG(Service_NVFlinger,
498 "slot={} frame={} time={} crop=[{},{},{},{}] transform={} scale={}", slot,
499 core->frame_counter + 1, timestamp, crop.Left(), crop.Top(), crop.Right(),
500 crop.Bottom(), transform, scaling_mode);
501
502 const std::shared_ptr<GraphicBuffer>& graphic_buffer(slots[slot].graphic_buffer);
503 Common::Rectangle<s32> buffer_rect(graphic_buffer->Width(), graphic_buffer->Height());
504 Common::Rectangle<s32> cropped_rect;
505 [[maybe_unused]] const bool unused = crop.Intersect(buffer_rect, &cropped_rect);
506
507 if (cropped_rect != crop) {
508 LOG_ERROR(Service_NVFlinger, "crop rect is not contained within the buffer in slot {}",
509 slot);
510 return Status::BadValue;
511 }
512
513 slots[slot].fence = fence;
514 slots[slot].buffer_state = BufferState::Queued;
515 ++core->frame_counter;
516 slots[slot].frame_number = core->frame_counter;
517
518 item.acquire_called = slots[slot].acquire_called;
519 item.graphic_buffer = slots[slot].graphic_buffer;
520 item.crop = crop;
521 item.transform = transform & ~NativeWindowTransform::InverseDisplay;
522 item.transform_to_display_inverse =
523 (transform & NativeWindowTransform::InverseDisplay) != NativeWindowTransform::None;
524 item.scaling_mode = static_cast<u32>(scaling_mode);
525 item.timestamp = timestamp;
526 item.is_auto_timestamp = is_auto_timestamp;
527 item.frame_number = core->frame_counter;
528 item.slot = slot;
529 item.fence = fence;
530 item.is_droppable = core->dequeue_buffer_cannot_block || async;
531 item.swap_interval = swap_interval;
532
533 sticky_transform = sticky_transform_;
534
535 if (core->queue.empty()) {
536 // When the queue is empty, we can simply queue this buffer
537 core->queue.push_back(item);
538 frame_available_listener = core->consumer_listener;
539 } else {
540 // When the queue is not empty, we need to look at the front buffer
541 // state to see if we need to replace it
542 auto front(core->queue.begin());
543
544 if (front->is_droppable) {
545 // If the front queued buffer is still being tracked, we first
546 // mark it as freed
547 if (core->StillTracking(*front)) {
548 slots[front->slot].buffer_state = BufferState::Free;
549 // Reset the frame number of the freed buffer so that it is the first in line to
550 // be dequeued again
551 slots[front->slot].frame_number = 0;
552 }
553 // Overwrite the droppable buffer with the incoming one
554 *front = item;
555 frame_replaced_listener = core->consumer_listener;
556 } else {
557 core->queue.push_back(item);
558 frame_available_listener = core->consumer_listener;
559 }
560 }
561
562 core->buffer_has_been_queued = true;
563 core->SignalDequeueCondition();
564 output->Inflate(core->default_width, core->default_height, core->transform_hint,
565 static_cast<u32>(core->queue.size()));
566
567 // Take a ticket for the callback functions
568 callback_ticket = next_callback_ticket++;
569 }
570
571 // Don't send the GraphicBuffer through the callback, and don't send the slot number, since the
572 // consumer shouldn't need it
573 item.graphic_buffer.reset();
574 item.slot = BufferItem::INVALID_BUFFER_SLOT;
575
576 // Call back without the main BufferQueue lock held, but with the callback lock held so we can
577 // ensure that callbacks occur in order
578 {
579 std::scoped_lock lock{callback_mutex};
580 while (callback_ticket != current_callback_ticket) {
581 callback_condition.wait(callback_mutex);
582 }
583
584 if (frame_available_listener != nullptr) {
585 frame_available_listener->OnFrameAvailable(item);
586 } else if (frame_replaced_listener != nullptr) {
587 frame_replaced_listener->OnFrameReplaced(item);
588 }
589
590 ++current_callback_ticket;
591 callback_condition.notify_all();
592 }
593
594 return Status::NoError;
595}
596
597void BufferQueueProducer::CancelBuffer(s32 slot, const Fence& fence) {
598 LOG_DEBUG(Service_NVFlinger, "slot {}", slot);
599
600 std::scoped_lock lock{core->mutex};
601
602 if (core->is_abandoned) {
603 LOG_ERROR(Service_NVFlinger, "BufferQueue has been abandoned");
604 return;
605 }
606
607 if (slot < 0 || slot >= BufferQueueDefs::NUM_BUFFER_SLOTS) {
608 LOG_ERROR(Service_NVFlinger, "slot index {} out of range [0, {})", slot,
609 BufferQueueDefs::NUM_BUFFER_SLOTS);
610 return;
611 } else if (slots[slot].buffer_state != BufferState::Dequeued) {
612 LOG_ERROR(Service_NVFlinger, "slot {} is not owned by the producer (state = {})", slot,
613 slots[slot].buffer_state);
614 return;
615 }
616
617 slots[slot].buffer_state = BufferState::Free;
618 slots[slot].frame_number = 0;
619 slots[slot].fence = fence;
620
621 core->SignalDequeueCondition();
622 buffer_wait_event->GetWritableEvent().Signal();
623}
624
625Status BufferQueueProducer::Query(NativeWindow what, s32* out_value) {
626 std::scoped_lock lock{core->mutex};
627
628 if (out_value == nullptr) {
629 LOG_ERROR(Service_NVFlinger, "outValue was nullptr");
630 return Status::BadValue;
631 }
632
633 if (core->is_abandoned) {
634 LOG_ERROR(Service_NVFlinger, "BufferQueue has been abandoned");
635 return Status::NoInit;
636 }
637
638 u32 value{};
639 switch (what) {
640 case NativeWindow::Width:
641 value = core->default_width;
642 break;
643 case NativeWindow::Height:
644 value = core->default_height;
645 break;
646 case NativeWindow::Format:
647 value = static_cast<u32>(core->default_buffer_format);
648 break;
649 case NativeWindow::MinUndequeedBuffers:
650 value = core->GetMinUndequeuedBufferCountLocked(false);
651 break;
652 case NativeWindow::StickyTransform:
653 value = sticky_transform;
654 break;
655 case NativeWindow::ConsumerRunningBehind:
656 value = (core->queue.size() > 1);
657 break;
658 case NativeWindow::ConsumerUsageBits:
659 value = core->consumer_usage_bit;
660 break;
661 default:
662 UNREACHABLE();
663 return Status::BadValue;
664 }
665
666 LOG_DEBUG(Service_NVFlinger, "what = {}, value = {}", what, value);
667
668 *out_value = static_cast<s32>(value);
669
670 return Status::NoError;
671}
672
673Status BufferQueueProducer::Connect(const std::shared_ptr<IProducerListener>& listener,
674 NativeWindowApi api, bool producer_controlled_by_app,
675 QueueBufferOutput* output) {
676 std::scoped_lock lock{core->mutex};
677
678 LOG_DEBUG(Service_NVFlinger, "api = {} producer_controlled_by_app = {}", api,
679 producer_controlled_by_app);
680
681 if (core->is_abandoned) {
682 LOG_ERROR(Service_NVFlinger, "BufferQueue has been abandoned");
683 return Status::NoInit;
684 }
685
686 if (core->consumer_listener == nullptr) {
687 LOG_ERROR(Service_NVFlinger, "BufferQueue has no consumer");
688 return Status::NoInit;
689 }
690
691 if (output == nullptr) {
692 LOG_ERROR(Service_NVFlinger, "output was nullptr");
693 return Status::BadValue;
694 }
695
696 if (core->connected_api != NativeWindowApi::NoConnectedApi) {
697 LOG_ERROR(Service_NVFlinger, "already connected (cur = {} req = {})", core->connected_api,
698 api);
699 return Status::BadValue;
700 }
701
702 Status status = Status::NoError;
703 switch (api) {
704 case NativeWindowApi::Egl:
705 case NativeWindowApi::Cpu:
706 case NativeWindowApi::Media:
707 case NativeWindowApi::Camera:
708 core->connected_api = api;
709 output->Inflate(core->default_width, core->default_height, core->transform_hint,
710 static_cast<u32>(core->queue.size()));
711 core->connected_producer_listener = listener;
712 break;
713 default:
714 LOG_ERROR(Service_NVFlinger, "unknown api = {}", api);
715 status = Status::BadValue;
716 break;
717 }
718
719 core->buffer_has_been_queued = false;
720 core->dequeue_buffer_cannot_block =
721 core->consumer_controlled_by_app && producer_controlled_by_app;
722
723 return status;
724}
725
726Status BufferQueueProducer::Disconnect(NativeWindowApi api) {
727 LOG_DEBUG(Service_NVFlinger, "api = {}", api);
728
729 Status status = Status::NoError;
730 std::shared_ptr<IConsumerListener> listener;
731
732 {
733 std::scoped_lock lock{core->mutex};
734
735 core->WaitWhileAllocatingLocked();
736
737 if (core->is_abandoned) {
738 // Disconnecting after the surface has been abandoned is a no-op.
739 return Status::NoError;
740 }
741
742 switch (api) {
743 case NativeWindowApi::Egl:
744 case NativeWindowApi::Cpu:
745 case NativeWindowApi::Media:
746 case NativeWindowApi::Camera:
747 if (core->connected_api == api) {
748 core->FreeAllBuffersLocked();
749 core->connected_producer_listener = nullptr;
750 core->connected_api = NativeWindowApi::NoConnectedApi;
751 core->SignalDequeueCondition();
752 buffer_wait_event->GetWritableEvent().Signal();
753 listener = core->consumer_listener;
754 } else {
755 LOG_ERROR(Service_NVFlinger, "still connected to another api (cur = {} req = {})",
756 core->connected_api, api);
757 status = Status::BadValue;
758 }
759 break;
760 default:
761 LOG_ERROR(Service_NVFlinger, "unknown api = {}", api);
762 status = Status::BadValue;
763 break;
764 }
765 }
766
767 // Call back without lock held
768 if (listener != nullptr) {
769 listener->OnBuffersReleased();
770 }
771
772 return status;
773}
774
775Status BufferQueueProducer::SetPreallocatedBuffer(s32 slot,
776 const std::shared_ptr<GraphicBuffer>& buffer) {
777 LOG_DEBUG(Service_NVFlinger, "slot {}", slot);
778
779 if (slot < 0 || slot >= BufferQueueDefs::NUM_BUFFER_SLOTS) {
780 return Status::BadValue;
781 }
782
783 std::scoped_lock lock{core->mutex};
784
785 slots[slot] = {};
786 slots[slot].graphic_buffer = buffer;
787 slots[slot].frame_number = 0;
788
789 // Most games preallocate a buffer and pass a valid buffer here. However, it is possible for
790 // this to be called with an empty buffer, Naruto Ultimate Ninja Storm is a game that does this.
791 if (buffer) {
792 slots[slot].is_preallocated = true;
793
794 core->override_max_buffer_count = core->GetPreallocatedBufferCountLocked();
795 core->default_width = buffer->Width();
796 core->default_height = buffer->Height();
797 core->default_buffer_format = buffer->Format();
798 }
799
800 core->SignalDequeueCondition();
801 buffer_wait_event->GetWritableEvent().Signal();
802
803 return Status::NoError;
804}
805
806void BufferQueueProducer::Transact(Kernel::HLERequestContext& ctx, TransactionId code, u32 flags) {
807 Status status{Status::NoError};
808 Parcel parcel_in{ctx.ReadBuffer()};
809 Parcel parcel_out{};
810
811 switch (code) {
812 case TransactionId::Connect: {
813 const auto enable_listener = parcel_in.Read<bool>();
814 const auto api = parcel_in.Read<NativeWindowApi>();
815 const auto producer_controlled_by_app = parcel_in.Read<bool>();
816
817 UNIMPLEMENTED_IF_MSG(enable_listener, "Listener is unimplemented!");
818
819 std::shared_ptr<IProducerListener> listener;
820 QueueBufferOutput output{};
821
822 status = Connect(listener, api, producer_controlled_by_app, &output);
823
824 parcel_out.Write(output);
825 break;
826 }
827 case TransactionId::SetPreallocatedBuffer: {
828 const auto slot = parcel_in.Read<s32>();
829 const auto buffer = parcel_in.ReadObject<GraphicBuffer>();
830
831 status = SetPreallocatedBuffer(slot, buffer);
832 break;
833 }
834 case TransactionId::DequeueBuffer: {
835 const auto is_async = parcel_in.Read<bool>();
836 const auto width = parcel_in.Read<u32>();
837 const auto height = parcel_in.Read<u32>();
838 const auto pixel_format = parcel_in.Read<PixelFormat>();
839 const auto usage = parcel_in.Read<u32>();
840
841 s32 slot{};
842 Fence fence{};
843
844 status = DequeueBuffer(&slot, &fence, is_async, width, height, pixel_format, usage);
845
846 parcel_out.Write(slot);
847 parcel_out.WriteObject(&fence);
848 break;
849 }
850 case TransactionId::RequestBuffer: {
851 const auto slot = parcel_in.Read<s32>();
852
853 std::shared_ptr<GraphicBuffer> buf;
854
855 status = RequestBuffer(slot, &buf);
856
857 parcel_out.WriteObject(buf);
858 break;
859 }
860 case TransactionId::QueueBuffer: {
861 const auto slot = parcel_in.Read<s32>();
862
863 QueueBufferInput input{parcel_in};
864 QueueBufferOutput output;
865
866 status = QueueBuffer(slot, input, &output);
867
868 parcel_out.Write(output);
869 break;
870 }
871 case TransactionId::Query: {
872 const auto what = parcel_in.Read<NativeWindow>();
873
874 s32 value{};
875
876 status = Query(what, &value);
877
878 parcel_out.Write(value);
879 break;
880 }
881 case TransactionId::CancelBuffer: {
882 const auto slot = parcel_in.Read<s32>();
883 const auto fence = parcel_in.ReadFlattened<Fence>();
884
885 CancelBuffer(slot, fence);
886 break;
887 }
888 case TransactionId::Disconnect: {
889 const auto api = parcel_in.Read<NativeWindowApi>();
890
891 status = Disconnect(api);
892 break;
893 }
894 case TransactionId::DetachBuffer: {
895 const auto slot = parcel_in.Read<s32>();
896
897 status = DetachBuffer(slot);
898 break;
899 }
900 case TransactionId::SetBufferCount: {
901 const auto buffer_count = parcel_in.Read<s32>();
902
903 status = SetBufferCount(buffer_count);
904 break;
905 }
906 case TransactionId::GetBufferHistory:
907 LOG_WARNING(Service_NVFlinger, "(STUBBED) called, transaction=GetBufferHistory");
908 break;
909 default:
910 ASSERT_MSG(false, "Unimplemented TransactionId {}", code);
911 break;
912 }
913
914 parcel_out.Write(status);
915
916 ctx.WriteBuffer(parcel_out.Serialize());
917}
918
919Kernel::KReadableEvent& BufferQueueProducer::GetNativeHandle() {
920 return buffer_wait_event->GetReadableEvent();
921}
922
923} // namespace Service::android
diff --git a/src/core/hle/service/nvflinger/buffer_queue_producer.h b/src/core/hle/service/nvflinger/buffer_queue_producer.h
new file mode 100644
index 000000000..c4ca68fd3
--- /dev/null
+++ b/src/core/hle/service/nvflinger/buffer_queue_producer.h
@@ -0,0 +1,83 @@
1// SPDX-License-Identifier: GPL-3.0-or-later
2// Copyright 2021 yuzu Emulator Project
3// Copyright 2014 The Android Open Source Project
4// Parts of this implementation were base on:
5// https://cs.android.com/android/platform/superproject/+/android-5.1.1_r38:frameworks/native/include/gui/BufferQueueProducer.h
6
7#pragma once
8
9#include <condition_variable>
10#include <memory>
11#include <mutex>
12
13#include "common/common_funcs.h"
14#include "core/hle/service/nvdrv/nvdata.h"
15#include "core/hle/service/nvflinger/binder.h"
16#include "core/hle/service/nvflinger/buffer_queue_defs.h"
17#include "core/hle/service/nvflinger/buffer_slot.h"
18#include "core/hle/service/nvflinger/graphic_buffer_producer.h"
19#include "core/hle/service/nvflinger/pixel_format.h"
20#include "core/hle/service/nvflinger/status.h"
21#include "core/hle/service/nvflinger/window.h"
22
23namespace Kernel {
24class KernelCore;
25class KEvent;
26class KReadableEvent;
27class KWritableEvent;
28} // namespace Kernel
29
30namespace Service::KernelHelpers {
31class ServiceContext;
32} // namespace Service::KernelHelpers
33
34namespace Service::android {
35
36class BufferQueueCore;
37class IProducerListener;
38
39class BufferQueueProducer final : public IBinder {
40public:
41 explicit BufferQueueProducer(Service::KernelHelpers::ServiceContext& service_context_,
42 std::shared_ptr<BufferQueueCore> buffer_queue_core_);
43 ~BufferQueueProducer();
44
45 void Transact(Kernel::HLERequestContext& ctx, android::TransactionId code, u32 flags) override;
46
47 Kernel::KReadableEvent& GetNativeHandle() override;
48
49public:
50 Status RequestBuffer(s32 slot, std::shared_ptr<GraphicBuffer>* buf);
51 Status SetBufferCount(s32 buffer_count);
52 Status DequeueBuffer(s32* out_slot, android::Fence* out_fence, bool async, u32 width,
53 u32 height, PixelFormat format, u32 usage);
54 Status DetachBuffer(s32 slot);
55 Status DetachNextBuffer(std::shared_ptr<GraphicBuffer>* out_buffer, Fence* out_fence);
56 Status AttachBuffer(s32* outSlot, const std::shared_ptr<GraphicBuffer>& buffer);
57 Status QueueBuffer(s32 slot, const QueueBufferInput& input, QueueBufferOutput* output);
58 void CancelBuffer(s32 slot, const Fence& fence);
59 Status Query(NativeWindow what, s32* out_value);
60 Status Connect(const std::shared_ptr<IProducerListener>& listener, NativeWindowApi api,
61 bool producer_controlled_by_app, QueueBufferOutput* output);
62
63 Status Disconnect(NativeWindowApi api);
64 Status SetPreallocatedBuffer(s32 slot, const std::shared_ptr<GraphicBuffer>& buffer);
65
66private:
67 BufferQueueProducer(const BufferQueueProducer&) = delete;
68
69 Status WaitForFreeSlotThenRelock(bool async, s32* found, Status* return_flags) const;
70
71 Kernel::KEvent* buffer_wait_event{};
72 Service::KernelHelpers::ServiceContext& service_context;
73
74 std::shared_ptr<BufferQueueCore> core;
75 BufferQueueDefs::SlotsType& slots;
76 u32 sticky_transform{};
77 std::mutex callback_mutex;
78 s32 next_callback_ticket{};
79 s32 current_callback_ticket{};
80 std::condition_variable_any callback_condition;
81};
82
83} // namespace Service::android
diff --git a/src/core/hle/service/nvflinger/buffer_slot.h b/src/core/hle/service/nvflinger/buffer_slot.h
new file mode 100644
index 000000000..e3ea58910
--- /dev/null
+++ b/src/core/hle/service/nvflinger/buffer_slot.h
@@ -0,0 +1,39 @@
1// SPDX-License-Identifier: GPL-3.0-or-later
2// Copyright 2021 yuzu Emulator Project
3// Copyright 2014 The Android Open Source Project
4// Parts of this implementation were base on:
5// https://cs.android.com/android/platform/superproject/+/android-5.1.1_r38:frameworks/native/include/gui/BufferSlot.h
6
7#pragma once
8
9#include <memory>
10
11#include "common/common_types.h"
12#include "core/hle/service/nvflinger/ui/fence.h"
13
14namespace Service::android {
15
16class GraphicBuffer;
17
18enum class BufferState : u32 {
19 Free = 0,
20 Dequeued = 1,
21 Queued = 2,
22 Acquired = 3,
23};
24
25struct BufferSlot final {
26 constexpr BufferSlot() = default;
27
28 std::shared_ptr<GraphicBuffer> graphic_buffer;
29 BufferState buffer_state{BufferState::Free};
30 bool request_buffer_called{};
31 u64 frame_number{};
32 Fence fence;
33 bool acquire_called{};
34 bool needs_cleanup_on_release{};
35 bool attached_by_consumer{};
36 bool is_preallocated{};
37};
38
39} // namespace Service::android
diff --git a/src/core/hle/service/nvflinger/buffer_transform_flags.h b/src/core/hle/service/nvflinger/buffer_transform_flags.h
new file mode 100644
index 000000000..e8e6300e3
--- /dev/null
+++ b/src/core/hle/service/nvflinger/buffer_transform_flags.h
@@ -0,0 +1,25 @@
1// SPDX-License-Identifier: GPL-3.0-or-later
2// Copyright 2021 yuzu Emulator Project
3
4#pragma once
5
6#include "common/common_types.h"
7
8namespace Service::android {
9
10enum class BufferTransformFlags : u32 {
11 /// No transform flags are set
12 Unset = 0x00,
13 /// Flip source image horizontally (around the vertical axis)
14 FlipH = 0x01,
15 /// Flip source image vertically (around the horizontal axis)
16 FlipV = 0x02,
17 /// Rotate source image 90 degrees clockwise
18 Rotate90 = 0x04,
19 /// Rotate source image 180 degrees
20 Rotate180 = 0x03,
21 /// Rotate source image 270 degrees clockwise
22 Rotate270 = 0x07,
23};
24
25} // namespace Service::android
diff --git a/src/core/hle/service/nvflinger/consumer_base.cpp b/src/core/hle/service/nvflinger/consumer_base.cpp
new file mode 100644
index 000000000..30fc21acc
--- /dev/null
+++ b/src/core/hle/service/nvflinger/consumer_base.cpp
@@ -0,0 +1,133 @@
1// SPDX-License-Identifier: GPL-3.0-or-later
2// Copyright 2021 yuzu Emulator Project
3// Copyright 2010 The Android Open Source Project
4// Parts of this implementation were base on:
5// https://cs.android.com/android/platform/superproject/+/android-5.1.1_r38:frameworks/native/libs/gui/ConsumerBase.cpp
6
7#include "common/assert.h"
8#include "common/logging/log.h"
9#include "core/hle/service/nvflinger/buffer_item.h"
10#include "core/hle/service/nvflinger/buffer_queue_consumer.h"
11#include "core/hle/service/nvflinger/buffer_queue_core.h"
12#include "core/hle/service/nvflinger/consumer_base.h"
13#include "core/hle/service/nvflinger/ui/graphic_buffer.h"
14
15namespace Service::android {
16
17ConsumerBase::ConsumerBase(std::unique_ptr<BufferQueueConsumer> consumer_)
18 : consumer{std::move(consumer_)} {}
19
20ConsumerBase::~ConsumerBase() {
21 std::scoped_lock lock{mutex};
22
23 ASSERT_MSG(is_abandoned, "consumer is not abandoned!");
24}
25
26void ConsumerBase::Connect(bool controlled_by_app) {
27 consumer->Connect(shared_from_this(), controlled_by_app);
28}
29
30void ConsumerBase::FreeBufferLocked(s32 slot_index) {
31 LOG_DEBUG(Service_NVFlinger, "slot_index={}", slot_index);
32
33 slots[slot_index].graphic_buffer = nullptr;
34 slots[slot_index].fence = Fence::NoFence();
35 slots[slot_index].frame_number = 0;
36}
37
38void ConsumerBase::OnFrameAvailable(const BufferItem& item) {
39 LOG_DEBUG(Service_NVFlinger, "called");
40}
41
42void ConsumerBase::OnFrameReplaced(const BufferItem& item) {
43 LOG_DEBUG(Service_NVFlinger, "called");
44}
45
46void ConsumerBase::OnBuffersReleased() {
47 std::scoped_lock lock{mutex};
48
49 LOG_DEBUG(Service_NVFlinger, "called");
50
51 if (is_abandoned) {
52 // Nothing to do if we're already abandoned.
53 return;
54 }
55
56 u64 mask = 0;
57 consumer->GetReleasedBuffers(&mask);
58 for (int i = 0; i < BufferQueueDefs::NUM_BUFFER_SLOTS; i++) {
59 if (mask & (1ULL << i)) {
60 FreeBufferLocked(i);
61 }
62 }
63}
64
65void ConsumerBase::OnSidebandStreamChanged() {}
66
67Status ConsumerBase::AcquireBufferLocked(BufferItem* item, std::chrono::nanoseconds present_when) {
68 Status err = consumer->AcquireBuffer(item, present_when);
69 if (err != Status::NoError) {
70 return err;
71 }
72
73 if (item->graphic_buffer != nullptr) {
74 slots[item->slot].graphic_buffer = item->graphic_buffer;
75 }
76
77 slots[item->slot].frame_number = item->frame_number;
78 slots[item->slot].fence = item->fence;
79
80 LOG_DEBUG(Service_NVFlinger, "slot={}", item->slot);
81
82 return Status::NoError;
83}
84
85Status ConsumerBase::AddReleaseFenceLocked(s32 slot,
86 const std::shared_ptr<GraphicBuffer> graphic_buffer,
87 const Fence& fence) {
88 LOG_DEBUG(Service_NVFlinger, "slot={}", slot);
89
90 // If consumer no longer tracks this graphic_buffer, we can safely
91 // drop this fence, as it will never be received by the producer.
92
93 if (!StillTracking(slot, graphic_buffer)) {
94 return Status::NoError;
95 }
96
97 slots[slot].fence = fence;
98
99 return Status::NoError;
100}
101
102Status ConsumerBase::ReleaseBufferLocked(s32 slot,
103 const std::shared_ptr<GraphicBuffer> graphic_buffer) {
104 // If consumer no longer tracks this graphic_buffer (we received a new
105 // buffer on the same slot), the buffer producer is definitely no longer
106 // tracking it.
107
108 if (!StillTracking(slot, graphic_buffer)) {
109 return Status::NoError;
110 }
111
112 LOG_DEBUG(Service_NVFlinger, "slot={}", slot);
113 Status err = consumer->ReleaseBuffer(slot, slots[slot].frame_number, slots[slot].fence);
114 if (err == Status::StaleBufferSlot) {
115 FreeBufferLocked(slot);
116 }
117
118 slots[slot].fence = Fence::NoFence();
119
120 return err;
121}
122
123bool ConsumerBase::StillTracking(s32 slot,
124 const std::shared_ptr<GraphicBuffer> graphic_buffer) const {
125 if (slot < 0 || slot >= BufferQueueDefs::NUM_BUFFER_SLOTS) {
126 return false;
127 }
128
129 return (slots[slot].graphic_buffer != nullptr &&
130 slots[slot].graphic_buffer->Handle() == graphic_buffer->Handle());
131}
132
133} // namespace Service::android
diff --git a/src/core/hle/service/nvflinger/consumer_base.h b/src/core/hle/service/nvflinger/consumer_base.h
new file mode 100644
index 000000000..736080e3a
--- /dev/null
+++ b/src/core/hle/service/nvflinger/consumer_base.h
@@ -0,0 +1,60 @@
1// SPDX-License-Identifier: GPL-3.0-or-later
2// Copyright 2021 yuzu Emulator Project
3// Copyright 2010 The Android Open Source Project
4// Parts of this implementation were base on:
5// https://cs.android.com/android/platform/superproject/+/android-5.1.1_r38:frameworks/native/include/gui/ConsumerBase.h
6
7#pragma once
8
9#include <array>
10#include <chrono>
11#include <memory>
12#include <mutex>
13
14#include "common/common_types.h"
15#include "core/hle/service/nvflinger/buffer_queue_defs.h"
16#include "core/hle/service/nvflinger/consumer_listener.h"
17#include "core/hle/service/nvflinger/status.h"
18
19namespace Service::android {
20
21class BufferItem;
22class BufferQueueConsumer;
23
24class ConsumerBase : public IConsumerListener, public std::enable_shared_from_this<ConsumerBase> {
25public:
26 void Connect(bool controlled_by_app);
27
28protected:
29 explicit ConsumerBase(std::unique_ptr<BufferQueueConsumer> consumer_);
30 virtual ~ConsumerBase();
31
32 virtual void OnFrameAvailable(const BufferItem& item) override;
33 virtual void OnFrameReplaced(const BufferItem& item) override;
34 virtual void OnBuffersReleased() override;
35 virtual void OnSidebandStreamChanged() override;
36
37 void FreeBufferLocked(s32 slot_index);
38 Status AcquireBufferLocked(BufferItem* item, std::chrono::nanoseconds present_when);
39 Status ReleaseBufferLocked(s32 slot, const std::shared_ptr<GraphicBuffer> graphic_buffer);
40 bool StillTracking(s32 slot, const std::shared_ptr<GraphicBuffer> graphic_buffer) const;
41 Status AddReleaseFenceLocked(s32 slot, const std::shared_ptr<GraphicBuffer> graphic_buffer,
42 const Fence& fence);
43
44 struct Slot final {
45 std::shared_ptr<GraphicBuffer> graphic_buffer;
46 Fence fence;
47 u64 frame_number{};
48 };
49
50protected:
51 std::array<Slot, BufferQueueDefs::NUM_BUFFER_SLOTS> slots;
52
53 bool is_abandoned{};
54
55 std::unique_ptr<BufferQueueConsumer> consumer;
56
57 mutable std::mutex mutex;
58};
59
60} // namespace Service::android
diff --git a/src/core/hle/service/nvflinger/consumer_listener.h b/src/core/hle/service/nvflinger/consumer_listener.h
new file mode 100644
index 000000000..b6d1c3e9a
--- /dev/null
+++ b/src/core/hle/service/nvflinger/consumer_listener.h
@@ -0,0 +1,26 @@
1// SPDX-License-Identifier: GPL-3.0-or-later
2// Copyright 2021 yuzu Emulator Project
3// Copyright 2014 The Android Open Source Project
4// Parts of this implementation were base on:
5// https://cs.android.com/android/platform/superproject/+/android-5.1.1_r38:frameworks/native/include/gui/IConsumerListener.h
6
7#pragma once
8
9namespace Service::android {
10
11class BufferItem;
12
13/// ConsumerListener is the interface through which the BufferQueue notifies the consumer of events
14/// that the consumer may wish to react to.
15class IConsumerListener {
16public:
17 IConsumerListener() = default;
18 virtual ~IConsumerListener() = default;
19
20 virtual void OnFrameAvailable(const BufferItem& item) = 0;
21 virtual void OnFrameReplaced(const BufferItem& item) = 0;
22 virtual void OnBuffersReleased() = 0;
23 virtual void OnSidebandStreamChanged() = 0;
24};
25
26}; // namespace Service::android
diff --git a/src/core/hle/service/nvflinger/graphic_buffer_producer.cpp b/src/core/hle/service/nvflinger/graphic_buffer_producer.cpp
new file mode 100644
index 000000000..04068827e
--- /dev/null
+++ b/src/core/hle/service/nvflinger/graphic_buffer_producer.cpp
@@ -0,0 +1,18 @@
1// SPDX-License-Identifier: GPL-3.0-or-later
2// Copyright 2021 yuzu Emulator Project
3// Copyright 2010 The Android Open Source Project
4// Parts of this implementation were base on:
5// https://cs.android.com/android/platform/superproject/+/android-5.1.1_r38:frameworks/native/libs/gui/IGraphicBufferProducer.cpp
6
7#include "core/hle/service/nvflinger/graphic_buffer_producer.h"
8#include "core/hle/service/nvflinger/parcel.h"
9
10namespace Service::android {
11
12QueueBufferInput::QueueBufferInput(Parcel& parcel) {
13 parcel.ReadFlattened(*this);
14}
15
16QueueBufferOutput::QueueBufferOutput() = default;
17
18} // namespace Service::android
diff --git a/src/core/hle/service/nvflinger/graphic_buffer_producer.h b/src/core/hle/service/nvflinger/graphic_buffer_producer.h
new file mode 100644
index 000000000..98d27871c
--- /dev/null
+++ b/src/core/hle/service/nvflinger/graphic_buffer_producer.h
@@ -0,0 +1,76 @@
1// SPDX-License-Identifier: GPL-3.0-or-later
2// Copyright 2021 yuzu Emulator Project
3// Copyright 2010 The Android Open Source Project
4// Parts of this implementation were base on:
5// https://cs.android.com/android/platform/superproject/+/android-5.1.1_r38:frameworks/native/include/gui/IGraphicBufferProducer.h
6
7#pragma once
8
9#include "common/common_funcs.h"
10#include "common/common_types.h"
11#include "common/math_util.h"
12#include "core/hle/service/nvflinger/ui/fence.h"
13#include "core/hle/service/nvflinger/window.h"
14
15namespace Service::android {
16
17class Parcel;
18
19#pragma pack(push, 1)
20struct QueueBufferInput final {
21 explicit QueueBufferInput(Parcel& parcel);
22
23 void Deflate(s64* timestamp_, bool* is_auto_timestamp_, Common::Rectangle<s32>* crop_,
24 NativeWindowScalingMode* scaling_mode_, NativeWindowTransform* transform_,
25 u32* sticky_transform_, bool* async_, s32* swap_interval_, Fence* fence_) const {
26 *timestamp_ = timestamp;
27 *is_auto_timestamp_ = static_cast<bool>(is_auto_timestamp);
28 *crop_ = crop;
29 *scaling_mode_ = scaling_mode;
30 *transform_ = transform;
31 *sticky_transform_ = sticky_transform;
32 *async_ = static_cast<bool>(async);
33 *swap_interval_ = swap_interval;
34 *fence_ = fence;
35 }
36
37private:
38 s64 timestamp{};
39 s32 is_auto_timestamp{};
40 Common::Rectangle<s32> crop{};
41 NativeWindowScalingMode scaling_mode{};
42 NativeWindowTransform transform{};
43 u32 sticky_transform{};
44 s32 async{};
45 s32 swap_interval{};
46 Fence fence{};
47};
48#pragma pack(pop)
49static_assert(sizeof(QueueBufferInput) == 84, "QueueBufferInput has wrong size");
50
51struct QueueBufferOutput final {
52 QueueBufferOutput();
53
54 void Deflate(u32* width_, u32* height_, u32* transform_hint_, u32* num_pending_buffers_) const {
55 *width_ = width;
56 *height_ = height;
57 *transform_hint_ = transform_hint;
58 *num_pending_buffers_ = num_pending_buffers;
59 }
60
61 void Inflate(u32 width_, u32 height_, u32 transform_hint_, u32 num_pending_buffers_) {
62 width = width_;
63 height = height_;
64 transform_hint = transform_hint_;
65 num_pending_buffers = num_pending_buffers_;
66 }
67
68private:
69 u32 width{};
70 u32 height{};
71 u32 transform_hint{};
72 u32 num_pending_buffers{};
73};
74static_assert(sizeof(QueueBufferOutput) == 16, "QueueBufferOutput has wrong size");
75
76} // namespace Service::android
diff --git a/src/core/hle/service/nvflinger/hos_binder_driver_server.cpp b/src/core/hle/service/nvflinger/hos_binder_driver_server.cpp
new file mode 100644
index 000000000..094ba2542
--- /dev/null
+++ b/src/core/hle/service/nvflinger/hos_binder_driver_server.cpp
@@ -0,0 +1,36 @@
1// SPDX-License-Identifier: GPL-3.0-or-later
2// Copyright 2021 yuzu Emulator Project
3
4#include <mutex>
5
6#include "common/common_types.h"
7#include "core/hle/service/nvflinger/hos_binder_driver_server.h"
8
9namespace Service::NVFlinger {
10
11HosBinderDriverServer::HosBinderDriverServer(Core::System& system_)
12 : service_context(system_, "HosBinderDriverServer") {}
13
14HosBinderDriverServer::~HosBinderDriverServer() {}
15
16u64 HosBinderDriverServer::RegisterProducer(std::unique_ptr<android::IBinder>&& binder) {
17 std::scoped_lock lk{lock};
18
19 last_id++;
20
21 producers[last_id] = std::move(binder);
22
23 return last_id;
24}
25
26android::IBinder* HosBinderDriverServer::TryGetProducer(u64 id) {
27 std::scoped_lock lk{lock};
28
29 if (auto search = producers.find(id); search != producers.end()) {
30 return search->second.get();
31 }
32
33 return {};
34}
35
36} // namespace Service::NVFlinger
diff --git a/src/core/hle/service/nvflinger/hos_binder_driver_server.h b/src/core/hle/service/nvflinger/hos_binder_driver_server.h
new file mode 100644
index 000000000..cbca87fa0
--- /dev/null
+++ b/src/core/hle/service/nvflinger/hos_binder_driver_server.h
@@ -0,0 +1,37 @@
1// SPDX-License-Identifier: GPL-3.0-or-later
2// Copyright 2021 yuzu Emulator Project
3
4#pragma once
5
6#include <memory>
7#include <mutex>
8#include <unordered_map>
9
10#include "common/common_types.h"
11#include "core/hle/service/kernel_helpers.h"
12#include "core/hle/service/nvflinger/binder.h"
13
14namespace Core {
15class System;
16}
17
18namespace Service::NVFlinger {
19
20class HosBinderDriverServer final {
21public:
22 explicit HosBinderDriverServer(Core::System& system_);
23 ~HosBinderDriverServer();
24
25 u64 RegisterProducer(std::unique_ptr<android::IBinder>&& binder);
26
27 android::IBinder* TryGetProducer(u64 id);
28
29private:
30 KernelHelpers::ServiceContext service_context;
31
32 std::unordered_map<u64, std::unique_ptr<android::IBinder>> producers;
33 std::mutex lock;
34 u64 last_id{};
35};
36
37} // namespace Service::NVFlinger
diff --git a/src/core/hle/service/nvflinger/nvflinger.cpp b/src/core/hle/service/nvflinger/nvflinger.cpp
index 01e69de30..6fb2cdff1 100644
--- a/src/core/hle/service/nvflinger/nvflinger.cpp
+++ b/src/core/hle/service/nvflinger/nvflinger.cpp
@@ -1,6 +1,5 @@
1// Copyright 2018 yuzu emulator team 1// SPDX-License-Identifier: GPL-3.0-or-later
2// Licensed under GPLv2 or any later version 2// Copyright 2021 yuzu Emulator Project
3// Refer to the license.txt file included.
4 3
5#include <algorithm> 4#include <algorithm>
6#include <optional> 5#include <optional>
@@ -16,8 +15,11 @@
16#include "core/hle/kernel/k_readable_event.h" 15#include "core/hle/kernel/k_readable_event.h"
17#include "core/hle/service/nvdrv/devices/nvdisp_disp0.h" 16#include "core/hle/service/nvdrv/devices/nvdisp_disp0.h"
18#include "core/hle/service/nvdrv/nvdrv.h" 17#include "core/hle/service/nvdrv/nvdrv.h"
19#include "core/hle/service/nvflinger/buffer_queue.h" 18#include "core/hle/service/nvflinger/buffer_item_consumer.h"
19#include "core/hle/service/nvflinger/buffer_queue_core.h"
20#include "core/hle/service/nvflinger/hos_binder_driver_server.h"
20#include "core/hle/service/nvflinger/nvflinger.h" 21#include "core/hle/service/nvflinger/nvflinger.h"
22#include "core/hle/service/nvflinger/ui/graphic_buffer.h"
21#include "core/hle/service/vi/display/vi_display.h" 23#include "core/hle/service/vi/display/vi_display.h"
22#include "core/hle/service/vi/layer/vi_layer.h" 24#include "core/hle/service/vi/layer/vi_layer.h"
23#include "video_core/gpu.h" 25#include "video_core/gpu.h"
@@ -53,13 +55,14 @@ void NVFlinger::SplitVSync(std::stop_token stop_token) {
53 } 55 }
54} 56}
55 57
56NVFlinger::NVFlinger(Core::System& system_) 58NVFlinger::NVFlinger(Core::System& system_, HosBinderDriverServer& hos_binder_driver_server_)
57 : system(system_), service_context(system_, "nvflinger") { 59 : system(system_), service_context(system_, "nvflinger"),
58 displays.emplace_back(0, "Default", service_context, system); 60 hos_binder_driver_server(hos_binder_driver_server_) {
59 displays.emplace_back(1, "External", service_context, system); 61 displays.emplace_back(0, "Default", hos_binder_driver_server, service_context, system);
60 displays.emplace_back(2, "Edid", service_context, system); 62 displays.emplace_back(1, "External", hos_binder_driver_server, service_context, system);
61 displays.emplace_back(3, "Internal", service_context, system); 63 displays.emplace_back(2, "Edid", hos_binder_driver_server, service_context, system);
62 displays.emplace_back(4, "Null", service_context, system); 64 displays.emplace_back(3, "Internal", hos_binder_driver_server, service_context, system);
65 displays.emplace_back(4, "Null", hos_binder_driver_server, service_context, system);
63 guard = std::make_shared<std::mutex>(); 66 guard = std::make_shared<std::mutex>();
64 67
65 // Schedule the screen composition events 68 // Schedule the screen composition events
@@ -83,12 +86,15 @@ NVFlinger::NVFlinger(Core::System& system_)
83} 86}
84 87
85NVFlinger::~NVFlinger() { 88NVFlinger::~NVFlinger() {
86 for (auto& buffer_queue : buffer_queues) {
87 buffer_queue->Disconnect();
88 }
89 if (!system.IsMulticore()) { 89 if (!system.IsMulticore()) {
90 system.CoreTiming().UnscheduleEvent(composition_event, 0); 90 system.CoreTiming().UnscheduleEvent(composition_event, 0);
91 } 91 }
92
93 for (auto& display : displays) {
94 for (size_t layer = 0; layer < display.GetNumLayers(); ++layer) {
95 display.GetLayer(layer).Core().NotifyShutdown();
96 }
97 }
92} 98}
93 99
94void NVFlinger::SetNVDrvInstance(std::shared_ptr<Nvidia::Module> instance) { 100void NVFlinger::SetNVDrvInstance(std::shared_ptr<Nvidia::Module> instance) {
@@ -98,7 +104,7 @@ void NVFlinger::SetNVDrvInstance(std::shared_ptr<Nvidia::Module> instance) {
98std::optional<u64> NVFlinger::OpenDisplay(std::string_view name) { 104std::optional<u64> NVFlinger::OpenDisplay(std::string_view name) {
99 const auto lock_guard = Lock(); 105 const auto lock_guard = Lock();
100 106
101 LOG_DEBUG(Service, "Opening \"{}\" display", name); 107 LOG_DEBUG(Service_NVFlinger, "Opening \"{}\" display", name);
102 108
103 const auto itr = 109 const auto itr =
104 std::find_if(displays.begin(), displays.end(), 110 std::find_if(displays.begin(), displays.end(),
@@ -125,10 +131,8 @@ std::optional<u64> NVFlinger::CreateLayer(u64 display_id) {
125} 131}
126 132
127void NVFlinger::CreateLayerAtId(VI::Display& display, u64 layer_id) { 133void NVFlinger::CreateLayerAtId(VI::Display& display, u64 layer_id) {
128 const u32 buffer_queue_id = next_buffer_queue_id++; 134 const auto buffer_id = next_buffer_queue_id++;
129 buffer_queues.emplace_back( 135 display.CreateLayer(layer_id, buffer_id);
130 std::make_unique<BufferQueue>(system.Kernel(), buffer_queue_id, layer_id, service_context));
131 display.CreateLayer(layer_id, *buffer_queues.back());
132} 136}
133 137
134void NVFlinger::CloseLayer(u64 layer_id) { 138void NVFlinger::CloseLayer(u64 layer_id) {
@@ -147,7 +151,7 @@ std::optional<u32> NVFlinger::FindBufferQueueId(u64 display_id, u64 layer_id) {
147 return std::nullopt; 151 return std::nullopt;
148 } 152 }
149 153
150 return layer->GetBufferQueue().GetId(); 154 return layer->GetBinderId();
151} 155}
152 156
153Kernel::KReadableEvent* NVFlinger::FindVsyncEvent(u64 display_id) { 157Kernel::KReadableEvent* NVFlinger::FindVsyncEvent(u64 display_id) {
@@ -161,18 +165,6 @@ Kernel::KReadableEvent* NVFlinger::FindVsyncEvent(u64 display_id) {
161 return &display->GetVSyncEvent(); 165 return &display->GetVSyncEvent();
162} 166}
163 167
164BufferQueue* NVFlinger::FindBufferQueue(u32 id) {
165 const auto lock_guard = Lock();
166 const auto itr = std::find_if(buffer_queues.begin(), buffer_queues.end(),
167 [id](const auto& queue) { return queue->GetId() == id; });
168
169 if (itr == buffer_queues.end()) {
170 return nullptr;
171 }
172
173 return itr->get();
174}
175
176VI::Display* NVFlinger::FindDisplay(u64 display_id) { 168VI::Display* NVFlinger::FindDisplay(u64 display_id) {
177 const auto itr = 169 const auto itr =
178 std::find_if(displays.begin(), displays.end(), 170 std::find_if(displays.begin(), displays.end(),
@@ -227,7 +219,7 @@ VI::Layer* NVFlinger::FindOrCreateLayer(u64 display_id, u64 layer_id) {
227 auto* layer = display->FindLayer(layer_id); 219 auto* layer = display->FindLayer(layer_id);
228 220
229 if (layer == nullptr) { 221 if (layer == nullptr) {
230 LOG_DEBUG(Service, "Layer at id {} not found. Trying to create it.", layer_id); 222 LOG_DEBUG(Service_NVFlinger, "Layer at id {} not found. Trying to create it.", layer_id);
231 CreateLayerAtId(*display, layer_id); 223 CreateLayerAtId(*display, layer_id);
232 return display->FindLayer(layer_id); 224 return display->FindLayer(layer_id);
233 } 225 }
@@ -246,23 +238,22 @@ void NVFlinger::Compose() {
246 238
247 // TODO(Subv): Support more than 1 layer. 239 // TODO(Subv): Support more than 1 layer.
248 VI::Layer& layer = display.GetLayer(0); 240 VI::Layer& layer = display.GetLayer(0);
249 auto& buffer_queue = layer.GetBufferQueue();
250 241
251 // Search for a queued buffer and acquire it 242 android::BufferItem buffer{};
252 auto buffer = buffer_queue.AcquireBuffer(); 243 const auto status = layer.GetConsumer().AcquireBuffer(&buffer, {}, false);
253 244
254 if (!buffer) { 245 if (status != android::Status::NoError) {
255 continue; 246 continue;
256 } 247 }
257 248
258 const auto& igbp_buffer = buffer->get().igbp_buffer; 249 const auto& igbp_buffer = *buffer.graphic_buffer;
259 250
260 if (!system.IsPoweredOn()) { 251 if (!system.IsPoweredOn()) {
261 return; // We are likely shutting down 252 return; // We are likely shutting down
262 } 253 }
263 254
264 auto& gpu = system.GPU(); 255 auto& gpu = system.GPU();
265 const auto& multi_fence = buffer->get().multi_fence; 256 const auto& multi_fence = buffer.fence;
266 guard->unlock(); 257 guard->unlock();
267 for (u32 fence_id = 0; fence_id < multi_fence.num_fences; fence_id++) { 258 for (u32 fence_id = 0; fence_id < multi_fence.num_fences; fence_id++) {
268 const auto& fence = multi_fence.fences[fence_id]; 259 const auto& fence = multi_fence.fences[fence_id];
@@ -278,12 +269,18 @@ void NVFlinger::Compose() {
278 auto nvdisp = nvdrv->GetDevice<Nvidia::Devices::nvdisp_disp0>("/dev/nvdisp_disp0"); 269 auto nvdisp = nvdrv->GetDevice<Nvidia::Devices::nvdisp_disp0>("/dev/nvdisp_disp0");
279 ASSERT(nvdisp); 270 ASSERT(nvdisp);
280 271
281 nvdisp->flip(igbp_buffer.gpu_buffer_id, igbp_buffer.offset, igbp_buffer.external_format, 272 Common::Rectangle<int> crop_rect{
282 igbp_buffer.width, igbp_buffer.height, igbp_buffer.stride, 273 static_cast<int>(buffer.crop.Left()), static_cast<int>(buffer.crop.Top()),
283 buffer->get().transform, buffer->get().crop_rect); 274 static_cast<int>(buffer.crop.Right()), static_cast<int>(buffer.crop.Bottom())};
275
276 nvdisp->flip(igbp_buffer.BufferId(), igbp_buffer.Offset(), igbp_buffer.ExternalFormat(),
277 igbp_buffer.Width(), igbp_buffer.Height(), igbp_buffer.Stride(),
278 static_cast<android::BufferTransformFlags>(buffer.transform), crop_rect);
279
280 swap_interval = buffer.swap_interval;
284 281
285 swap_interval = buffer->get().swap_interval; 282 auto fence = android::Fence::NoFence();
286 buffer_queue.ReleaseBuffer(buffer->get().slot); 283 layer.GetConsumer().ReleaseBuffer(buffer, fence);
287 } 284 }
288} 285}
289 286
diff --git a/src/core/hle/service/nvflinger/nvflinger.h b/src/core/hle/service/nvflinger/nvflinger.h
index 7935cf773..ed160f6f9 100644
--- a/src/core/hle/service/nvflinger/nvflinger.h
+++ b/src/core/hle/service/nvflinger/nvflinger.h
@@ -1,6 +1,5 @@
1// Copyright 2018 yuzu emulator team 1// SPDX-License-Identifier: GPL-3.0-or-later
2// Licensed under GPLv2 or any later version 2// Copyright 2021 yuzu Emulator Project
3// Refer to the license.txt file included.
4 3
5#pragma once 4#pragma once
6 5
@@ -37,13 +36,16 @@ class Display;
37class Layer; 36class Layer;
38} // namespace Service::VI 37} // namespace Service::VI
39 38
40namespace Service::NVFlinger { 39namespace Service::android {
40class BufferQueueCore;
41class BufferQueueProducer;
42} // namespace Service::android
41 43
42class BufferQueue; 44namespace Service::NVFlinger {
43 45
44class NVFlinger final { 46class NVFlinger final {
45public: 47public:
46 explicit NVFlinger(Core::System& system_); 48 explicit NVFlinger(Core::System& system_, HosBinderDriverServer& hos_binder_driver_server_);
47 ~NVFlinger(); 49 ~NVFlinger();
48 50
49 /// Sets the NVDrv module instance to use to send buffers to the GPU. 51 /// Sets the NVDrv module instance to use to send buffers to the GPU.
@@ -72,9 +74,6 @@ public:
72 /// If an invalid display ID is provided, then nullptr is returned. 74 /// If an invalid display ID is provided, then nullptr is returned.
73 [[nodiscard]] Kernel::KReadableEvent* FindVsyncEvent(u64 display_id); 75 [[nodiscard]] Kernel::KReadableEvent* FindVsyncEvent(u64 display_id);
74 76
75 /// Obtains a buffer queue identified by the ID.
76 [[nodiscard]] BufferQueue* FindBufferQueue(u32 id);
77
78 /// Performs a composition request to the emulated nvidia GPU and triggers the vsync events when 77 /// Performs a composition request to the emulated nvidia GPU and triggers the vsync events when
79 /// finished. 78 /// finished.
80 void Compose(); 79 void Compose();
@@ -82,6 +81,12 @@ public:
82 [[nodiscard]] s64 GetNextTicks() const; 81 [[nodiscard]] s64 GetNextTicks() const;
83 82
84private: 83private:
84 struct Layer {
85 std::unique_ptr<android::BufferQueueCore> core;
86 std::unique_ptr<android::BufferQueueProducer> producer;
87 };
88
89private:
85 [[nodiscard]] std::unique_lock<std::mutex> Lock() const { 90 [[nodiscard]] std::unique_lock<std::mutex> Lock() const {
86 return std::unique_lock{*guard}; 91 return std::unique_lock{*guard};
87 } 92 }
@@ -111,7 +116,6 @@ private:
111 std::shared_ptr<Nvidia::Module> nvdrv; 116 std::shared_ptr<Nvidia::Module> nvdrv;
112 117
113 std::list<VI::Display> displays; 118 std::list<VI::Display> displays;
114 std::vector<std::unique_ptr<BufferQueue>> buffer_queues;
115 119
116 /// Id to use for the next layer that is created, this counter is shared among all displays. 120 /// Id to use for the next layer that is created, this counter is shared among all displays.
117 u64 next_layer_id = 1; 121 u64 next_layer_id = 1;
@@ -131,6 +135,8 @@ private:
131 std::jthread vsync_thread; 135 std::jthread vsync_thread;
132 136
133 KernelHelpers::ServiceContext service_context; 137 KernelHelpers::ServiceContext service_context;
138
139 HosBinderDriverServer& hos_binder_driver_server;
134}; 140};
135 141
136} // namespace Service::NVFlinger 142} // namespace Service::NVFlinger
diff --git a/src/core/hle/service/nvflinger/parcel.h b/src/core/hle/service/nvflinger/parcel.h
new file mode 100644
index 000000000..aa36e6479
--- /dev/null
+++ b/src/core/hle/service/nvflinger/parcel.h
@@ -0,0 +1,172 @@
1// SPDX-License-Identifier: GPL-3.0-or-later
2// Copyright 2021 yuzu Emulator Project
3
4#pragma once
5
6#include <memory>
7#include <vector>
8
9#include "common/alignment.h"
10#include "common/assert.h"
11#include "common/common_types.h"
12
13namespace Service::android {
14
15class Parcel final {
16public:
17 static constexpr std::size_t DefaultBufferSize = 0x40;
18
19 Parcel() : buffer(DefaultBufferSize) {}
20
21 template <typename T>
22 explicit Parcel(const T& out_data) : buffer(DefaultBufferSize) {
23 Write(out_data);
24 }
25
26 explicit Parcel(std::vector<u8> in_data) : buffer(std::move(in_data)) {
27 DeserializeHeader();
28 [[maybe_unused]] const std::u16string token = ReadInterfaceToken();
29 }
30
31 template <typename T>
32 void Read(T& val) {
33 static_assert(std::is_trivially_copyable_v<T>, "T must be trivially copyable.");
34 ASSERT(read_index + sizeof(T) <= buffer.size());
35
36 std::memcpy(&val, buffer.data() + read_index, sizeof(T));
37 read_index += sizeof(T);
38 read_index = Common::AlignUp(read_index, 4);
39 }
40
41 template <typename T>
42 T Read() {
43 T val;
44 Read(val);
45 return val;
46 }
47
48 template <typename T>
49 void ReadFlattened(T& val) {
50 const auto flattened_size = Read<s64>();
51 ASSERT(sizeof(T) == flattened_size);
52 Read(val);
53 }
54
55 template <typename T>
56 T ReadFlattened() {
57 T val;
58 ReadFlattened(val);
59 return val;
60 }
61
62 template <typename T>
63 T ReadUnaligned() {
64 static_assert(std::is_trivially_copyable_v<T>, "T must be trivially copyable.");
65 ASSERT(read_index + sizeof(T) <= buffer.size());
66
67 T val;
68 std::memcpy(&val, buffer.data() + read_index, sizeof(T));
69 read_index += sizeof(T);
70 return val;
71 }
72
73 template <typename T>
74 const std::shared_ptr<T> ReadObject() {
75 static_assert(std::is_trivially_copyable_v<T>, "T must be trivially copyable.");
76
77 const auto is_valid{Read<bool>()};
78
79 if (is_valid) {
80 auto result = std::make_shared<T>();
81 ReadFlattened(*result);
82 return result;
83 }
84
85 return {};
86 }
87
88 std::u16string ReadInterfaceToken() {
89 [[maybe_unused]] const u32 unknown = Read<u32>();
90 const u32 length = Read<u32>();
91
92 std::u16string token;
93 token.reserve(length + 1);
94
95 for (u32 ch = 0; ch < length + 1; ++ch) {
96 token.push_back(ReadUnaligned<u16>());
97 }
98
99 read_index = Common::AlignUp(read_index, 4);
100
101 return token;
102 }
103
104 template <typename T>
105 void Write(const T& val) {
106 static_assert(std::is_trivially_copyable_v<T>, "T must be trivially copyable.");
107
108 if (buffer.size() < write_index + sizeof(T)) {
109 buffer.resize(buffer.size() + sizeof(T) + DefaultBufferSize);
110 }
111
112 std::memcpy(buffer.data() + write_index, &val, sizeof(T));
113 write_index += sizeof(T);
114 write_index = Common::AlignUp(write_index, 4);
115 }
116
117 template <typename T>
118 void WriteObject(const T* ptr) {
119 static_assert(std::is_trivially_copyable_v<T>, "T must be trivially copyable.");
120
121 if (!ptr) {
122 Write<u32>(0);
123 return;
124 }
125
126 Write<u32>(1);
127 Write<s64>(sizeof(T));
128 Write(*ptr);
129 }
130
131 template <typename T>
132 void WriteObject(const std::shared_ptr<T> ptr) {
133 WriteObject(ptr.get());
134 }
135
136 void DeserializeHeader() {
137 ASSERT(buffer.size() > sizeof(Header));
138
139 Header header{};
140 std::memcpy(&header, buffer.data(), sizeof(Header));
141
142 read_index = header.data_offset;
143 }
144
145 std::vector<u8> Serialize() const {
146 ASSERT(read_index == 0);
147
148 Header header{};
149 header.data_size = static_cast<u32>(write_index - sizeof(Header));
150 header.data_offset = sizeof(Header);
151 header.objects_size = 4;
152 header.objects_offset = static_cast<u32>(sizeof(Header) + header.data_size);
153 std::memcpy(buffer.data(), &header, sizeof(Header));
154
155 return buffer;
156 }
157
158private:
159 struct Header {
160 u32 data_size;
161 u32 data_offset;
162 u32 objects_size;
163 u32 objects_offset;
164 };
165 static_assert(sizeof(Header) == 16, "ParcelHeader has wrong size");
166
167 mutable std::vector<u8> buffer;
168 std::size_t read_index = 0;
169 std::size_t write_index = sizeof(Header);
170};
171
172} // namespace Service::android
diff --git a/src/core/hle/service/nvflinger/pixel_format.h b/src/core/hle/service/nvflinger/pixel_format.h
new file mode 100644
index 000000000..8a77d8bea
--- /dev/null
+++ b/src/core/hle/service/nvflinger/pixel_format.h
@@ -0,0 +1,21 @@
1// SPDX-License-Identifier: GPL-3.0-or-later
2// Copyright 2021 yuzu Emulator Project
3
4#pragma once
5
6#include "common/common_types.h"
7
8namespace Service::android {
9
10enum class PixelFormat : u32 {
11 NoFormat = 0,
12 Rgba8888 = 1,
13 Rgbx8888 = 2,
14 Rgb888 = 3,
15 Rgb565 = 4,
16 Bgra8888 = 5,
17 Rgba5551 = 6,
18 Rgba4444 = 7,
19};
20
21} // namespace Service::android
diff --git a/src/core/hle/service/nvflinger/producer_listener.h b/src/core/hle/service/nvflinger/producer_listener.h
new file mode 100644
index 000000000..468e06431
--- /dev/null
+++ b/src/core/hle/service/nvflinger/producer_listener.h
@@ -0,0 +1,16 @@
1// SPDX-License-Identifier: GPL-3.0-or-later
2// Copyright 2021 yuzu Emulator Project
3// Copyright 2014 The Android Open Source Project
4// Parts of this implementation were base on:
5// https://cs.android.com/android/platform/superproject/+/android-5.1.1_r38:frameworks/native/include/gui/IProducerListener.h
6
7#pragma once
8
9namespace Service::android {
10
11class IProducerListener {
12public:
13 virtual void OnBufferReleased() = 0;
14};
15
16} // namespace Service::android
diff --git a/src/core/hle/service/nvflinger/status.h b/src/core/hle/service/nvflinger/status.h
new file mode 100644
index 000000000..a003eda89
--- /dev/null
+++ b/src/core/hle/service/nvflinger/status.h
@@ -0,0 +1,28 @@
1// SPDX-License-Identifier: GPL-3.0-or-later
2// Copyright 2021 yuzu Emulator Project
3
4#pragma once
5
6#include "common/common_funcs.h"
7#include "common/common_types.h"
8
9namespace Service::android {
10
11enum class Status : s32 {
12 None = 0,
13 NoError = 0,
14 StaleBufferSlot = 1,
15 NoBufferAvailable = 2,
16 PresentLater = 3,
17 WouldBlock = -11,
18 NoMemory = -12,
19 Busy = -16,
20 NoInit = -19,
21 BadValue = -22,
22 InvalidOperation = -37,
23 BufferNeedsReallocation = 1,
24 ReleaseAllBuffers = 2,
25};
26DECLARE_ENUM_FLAG_OPERATORS(Status);
27
28} // namespace Service::android
diff --git a/src/core/hle/service/nvflinger/ui/fence.h b/src/core/hle/service/nvflinger/ui/fence.h
new file mode 100644
index 000000000..4a74e26a3
--- /dev/null
+++ b/src/core/hle/service/nvflinger/ui/fence.h
@@ -0,0 +1,32 @@
1// SPDX-License-Identifier: GPL-3.0-or-later
2// Copyright 2021 yuzu Emulator Project
3// Copyright 2012 The Android Open Source Project
4// Parts of this implementation were base on:
5// https://cs.android.com/android/platform/superproject/+/android-5.1.1_r38:frameworks/native/include/ui/Fence.h
6
7#pragma once
8
9#include <array>
10
11#include "common/common_types.h"
12#include "core/hle/service/nvdrv/nvdata.h"
13
14namespace Service::android {
15
16class Fence {
17public:
18 constexpr Fence() = default;
19
20 static constexpr Fence NoFence() {
21 Fence fence;
22 fence.fences[0].id = -1;
23 return fence;
24 }
25
26public:
27 u32 num_fences{};
28 std::array<Service::Nvidia::NvFence, 4> fences{};
29};
30static_assert(sizeof(Fence) == 36, "Fence has wrong size");
31
32} // namespace Service::android
diff --git a/src/core/hle/service/nvflinger/ui/graphic_buffer.h b/src/core/hle/service/nvflinger/ui/graphic_buffer.h
new file mode 100644
index 000000000..7abbf78ba
--- /dev/null
+++ b/src/core/hle/service/nvflinger/ui/graphic_buffer.h
@@ -0,0 +1,100 @@
1// SPDX-License-Identifier: GPL-3.0-or-later
2// Copyright 2021 yuzu Emulator Project
3// Copyright 2007 The Android Open Source Project
4// Parts of this implementation were base on:
5// https://cs.android.com/android/platform/superproject/+/android-5.1.1_r38:frameworks/native/include/ui/GraphicBuffer.h
6
7#pragma once
8
9#include "common/common_funcs.h"
10#include "common/common_types.h"
11#include "core/hle/service/nvflinger/pixel_format.h"
12
13namespace Service::android {
14
15class GraphicBuffer final {
16public:
17 constexpr GraphicBuffer() = default;
18
19 constexpr GraphicBuffer(u32 width_, u32 height_, PixelFormat format_, u32 usage_)
20 : width{static_cast<s32>(width_)}, height{static_cast<s32>(height_)}, format{format_},
21 usage{static_cast<s32>(usage_)} {}
22
23 constexpr u32 Width() const {
24 return static_cast<u32>(width);
25 }
26
27 constexpr u32 Height() const {
28 return static_cast<u32>(height);
29 }
30
31 constexpr u32 Stride() const {
32 return static_cast<u32>(stride);
33 }
34
35 constexpr u32 Usage() const {
36 return static_cast<u32>(usage);
37 }
38
39 constexpr PixelFormat Format() const {
40 return format;
41 }
42
43 constexpr u32 BufferId() const {
44 return buffer_id;
45 }
46
47 constexpr PixelFormat ExternalFormat() const {
48 return external_format;
49 }
50
51 constexpr u32 Handle() const {
52 return handle;
53 }
54
55 constexpr u32 Offset() const {
56 return offset;
57 }
58
59 constexpr bool NeedsReallocation(u32 width_, u32 height_, PixelFormat format_,
60 u32 usage_) const {
61 if (static_cast<s32>(width_) != width) {
62 return true;
63 }
64
65 if (static_cast<s32>(height_) != height) {
66 return true;
67 }
68
69 if (format_ != format) {
70 return true;
71 }
72
73 if ((static_cast<u32>(usage) & usage_) != usage_) {
74 return true;
75 }
76
77 return false;
78 }
79
80private:
81 u32 magic{};
82 s32 width{};
83 s32 height{};
84 s32 stride{};
85 PixelFormat format{};
86 s32 usage{};
87 INSERT_PADDING_WORDS(1);
88 u32 index{};
89 INSERT_PADDING_WORDS(3);
90 u32 buffer_id{};
91 INSERT_PADDING_WORDS(6);
92 PixelFormat external_format{};
93 INSERT_PADDING_WORDS(10);
94 u32 handle{};
95 u32 offset{};
96 INSERT_PADDING_WORDS(60);
97};
98static_assert(sizeof(GraphicBuffer) == 0x16C, "GraphicBuffer has wrong size");
99
100} // namespace Service::android
diff --git a/src/core/hle/service/nvflinger/window.h b/src/core/hle/service/nvflinger/window.h
new file mode 100644
index 000000000..e26f8160e
--- /dev/null
+++ b/src/core/hle/service/nvflinger/window.h
@@ -0,0 +1,53 @@
1// SPDX-License-Identifier: GPL-3.0-or-later
2// Copyright 2021 yuzu Emulator Project
3
4#pragma once
5
6#include "common/common_funcs.h"
7#include "common/common_types.h"
8
9namespace Service::android {
10
11/// Attributes queryable with Query
12enum class NativeWindow : s32 {
13 Width = 0,
14 Height = 1,
15 Format = 2,
16 MinUndequeedBuffers = 3,
17 QueuesToWindowComposer = 4,
18 ConcreteType = 5,
19 DefaultWidth = 6,
20 DefaultHeight = 7,
21 TransformHint = 8,
22 ConsumerRunningBehind = 9,
23 ConsumerUsageBits = 10,
24 StickyTransform = 11,
25 DefaultDataSpace = 12,
26 BufferAge = 13,
27};
28
29/// Parameter for Connect/Disconnect
30enum class NativeWindowApi : s32 {
31 NoConnectedApi = 0,
32 Egl = 1,
33 Cpu = 2,
34 Media = 3,
35 Camera = 4,
36};
37
38/// Scaling mode parameter for QueueBuffer
39enum class NativeWindowScalingMode : s32 {
40 Freeze = 0,
41 ScaleToWindow = 1,
42 ScaleCrop = 2,
43 NoScaleCrop = 3,
44};
45
46/// Transform parameter for QueueBuffer
47enum class NativeWindowTransform : u32 {
48 None = 0x0,
49 InverseDisplay = 0x08,
50};
51DECLARE_ENUM_FLAG_OPERATORS(NativeWindowTransform);
52
53} // namespace Service::android
diff --git a/src/core/hle/service/service.cpp b/src/core/hle/service/service.cpp
index eb1138313..0f59a03c5 100644
--- a/src/core/hle/service/service.cpp
+++ b/src/core/hle/service/service.cpp
@@ -32,6 +32,7 @@
32#include "core/hle/service/glue/glue.h" 32#include "core/hle/service/glue/glue.h"
33#include "core/hle/service/grc/grc.h" 33#include "core/hle/service/grc/grc.h"
34#include "core/hle/service/hid/hid.h" 34#include "core/hle/service/hid/hid.h"
35#include "core/hle/service/jit/jit.h"
35#include "core/hle/service/lbl/lbl.h" 36#include "core/hle/service/lbl/lbl.h"
36#include "core/hle/service/ldn/ldn.h" 37#include "core/hle/service/ldn/ldn.h"
37#include "core/hle/service/ldr/ldr.h" 38#include "core/hle/service/ldr/ldr.h"
@@ -49,6 +50,7 @@
49#include "core/hle/service/npns/npns.h" 50#include "core/hle/service/npns/npns.h"
50#include "core/hle/service/ns/ns.h" 51#include "core/hle/service/ns/ns.h"
51#include "core/hle/service/nvdrv/nvdrv.h" 52#include "core/hle/service/nvdrv/nvdrv.h"
53#include "core/hle/service/nvflinger/hos_binder_driver_server.h"
52#include "core/hle/service/nvflinger/nvflinger.h" 54#include "core/hle/service/nvflinger/nvflinger.h"
53#include "core/hle/service/olsc/olsc.h" 55#include "core/hle/service/olsc/olsc.h"
54#include "core/hle/service/pcie/pcie.h" 56#include "core/hle/service/pcie/pcie.h"
@@ -90,8 +92,9 @@ namespace Service {
90} 92}
91 93
92ServiceFrameworkBase::ServiceFrameworkBase(Core::System& system_, const char* service_name_, 94ServiceFrameworkBase::ServiceFrameworkBase(Core::System& system_, const char* service_name_,
93 u32 max_sessions_, InvokerFn* handler_invoker_) 95 ServiceThreadType thread_type, u32 max_sessions_,
94 : SessionRequestHandler(system_.Kernel(), service_name_), system{system_}, 96 InvokerFn* handler_invoker_)
97 : SessionRequestHandler(system_.Kernel(), service_name_, thread_type), system{system_},
95 service_name{service_name_}, max_sessions{max_sessions_}, handler_invoker{handler_invoker_} {} 98 service_name{service_name_}, max_sessions{max_sessions_}, handler_invoker{handler_invoker_} {}
96 99
97ServiceFrameworkBase::~ServiceFrameworkBase() { 100ServiceFrameworkBase::~ServiceFrameworkBase() {
@@ -230,7 +233,8 @@ ResultCode ServiceFrameworkBase::HandleSyncRequest(Kernel::KServerSession& sessi
230 233
231/// Initialize Services 234/// Initialize Services
232Services::Services(std::shared_ptr<SM::ServiceManager>& sm, Core::System& system) 235Services::Services(std::shared_ptr<SM::ServiceManager>& sm, Core::System& system)
233 : nv_flinger{std::make_unique<NVFlinger::NVFlinger>(system)} { 236 : hos_binder_driver_server{std::make_unique<NVFlinger::HosBinderDriverServer>(system)},
237 nv_flinger{std::make_unique<NVFlinger::NVFlinger>(system, *hos_binder_driver_server)} {
234 238
235 // NVFlinger needs to be accessed by several services like Vi and AppletOE so we instantiate it 239 // NVFlinger needs to be accessed by several services like Vi and AppletOE so we instantiate it
236 // here and pass it into the respective InstallInterfaces functions. 240 // here and pass it into the respective InstallInterfaces functions.
@@ -259,6 +263,7 @@ Services::Services(std::shared_ptr<SM::ServiceManager>& sm, Core::System& system
259 Glue::InstallInterfaces(system); 263 Glue::InstallInterfaces(system);
260 GRC::InstallInterfaces(*sm, system); 264 GRC::InstallInterfaces(*sm, system);
261 HID::InstallInterfaces(*sm, system); 265 HID::InstallInterfaces(*sm, system);
266 JIT::InstallInterfaces(*sm, system);
262 LBL::InstallInterfaces(*sm, system); 267 LBL::InstallInterfaces(*sm, system);
263 LDN::InstallInterfaces(*sm, system); 268 LDN::InstallInterfaces(*sm, system);
264 LDR::InstallInterfaces(*sm, system); 269 LDR::InstallInterfaces(*sm, system);
@@ -290,7 +295,7 @@ Services::Services(std::shared_ptr<SM::ServiceManager>& sm, Core::System& system
290 SSL::InstallInterfaces(*sm, system); 295 SSL::InstallInterfaces(*sm, system);
291 Time::InstallInterfaces(system); 296 Time::InstallInterfaces(system);
292 USB::InstallInterfaces(*sm, system); 297 USB::InstallInterfaces(*sm, system);
293 VI::InstallInterfaces(*sm, system, *nv_flinger); 298 VI::InstallInterfaces(*sm, system, *nv_flinger, *hos_binder_driver_server);
294 WLAN::InstallInterfaces(*sm, system); 299 WLAN::InstallInterfaces(*sm, system);
295} 300}
296 301
diff --git a/src/core/hle/service/service.h b/src/core/hle/service/service.h
index c9d6b879d..148265218 100644
--- a/src/core/hle/service/service.h
+++ b/src/core/hle/service/service.h
@@ -9,7 +9,6 @@
9#include <string> 9#include <string>
10#include <boost/container/flat_map.hpp> 10#include <boost/container/flat_map.hpp>
11#include "common/common_types.h" 11#include "common/common_types.h"
12#include "common/spin_lock.h"
13#include "core/hle/kernel/hle_ipc.h" 12#include "core/hle/kernel/hle_ipc.h"
14 13
15//////////////////////////////////////////////////////////////////////////////////////////////////// 14////////////////////////////////////////////////////////////////////////////////////////////////////
@@ -33,8 +32,9 @@ class FileSystemController;
33} 32}
34 33
35namespace NVFlinger { 34namespace NVFlinger {
35class HosBinderDriverServer;
36class NVFlinger; 36class NVFlinger;
37} 37} // namespace NVFlinger
38 38
39namespace SM { 39namespace SM {
40class ServiceManager; 40class ServiceManager;
@@ -89,7 +89,7 @@ protected:
89 using HandlerFnP = void (Self::*)(Kernel::HLERequestContext&); 89 using HandlerFnP = void (Self::*)(Kernel::HLERequestContext&);
90 90
91 /// Used to gain exclusive access to the service members, e.g. from CoreTiming thread. 91 /// Used to gain exclusive access to the service members, e.g. from CoreTiming thread.
92 [[nodiscard]] std::scoped_lock<Common::SpinLock> LockService() { 92 [[nodiscard]] std::scoped_lock<std::mutex> LockService() {
93 return std::scoped_lock{lock_service}; 93 return std::scoped_lock{lock_service};
94 } 94 }
95 95
@@ -113,7 +113,8 @@ private:
113 Kernel::HLERequestContext& ctx); 113 Kernel::HLERequestContext& ctx);
114 114
115 explicit ServiceFrameworkBase(Core::System& system_, const char* service_name_, 115 explicit ServiceFrameworkBase(Core::System& system_, const char* service_name_,
116 u32 max_sessions_, InvokerFn* handler_invoker_); 116 ServiceThreadType thread_type, u32 max_sessions_,
117 InvokerFn* handler_invoker_);
117 ~ServiceFrameworkBase() override; 118 ~ServiceFrameworkBase() override;
118 119
119 void RegisterHandlersBase(const FunctionInfoBase* functions, std::size_t n); 120 void RegisterHandlersBase(const FunctionInfoBase* functions, std::size_t n);
@@ -133,7 +134,7 @@ private:
133 boost::container::flat_map<u32, FunctionInfoBase> handlers_tipc; 134 boost::container::flat_map<u32, FunctionInfoBase> handlers_tipc;
134 135
135 /// Used to gain exclusive access to the service members, e.g. from CoreTiming thread. 136 /// Used to gain exclusive access to the service members, e.g. from CoreTiming thread.
136 Common::SpinLock lock_service; 137 std::mutex lock_service;
137}; 138};
138 139
139/** 140/**
@@ -175,14 +176,17 @@ protected:
175 /** 176 /**
176 * Initializes the handler with no functions installed. 177 * Initializes the handler with no functions installed.
177 * 178 *
178 * @param system_ The system context to construct this service under. 179 * @param system_ The system context to construct this service under.
179 * @param service_name_ Name of the service. 180 * @param service_name_ Name of the service.
180 * @param max_sessions_ Maximum number of sessions that can be 181 * @param thread_type Specifies the thread type for this service. If this is set to CreateNew,
181 * connected to this service at the same time. 182 * it creates a new thread for it, otherwise this uses the default thread.
183 * @param max_sessions_ Maximum number of sessions that can be connected to this service at the
184 * same time.
182 */ 185 */
183 explicit ServiceFramework(Core::System& system_, const char* service_name_, 186 explicit ServiceFramework(Core::System& system_, const char* service_name_,
187 ServiceThreadType thread_type = ServiceThreadType::Default,
184 u32 max_sessions_ = ServerSessionCountMax) 188 u32 max_sessions_ = ServerSessionCountMax)
185 : ServiceFrameworkBase(system_, service_name_, max_sessions_, Invoker) {} 189 : ServiceFrameworkBase(system_, service_name_, thread_type, max_sessions_, Invoker) {}
186 190
187 /// Registers handlers in the service. 191 /// Registers handlers in the service.
188 template <std::size_t N> 192 template <std::size_t N>
@@ -236,6 +240,7 @@ public:
236 ~Services(); 240 ~Services();
237 241
238private: 242private:
243 std::unique_ptr<NVFlinger::HosBinderDriverServer> hos_binder_driver_server;
239 std::unique_ptr<NVFlinger::NVFlinger> nv_flinger; 244 std::unique_ptr<NVFlinger::NVFlinger> nv_flinger;
240}; 245};
241 246
diff --git a/src/core/hle/service/sm/sm.cpp b/src/core/hle/service/sm/sm.cpp
index 695a1faa6..13f5e08ec 100644
--- a/src/core/hle/service/sm/sm.cpp
+++ b/src/core/hle/service/sm/sm.cpp
@@ -153,7 +153,7 @@ ResultVal<Kernel::KClientSession*> SM::GetServiceImpl(Kernel::HLERequestContext&
153 auto& port = port_result.Unwrap(); 153 auto& port = port_result.Unwrap();
154 SCOPE_EXIT({ port->GetClientPort().Close(); }); 154 SCOPE_EXIT({ port->GetClientPort().Close(); });
155 155
156 server_ports.emplace_back(&port->GetServerPort()); 156 kernel.RegisterServerObject(&port->GetServerPort());
157 157
158 // Create a new session. 158 // Create a new session.
159 Kernel::KClientSession* session{}; 159 Kernel::KClientSession* session{};
@@ -206,7 +206,7 @@ void SM::UnregisterService(Kernel::HLERequestContext& ctx) {
206} 206}
207 207
208SM::SM(ServiceManager& service_manager_, Core::System& system_) 208SM::SM(ServiceManager& service_manager_, Core::System& system_)
209 : ServiceFramework{system_, "sm:", 4}, 209 : ServiceFramework{system_, "sm:", ServiceThreadType::Default, 4},
210 service_manager{service_manager_}, kernel{system_.Kernel()} { 210 service_manager{service_manager_}, kernel{system_.Kernel()} {
211 RegisterHandlers({ 211 RegisterHandlers({
212 {0, &SM::Initialize, "Initialize"}, 212 {0, &SM::Initialize, "Initialize"},
@@ -224,10 +224,6 @@ SM::SM(ServiceManager& service_manager_, Core::System& system_)
224 }); 224 });
225} 225}
226 226
227SM::~SM() { 227SM::~SM() = default;
228 for (auto& server_port : server_ports) {
229 server_port->Close();
230 }
231}
232 228
233} // namespace Service::SM 229} // namespace Service::SM
diff --git a/src/core/hle/service/sm/sm.h b/src/core/hle/service/sm/sm.h
index 021eb51b4..f3ff7b27e 100644
--- a/src/core/hle/service/sm/sm.h
+++ b/src/core/hle/service/sm/sm.h
@@ -22,7 +22,6 @@ class KClientPort;
22class KClientSession; 22class KClientSession;
23class KernelCore; 23class KernelCore;
24class KPort; 24class KPort;
25class KServerPort;
26class SessionRequestHandler; 25class SessionRequestHandler;
27} // namespace Kernel 26} // namespace Kernel
28 27
@@ -48,7 +47,6 @@ private:
48 ServiceManager& service_manager; 47 ServiceManager& service_manager;
49 bool is_initialized{}; 48 bool is_initialized{};
50 Kernel::KernelCore& kernel; 49 Kernel::KernelCore& kernel;
51 std::vector<Kernel::KServerPort*> server_ports;
52}; 50};
53 51
54class ServiceManager { 52class ServiceManager {
diff --git a/src/core/hle/service/sockets/bsd.cpp b/src/core/hle/service/sockets/bsd.cpp
index fc93fb743..d25b050e2 100644
--- a/src/core/hle/service/sockets/bsd.cpp
+++ b/src/core/hle/service/sockets/bsd.cpp
@@ -689,6 +689,9 @@ Errno BSD::SetSockOptImpl(s32 fd, u32 level, OptName optname, size_t optlen, con
689 case OptName::REUSEADDR: 689 case OptName::REUSEADDR:
690 ASSERT(value == 0 || value == 1); 690 ASSERT(value == 0 || value == 1);
691 return Translate(socket->SetReuseAddr(value != 0)); 691 return Translate(socket->SetReuseAddr(value != 0));
692 case OptName::KEEPALIVE:
693 ASSERT(value == 0 || value == 1);
694 return Translate(socket->SetKeepAlive(value != 0));
692 case OptName::BROADCAST: 695 case OptName::BROADCAST:
693 ASSERT(value == 0 || value == 1); 696 ASSERT(value == 0 || value == 1);
694 return Translate(socket->SetBroadcast(value != 0)); 697 return Translate(socket->SetBroadcast(value != 0));
@@ -837,7 +840,8 @@ void BSD::BuildErrnoResponse(Kernel::HLERequestContext& ctx, Errno bsd_errno) co
837 rb.PushEnum(bsd_errno); 840 rb.PushEnum(bsd_errno);
838} 841}
839 842
840BSD::BSD(Core::System& system_, const char* name) : ServiceFramework{system_, name} { 843BSD::BSD(Core::System& system_, const char* name)
844 : ServiceFramework{system_, name, ServiceThreadType::CreateNew} {
841 // clang-format off 845 // clang-format off
842 static const FunctionInfo functions[] = { 846 static const FunctionInfo functions[] = {
843 {0, &BSD::RegisterClient, "RegisterClient"}, 847 {0, &BSD::RegisterClient, "RegisterClient"},
diff --git a/src/core/hle/service/sockets/sfdnsres.cpp b/src/core/hle/service/sockets/sfdnsres.cpp
index fb6142c49..a193fb578 100644
--- a/src/core/hle/service/sockets/sfdnsres.cpp
+++ b/src/core/hle/service/sockets/sfdnsres.cpp
@@ -2,8 +2,28 @@
2// Licensed under GPLv2 or any later version 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <string_view>
6#include <utility>
7#include <vector>
8
9#include "common/string_util.h"
10#include "common/swap.h"
11#include "core/core.h"
5#include "core/hle/ipc_helpers.h" 12#include "core/hle/ipc_helpers.h"
6#include "core/hle/service/sockets/sfdnsres.h" 13#include "core/hle/service/sockets/sfdnsres.h"
14#include "core/memory.h"
15
16#ifdef _WIN32
17#include <ws2tcpip.h>
18#elif YUZU_UNIX
19#include <arpa/inet.h>
20#include <netdb.h>
21#include <netinet/in.h>
22#include <sys/socket.h>
23#ifndef EAI_NODATA
24#define EAI_NODATA EAI_NONAME
25#endif
26#endif
7 27
8namespace Service::Sockets { 28namespace Service::Sockets {
9 29
@@ -21,7 +41,7 @@ SFDNSRES::SFDNSRES(Core::System& system_) : ServiceFramework{system_, "sfdnsres"
21 {9, nullptr, "CancelRequest"}, 41 {9, nullptr, "CancelRequest"},
22 {10, nullptr, "GetHostByNameRequestWithOptions"}, 42 {10, nullptr, "GetHostByNameRequestWithOptions"},
23 {11, nullptr, "GetHostByAddrRequestWithOptions"}, 43 {11, nullptr, "GetHostByAddrRequestWithOptions"},
24 {12, nullptr, "GetAddrInfoRequestWithOptions"}, 44 {12, &SFDNSRES::GetAddrInfoRequestWithOptions, "GetAddrInfoRequestWithOptions"},
25 {13, nullptr, "GetNameInfoRequestWithOptions"}, 45 {13, nullptr, "GetNameInfoRequestWithOptions"},
26 {14, nullptr, "ResolverSetOptionRequest"}, 46 {14, nullptr, "ResolverSetOptionRequest"},
27 {15, nullptr, "ResolverGetOptionRequest"}, 47 {15, nullptr, "ResolverGetOptionRequest"},
@@ -31,7 +51,142 @@ SFDNSRES::SFDNSRES(Core::System& system_) : ServiceFramework{system_, "sfdnsres"
31 51
32SFDNSRES::~SFDNSRES() = default; 52SFDNSRES::~SFDNSRES() = default;
33 53
34void SFDNSRES::GetAddrInfoRequest(Kernel::HLERequestContext& ctx) { 54enum class NetDbError : s32 {
55 Internal = -1,
56 Success = 0,
57 HostNotFound = 1,
58 TryAgain = 2,
59 NoRecovery = 3,
60 NoData = 4,
61};
62
63static NetDbError AddrInfoErrorToNetDbError(s32 result) {
64 // Best effort guess to map errors
65 switch (result) {
66 case 0:
67 return NetDbError::Success;
68 case EAI_AGAIN:
69 return NetDbError::TryAgain;
70 case EAI_NODATA:
71 return NetDbError::NoData;
72 default:
73 return NetDbError::HostNotFound;
74 }
75}
76
77static std::vector<u8> SerializeAddrInfo(const addrinfo* addrinfo, s32 result_code,
78 std::string_view host) {
79 // Adapted from
80 // https://github.com/switchbrew/libnx/blob/c5a9a909a91657a9818a3b7e18c9b91ff0cbb6e3/nx/source/runtime/resolver.c#L190
81 std::vector<u8> data;
82
83 auto* current = addrinfo;
84 while (current != nullptr) {
85 struct SerializedResponseHeader {
86 u32 magic;
87 s32 flags;
88 s32 family;
89 s32 socket_type;
90 s32 protocol;
91 u32 address_length;
92 };
93 static_assert(sizeof(SerializedResponseHeader) == 0x18,
94 "Response header size must be 0x18 bytes");
95
96 constexpr auto header_size = sizeof(SerializedResponseHeader);
97 const auto addr_size =
98 current->ai_addr && current->ai_addrlen > 0 ? current->ai_addrlen : 4;
99 const auto canonname_size = current->ai_canonname ? strlen(current->ai_canonname) + 1 : 1;
100
101 const auto last_size = data.size();
102 data.resize(last_size + header_size + addr_size + canonname_size);
103
104 // Header in network byte order
105 SerializedResponseHeader header{};
106
107 constexpr auto HEADER_MAGIC = 0xBEEFCAFE;
108 header.magic = htonl(HEADER_MAGIC);
109 header.family = htonl(current->ai_family);
110 header.flags = htonl(current->ai_flags);
111 header.socket_type = htonl(current->ai_socktype);
112 header.protocol = htonl(current->ai_protocol);
113 header.address_length = current->ai_addr ? htonl((u32)current->ai_addrlen) : 0;
114
115 auto* header_ptr = data.data() + last_size;
116 std::memcpy(header_ptr, &header, header_size);
117
118 if (header.address_length == 0) {
119 std::memset(header_ptr + header_size, 0, 4);
120 } else {
121 switch (current->ai_family) {
122 case AF_INET: {
123 struct SockAddrIn {
124 s16 sin_family;
125 u16 sin_port;
126 u32 sin_addr;
127 u8 sin_zero[8];
128 };
129
130 SockAddrIn serialized_addr{};
131 const auto addr = *reinterpret_cast<sockaddr_in*>(current->ai_addr);
132 serialized_addr.sin_port = htons(addr.sin_port);
133 serialized_addr.sin_family = htons(addr.sin_family);
134 serialized_addr.sin_addr = htonl(addr.sin_addr.s_addr);
135 std::memcpy(header_ptr + header_size, &serialized_addr, sizeof(SockAddrIn));
136
137 char addr_string_buf[64]{};
138 inet_ntop(AF_INET, &addr.sin_addr, addr_string_buf, std::size(addr_string_buf));
139 LOG_INFO(Service, "Resolved host '{}' to IPv4 address {}", host, addr_string_buf);
140 break;
141 }
142 case AF_INET6: {
143 struct SockAddrIn6 {
144 s16 sin6_family;
145 u16 sin6_port;
146 u32 sin6_flowinfo;
147 u8 sin6_addr[16];
148 u32 sin6_scope_id;
149 };
150
151 SockAddrIn6 serialized_addr{};
152 const auto addr = *reinterpret_cast<sockaddr_in6*>(current->ai_addr);
153 serialized_addr.sin6_family = htons(addr.sin6_family);
154 serialized_addr.sin6_port = htons(addr.sin6_port);
155 serialized_addr.sin6_flowinfo = htonl(addr.sin6_flowinfo);
156 serialized_addr.sin6_scope_id = htonl(addr.sin6_scope_id);
157 std::memcpy(serialized_addr.sin6_addr, &addr.sin6_addr,
158 sizeof(SockAddrIn6::sin6_addr));
159 std::memcpy(header_ptr + header_size, &serialized_addr, sizeof(SockAddrIn6));
160
161 char addr_string_buf[64]{};
162 inet_ntop(AF_INET6, &addr.sin6_addr, addr_string_buf, std::size(addr_string_buf));
163 LOG_INFO(Service, "Resolved host '{}' to IPv6 address {}", host, addr_string_buf);
164 break;
165 }
166 default:
167 std::memcpy(header_ptr + header_size, current->ai_addr, addr_size);
168 break;
169 }
170 }
171 if (current->ai_canonname) {
172 std::memcpy(header_ptr + addr_size, current->ai_canonname, canonname_size);
173 } else {
174 *(header_ptr + header_size + addr_size) = 0;
175 }
176
177 current = current->ai_next;
178 }
179
180 // 4-byte sentinel value
181 data.push_back(0);
182 data.push_back(0);
183 data.push_back(0);
184 data.push_back(0);
185
186 return data;
187}
188
189static std::pair<u32, s32> GetAddrInfoRequestImpl(Kernel::HLERequestContext& ctx) {
35 struct Parameters { 190 struct Parameters {
36 u8 use_nsd_resolve; 191 u8 use_nsd_resolve;
37 u32 unknown; 192 u32 unknown;
@@ -42,11 +197,51 @@ void SFDNSRES::GetAddrInfoRequest(Kernel::HLERequestContext& ctx) {
42 const auto parameters = rp.PopRaw<Parameters>(); 197 const auto parameters = rp.PopRaw<Parameters>();
43 198
44 LOG_WARNING(Service, 199 LOG_WARNING(Service,
45 "(STUBBED) called. use_nsd_resolve={}, unknown=0x{:08X}, process_id=0x{:016X}", 200 "called with ignored parameters: use_nsd_resolve={}, unknown={}, process_id={}",
46 parameters.use_nsd_resolve, parameters.unknown, parameters.process_id); 201 parameters.use_nsd_resolve, parameters.unknown, parameters.process_id);
47 202
48 IPC::ResponseBuilder rb{ctx, 2}; 203 const auto host_buffer = ctx.ReadBuffer(0);
204 const std::string host = Common::StringFromBuffer(host_buffer);
205
206 const auto service_buffer = ctx.ReadBuffer(1);
207 const std::string service = Common::StringFromBuffer(service_buffer);
208
209 addrinfo* addrinfo;
210 // Pass null for hints. Serialized hints are also passed in a buffer, but are ignored for now
211 s32 result_code = getaddrinfo(host.c_str(), service.c_str(), nullptr, &addrinfo);
212
213 u32 data_size = 0;
214 if (result_code == 0 && addrinfo != nullptr) {
215 const std::vector<u8>& data = SerializeAddrInfo(addrinfo, result_code, host);
216 data_size = static_cast<u32>(data.size());
217 freeaddrinfo(addrinfo);
218
219 ctx.WriteBuffer(data, 0);
220 }
221
222 return std::make_pair(data_size, result_code);
223}
224
225void SFDNSRES::GetAddrInfoRequest(Kernel::HLERequestContext& ctx) {
226 auto [data_size, result_code] = GetAddrInfoRequestImpl(ctx);
227
228 IPC::ResponseBuilder rb{ctx, 4};
229 rb.Push(ResultSuccess);
230 rb.Push(static_cast<s32>(AddrInfoErrorToNetDbError(result_code))); // NetDBErrorCode
231 rb.Push(result_code); // errno
232 rb.Push(data_size); // serialized size
233}
234
235void SFDNSRES::GetAddrInfoRequestWithOptions(Kernel::HLERequestContext& ctx) {
236 // Additional options are ignored
237 auto [data_size, result_code] = GetAddrInfoRequestImpl(ctx);
238
239 IPC::ResponseBuilder rb{ctx, 5};
49 rb.Push(ResultSuccess); 240 rb.Push(ResultSuccess);
241 rb.Push(data_size); // serialized size
242 rb.Push(result_code); // errno
243 rb.Push(static_cast<s32>(AddrInfoErrorToNetDbError(result_code))); // NetDBErrorCode
244 rb.Push(0);
50} 245}
51 246
52} // namespace Service::Sockets 247} // namespace Service::Sockets \ No newline at end of file
diff --git a/src/core/hle/service/sockets/sfdnsres.h b/src/core/hle/service/sockets/sfdnsres.h
index 5d3b4dc2d..f0c57377d 100644
--- a/src/core/hle/service/sockets/sfdnsres.h
+++ b/src/core/hle/service/sockets/sfdnsres.h
@@ -19,6 +19,7 @@ public:
19 19
20private: 20private:
21 void GetAddrInfoRequest(Kernel::HLERequestContext& ctx); 21 void GetAddrInfoRequest(Kernel::HLERequestContext& ctx);
22 void GetAddrInfoRequestWithOptions(Kernel::HLERequestContext& ctx);
22}; 23};
23 24
24} // namespace Service::Sockets 25} // namespace Service::Sockets
diff --git a/src/core/hle/service/sockets/sockets.h b/src/core/hle/service/sockets/sockets.h
index 02dbbae40..d69c86431 100644
--- a/src/core/hle/service/sockets/sockets.h
+++ b/src/core/hle/service/sockets/sockets.h
@@ -46,6 +46,7 @@ enum class Protocol : u32 {
46 46
47enum class OptName : u32 { 47enum class OptName : u32 {
48 REUSEADDR = 0x4, 48 REUSEADDR = 0x4,
49 KEEPALIVE = 0x8,
49 BROADCAST = 0x20, 50 BROADCAST = 0x20,
50 LINGER = 0x80, 51 LINGER = 0x80,
51 SNDBUF = 0x1001, 52 SNDBUF = 0x1001,
diff --git a/src/core/hle/service/vi/display/vi_display.cpp b/src/core/hle/service/vi/display/vi_display.cpp
index b7705c02a..558022511 100644
--- a/src/core/hle/service/vi/display/vi_display.cpp
+++ b/src/core/hle/service/vi/display/vi_display.cpp
@@ -13,14 +13,34 @@
13#include "core/hle/kernel/k_readable_event.h" 13#include "core/hle/kernel/k_readable_event.h"
14#include "core/hle/kernel/k_writable_event.h" 14#include "core/hle/kernel/k_writable_event.h"
15#include "core/hle/service/kernel_helpers.h" 15#include "core/hle/service/kernel_helpers.h"
16#include "core/hle/service/nvflinger/buffer_item_consumer.h"
17#include "core/hle/service/nvflinger/buffer_queue_consumer.h"
18#include "core/hle/service/nvflinger/buffer_queue_core.h"
19#include "core/hle/service/nvflinger/buffer_queue_producer.h"
20#include "core/hle/service/nvflinger/hos_binder_driver_server.h"
16#include "core/hle/service/vi/display/vi_display.h" 21#include "core/hle/service/vi/display/vi_display.h"
17#include "core/hle/service/vi/layer/vi_layer.h" 22#include "core/hle/service/vi/layer/vi_layer.h"
18 23
19namespace Service::VI { 24namespace Service::VI {
20 25
21Display::Display(u64 id, std::string name_, KernelHelpers::ServiceContext& service_context_, 26struct BufferQueue {
22 Core::System& system_) 27 std::shared_ptr<android::BufferQueueCore> core;
23 : display_id{id}, name{std::move(name_)}, service_context{service_context_} { 28 std::unique_ptr<android::BufferQueueProducer> producer;
29 std::unique_ptr<android::BufferQueueConsumer> consumer;
30};
31
32static BufferQueue CreateBufferQueue(KernelHelpers::ServiceContext& service_context) {
33 auto buffer_queue_core = std::make_shared<android::BufferQueueCore>();
34 return {buffer_queue_core,
35 std::make_unique<android::BufferQueueProducer>(service_context, buffer_queue_core),
36 std::make_unique<android::BufferQueueConsumer>(buffer_queue_core)};
37}
38
39Display::Display(u64 id, std::string name_,
40 NVFlinger::HosBinderDriverServer& hos_binder_driver_server_,
41 KernelHelpers::ServiceContext& service_context_, Core::System& system_)
42 : display_id{id}, name{std::move(name_)}, hos_binder_driver_server{hos_binder_driver_server_},
43 service_context{service_context_} {
24 vsync_event = service_context.CreateEvent(fmt::format("Display VSync Event {}", id)); 44 vsync_event = service_context.CreateEvent(fmt::format("Display VSync Event {}", id));
25} 45}
26 46
@@ -44,21 +64,29 @@ void Display::SignalVSyncEvent() {
44 vsync_event->GetWritableEvent().Signal(); 64 vsync_event->GetWritableEvent().Signal();
45} 65}
46 66
47void Display::CreateLayer(u64 layer_id, NVFlinger::BufferQueue& buffer_queue) { 67void Display::CreateLayer(u64 layer_id, u32 binder_id) {
48 // TODO(Subv): Support more than 1 layer.
49 ASSERT_MSG(layers.empty(), "Only one layer is supported per display at the moment"); 68 ASSERT_MSG(layers.empty(), "Only one layer is supported per display at the moment");
50 69
51 layers.emplace_back(std::make_shared<Layer>(layer_id, buffer_queue)); 70 auto [core, producer, consumer] = CreateBufferQueue(service_context);
71
72 auto buffer_item_consumer = std::make_shared<android::BufferItemConsumer>(std::move(consumer));
73 buffer_item_consumer->Connect(false);
74
75 layers.emplace_back(std::make_unique<Layer>(layer_id, binder_id, *core, *producer,
76 std::move(buffer_item_consumer)));
77
78 hos_binder_driver_server.RegisterProducer(std::move(producer));
52} 79}
53 80
54void Display::CloseLayer(u64 layer_id) { 81void Display::CloseLayer(u64 layer_id) {
55 std::erase_if(layers, [layer_id](const auto& layer) { return layer->GetID() == layer_id; }); 82 std::erase_if(layers,
83 [layer_id](const auto& layer) { return layer->GetLayerId() == layer_id; });
56} 84}
57 85
58Layer* Display::FindLayer(u64 layer_id) { 86Layer* Display::FindLayer(u64 layer_id) {
59 const auto itr = 87 const auto itr =
60 std::find_if(layers.begin(), layers.end(), [layer_id](const std::shared_ptr<Layer>& layer) { 88 std::find_if(layers.begin(), layers.end(), [layer_id](const std::unique_ptr<Layer>& layer) {
61 return layer->GetID() == layer_id; 89 return layer->GetLayerId() == layer_id;
62 }); 90 });
63 91
64 if (itr == layers.end()) { 92 if (itr == layers.end()) {
@@ -70,8 +98,8 @@ Layer* Display::FindLayer(u64 layer_id) {
70 98
71const Layer* Display::FindLayer(u64 layer_id) const { 99const Layer* Display::FindLayer(u64 layer_id) const {
72 const auto itr = 100 const auto itr =
73 std::find_if(layers.begin(), layers.end(), [layer_id](const std::shared_ptr<Layer>& layer) { 101 std::find_if(layers.begin(), layers.end(), [layer_id](const std::unique_ptr<Layer>& layer) {
74 return layer->GetID() == layer_id; 102 return layer->GetLayerId() == layer_id;
75 }); 103 });
76 104
77 if (itr == layers.end()) { 105 if (itr == layers.end()) {
diff --git a/src/core/hle/service/vi/display/vi_display.h b/src/core/hle/service/vi/display/vi_display.h
index 329f4ba86..e93d084ee 100644
--- a/src/core/hle/service/vi/display/vi_display.h
+++ b/src/core/hle/service/vi/display/vi_display.h
@@ -15,12 +15,17 @@ namespace Kernel {
15class KEvent; 15class KEvent;
16} 16}
17 17
18namespace Service::NVFlinger { 18namespace Service::android {
19class BufferQueue; 19class BufferQueueProducer;
20} 20}
21
21namespace Service::KernelHelpers { 22namespace Service::KernelHelpers {
22class ServiceContext; 23class ServiceContext;
23} // namespace Service::KernelHelpers 24}
25
26namespace Service::NVFlinger {
27class HosBinderDriverServer;
28}
24 29
25namespace Service::VI { 30namespace Service::VI {
26 31
@@ -35,12 +40,13 @@ public:
35 /// Constructs a display with a given unique ID and name. 40 /// Constructs a display with a given unique ID and name.
36 /// 41 ///
37 /// @param id The unique ID for this display. 42 /// @param id The unique ID for this display.
43 /// @param hos_binder_driver_server_ NVFlinger HOSBinderDriver server instance.
38 /// @param service_context_ The ServiceContext for the owning service. 44 /// @param service_context_ The ServiceContext for the owning service.
39 /// @param name_ The name for this display. 45 /// @param name_ The name for this display.
40 /// @param system_ The global system instance. 46 /// @param system_ The global system instance.
41 /// 47 ///
42 Display(u64 id, std::string name_, KernelHelpers::ServiceContext& service_context_, 48 Display(u64 id, std::string name_, NVFlinger::HosBinderDriverServer& hos_binder_driver_server_,
43 Core::System& system_); 49 KernelHelpers::ServiceContext& service_context_, Core::System& system_);
44 ~Display(); 50 ~Display();
45 51
46 /// Gets the unique ID assigned to this display. 52 /// Gets the unique ID assigned to this display.
@@ -64,6 +70,10 @@ public:
64 /// Gets a layer for this display based off an index. 70 /// Gets a layer for this display based off an index.
65 const Layer& GetLayer(std::size_t index) const; 71 const Layer& GetLayer(std::size_t index) const;
66 72
73 std::size_t GetNumLayers() const {
74 return layers.size();
75 }
76
67 /// Gets the readable vsync event. 77 /// Gets the readable vsync event.
68 Kernel::KReadableEvent& GetVSyncEvent(); 78 Kernel::KReadableEvent& GetVSyncEvent();
69 79
@@ -72,10 +82,10 @@ public:
72 82
73 /// Creates and adds a layer to this display with the given ID. 83 /// Creates and adds a layer to this display with the given ID.
74 /// 84 ///
75 /// @param layer_id The ID to assign to the created layer. 85 /// @param layer_id The ID to assign to the created layer.
76 /// @param buffer_queue The buffer queue for the layer instance to use. 86 /// @param binder_id The ID assigned to the buffer queue.
77 /// 87 ///
78 void CreateLayer(u64 layer_id, NVFlinger::BufferQueue& buffer_queue); 88 void CreateLayer(u64 layer_id, u32 binder_id);
79 89
80 /// Closes and removes a layer from this display with the given ID. 90 /// Closes and removes a layer from this display with the given ID.
81 /// 91 ///
@@ -104,9 +114,10 @@ public:
104private: 114private:
105 u64 display_id; 115 u64 display_id;
106 std::string name; 116 std::string name;
117 NVFlinger::HosBinderDriverServer& hos_binder_driver_server;
107 KernelHelpers::ServiceContext& service_context; 118 KernelHelpers::ServiceContext& service_context;
108 119
109 std::vector<std::shared_ptr<Layer>> layers; 120 std::vector<std::unique_ptr<Layer>> layers;
110 Kernel::KEvent* vsync_event{}; 121 Kernel::KEvent* vsync_event{};
111}; 122};
112 123
diff --git a/src/core/hle/service/vi/layer/vi_layer.cpp b/src/core/hle/service/vi/layer/vi_layer.cpp
index 9bc382587..93858e91f 100644
--- a/src/core/hle/service/vi/layer/vi_layer.cpp
+++ b/src/core/hle/service/vi/layer/vi_layer.cpp
@@ -6,7 +6,11 @@
6 6
7namespace Service::VI { 7namespace Service::VI {
8 8
9Layer::Layer(u64 id, NVFlinger::BufferQueue& queue) : layer_id{id}, buffer_queue{queue} {} 9Layer::Layer(u64 layer_id_, u32 binder_id_, android::BufferQueueCore& core_,
10 android::BufferQueueProducer& binder_,
11 std::shared_ptr<android::BufferItemConsumer>&& consumer_)
12 : layer_id{layer_id_}, binder_id{binder_id_}, core{core_}, binder{binder_}, consumer{std::move(
13 consumer_)} {}
10 14
11Layer::~Layer() = default; 15Layer::~Layer() = default;
12 16
diff --git a/src/core/hle/service/vi/layer/vi_layer.h b/src/core/hle/service/vi/layer/vi_layer.h
index ebdd85505..c28b14450 100644
--- a/src/core/hle/service/vi/layer/vi_layer.h
+++ b/src/core/hle/service/vi/layer/vi_layer.h
@@ -4,11 +4,15 @@
4 4
5#pragma once 5#pragma once
6 6
7#include <memory>
8
7#include "common/common_types.h" 9#include "common/common_types.h"
8 10
9namespace Service::NVFlinger { 11namespace Service::android {
10class BufferQueue; 12class BufferItemConsumer;
11} 13class BufferQueueCore;
14class BufferQueueProducer;
15} // namespace Service::android
12 16
13namespace Service::VI { 17namespace Service::VI {
14 18
@@ -17,10 +21,13 @@ class Layer {
17public: 21public:
18 /// Constructs a layer with a given ID and buffer queue. 22 /// Constructs a layer with a given ID and buffer queue.
19 /// 23 ///
20 /// @param id The ID to assign to this layer. 24 /// @param layer_id_ The ID to assign to this layer.
21 /// @param queue The buffer queue for this layer to use. 25 /// @param binder_id_ The binder ID to assign to this layer.
26 /// @param binder_ The buffer producer queue for this layer to use.
22 /// 27 ///
23 Layer(u64 id, NVFlinger::BufferQueue& queue); 28 Layer(u64 layer_id_, u32 binder_id_, android::BufferQueueCore& core_,
29 android::BufferQueueProducer& binder_,
30 std::shared_ptr<android::BufferItemConsumer>&& consumer_);
24 ~Layer(); 31 ~Layer();
25 32
26 Layer(const Layer&) = delete; 33 Layer(const Layer&) = delete;
@@ -30,23 +37,47 @@ public:
30 Layer& operator=(Layer&&) = delete; 37 Layer& operator=(Layer&&) = delete;
31 38
32 /// Gets the ID for this layer. 39 /// Gets the ID for this layer.
33 u64 GetID() const { 40 u64 GetLayerId() const {
34 return layer_id; 41 return layer_id;
35 } 42 }
36 43
44 /// Gets the binder ID for this layer.
45 u32 GetBinderId() const {
46 return binder_id;
47 }
48
37 /// Gets a reference to the buffer queue this layer is using. 49 /// Gets a reference to the buffer queue this layer is using.
38 NVFlinger::BufferQueue& GetBufferQueue() { 50 android::BufferQueueProducer& GetBufferQueue() {
39 return buffer_queue; 51 return binder;
40 } 52 }
41 53
42 /// Gets a const reference to the buffer queue this layer is using. 54 /// Gets a const reference to the buffer queue this layer is using.
43 const NVFlinger::BufferQueue& GetBufferQueue() const { 55 const android::BufferQueueProducer& GetBufferQueue() const {
44 return buffer_queue; 56 return binder;
57 }
58
59 android::BufferItemConsumer& GetConsumer() {
60 return *consumer;
61 }
62
63 const android::BufferItemConsumer& GetConsumer() const {
64 return *consumer;
65 }
66
67 android::BufferQueueCore& Core() {
68 return core;
69 }
70
71 const android::BufferQueueCore& Core() const {
72 return core;
45 } 73 }
46 74
47private: 75private:
48 u64 layer_id; 76 const u64 layer_id;
49 NVFlinger::BufferQueue& buffer_queue; 77 const u32 binder_id;
78 android::BufferQueueCore& core;
79 android::BufferQueueProducer& binder;
80 std::shared_ptr<android::BufferItemConsumer> consumer;
50}; 81};
51 82
52} // namespace Service::VI 83} // namespace Service::VI
diff --git a/src/core/hle/service/vi/vi.cpp b/src/core/hle/service/vi/vi.cpp
index 75ee3e5e4..a3436c8ea 100644
--- a/src/core/hle/service/vi/vi.cpp
+++ b/src/core/hle/service/vi/vi.cpp
@@ -22,8 +22,11 @@
22#include "core/hle/kernel/k_readable_event.h" 22#include "core/hle/kernel/k_readable_event.h"
23#include "core/hle/kernel/k_thread.h" 23#include "core/hle/kernel/k_thread.h"
24#include "core/hle/service/nvdrv/nvdata.h" 24#include "core/hle/service/nvdrv/nvdata.h"
25#include "core/hle/service/nvflinger/buffer_queue.h" 25#include "core/hle/service/nvflinger/binder.h"
26#include "core/hle/service/nvflinger/buffer_queue_producer.h"
27#include "core/hle/service/nvflinger/hos_binder_driver_server.h"
26#include "core/hle/service/nvflinger/nvflinger.h" 28#include "core/hle/service/nvflinger/nvflinger.h"
29#include "core/hle/service/nvflinger/parcel.h"
27#include "core/hle/service/service.h" 30#include "core/hle/service/service.h"
28#include "core/hle/service/vi/vi.h" 31#include "core/hle/service/vi/vi.h"
29#include "core/hle/service/vi/vi_m.h" 32#include "core/hle/service/vi/vi_m.h"
@@ -57,447 +60,25 @@ struct DisplayInfo {
57}; 60};
58static_assert(sizeof(DisplayInfo) == 0x60, "DisplayInfo has wrong size"); 61static_assert(sizeof(DisplayInfo) == 0x60, "DisplayInfo has wrong size");
59 62
60class Parcel { 63class NativeWindow final {
61public: 64public:
62 // This default size was chosen arbitrarily. 65 constexpr explicit NativeWindow(u32 id_) : id{id_} {}
63 static constexpr std::size_t DefaultBufferSize = 0x40;
64 Parcel() : buffer(DefaultBufferSize) {}
65 explicit Parcel(std::vector<u8> data) : buffer(std::move(data)) {}
66 virtual ~Parcel() = default;
67
68 template <typename T>
69 T Read() {
70 static_assert(std::is_trivially_copyable_v<T>, "T must be trivially copyable.");
71 ASSERT(read_index + sizeof(T) <= buffer.size());
72
73 T val;
74 std::memcpy(&val, buffer.data() + read_index, sizeof(T));
75 read_index += sizeof(T);
76 read_index = Common::AlignUp(read_index, 4);
77 return val;
78 }
79
80 template <typename T>
81 T ReadUnaligned() {
82 static_assert(std::is_trivially_copyable_v<T>, "T must be trivially copyable.");
83 ASSERT(read_index + sizeof(T) <= buffer.size());
84
85 T val;
86 std::memcpy(&val, buffer.data() + read_index, sizeof(T));
87 read_index += sizeof(T);
88 return val;
89 }
90
91 std::vector<u8> ReadBlock(std::size_t length) {
92 ASSERT(read_index + length <= buffer.size());
93 const u8* const begin = buffer.data() + read_index;
94 const u8* const end = begin + length;
95 std::vector<u8> data(begin, end);
96 read_index += length;
97 read_index = Common::AlignUp(read_index, 4);
98 return data;
99 }
100
101 std::u16string ReadInterfaceToken() {
102 [[maybe_unused]] const u32 unknown = Read<u32_le>();
103 const u32 length = Read<u32_le>();
104
105 std::u16string token{};
106
107 for (u32 ch = 0; ch < length + 1; ++ch) {
108 token.push_back(ReadUnaligned<u16_le>());
109 }
110
111 read_index = Common::AlignUp(read_index, 4);
112
113 return token;
114 }
115
116 template <typename T>
117 void Write(const T& val) {
118 static_assert(std::is_trivially_copyable_v<T>, "T must be trivially copyable.");
119
120 if (buffer.size() < write_index + sizeof(T)) {
121 buffer.resize(buffer.size() + sizeof(T) + DefaultBufferSize);
122 }
123
124 std::memcpy(buffer.data() + write_index, &val, sizeof(T));
125 write_index += sizeof(T);
126 write_index = Common::AlignUp(write_index, 4);
127 }
128
129 template <typename T>
130 void WriteObject(const T& val) {
131 static_assert(std::is_trivially_copyable_v<T>, "T must be trivially copyable.");
132
133 const u32_le size = static_cast<u32>(sizeof(val));
134 Write(size);
135 // TODO(Subv): Support file descriptors.
136 Write<u32_le>(0); // Fd count.
137 Write(val);
138 }
139
140 void Deserialize() {
141 ASSERT(buffer.size() > sizeof(Header));
142
143 Header header{};
144 std::memcpy(&header, buffer.data(), sizeof(Header));
145
146 read_index = header.data_offset;
147 DeserializeData();
148 }
149
150 std::vector<u8> Serialize() {
151 ASSERT(read_index == 0);
152 write_index = sizeof(Header);
153
154 SerializeData();
155
156 Header header{};
157 header.data_size = static_cast<u32_le>(write_index - sizeof(Header));
158 header.data_offset = sizeof(Header);
159 header.objects_size = 4;
160 header.objects_offset = static_cast<u32>(sizeof(Header) + header.data_size);
161 std::memcpy(buffer.data(), &header, sizeof(Header));
162
163 return buffer;
164 }
165
166protected:
167 virtual void SerializeData() {}
168
169 virtual void DeserializeData() {}
170
171private:
172 struct Header {
173 u32_le data_size;
174 u32_le data_offset;
175 u32_le objects_size;
176 u32_le objects_offset;
177 };
178 static_assert(sizeof(Header) == 16, "ParcelHeader has wrong size");
179
180 std::vector<u8> buffer;
181 std::size_t read_index = 0;
182 std::size_t write_index = 0;
183};
184
185class NativeWindow : public Parcel {
186public:
187 explicit NativeWindow(u32 id) {
188 data.id = id;
189 }
190 ~NativeWindow() override = default;
191
192protected:
193 void SerializeData() override {
194 Write(data);
195 }
196
197private:
198 struct Data {
199 u32_le magic = 2;
200 u32_le process_id = 1;
201 u32_le id;
202 INSERT_PADDING_WORDS(3);
203 std::array<u8, 8> dispdrv = {'d', 'i', 's', 'p', 'd', 'r', 'v', '\0'};
204 INSERT_PADDING_WORDS(2);
205 };
206 static_assert(sizeof(Data) == 0x28, "ParcelData has wrong size");
207
208 Data data{};
209};
210
211class IGBPConnectRequestParcel : public Parcel {
212public:
213 explicit IGBPConnectRequestParcel(std::vector<u8> buffer_) : Parcel(std::move(buffer_)) {
214 Deserialize();
215 }
216
217 void DeserializeData() override {
218 [[maybe_unused]] const std::u16string token = ReadInterfaceToken();
219 data = Read<Data>();
220 }
221
222 struct Data {
223 u32_le unk;
224 u32_le api;
225 u32_le producer_controlled_by_app;
226 };
227
228 Data data;
229};
230
231class IGBPConnectResponseParcel : public Parcel {
232public:
233 explicit IGBPConnectResponseParcel(u32 width, u32 height) {
234 data.width = width;
235 data.height = height;
236 }
237 ~IGBPConnectResponseParcel() override = default;
238
239protected:
240 void SerializeData() override {
241 Write(data);
242 }
243
244private:
245 struct Data {
246 u32_le width;
247 u32_le height;
248 u32_le transform_hint;
249 u32_le num_pending_buffers;
250 u32_le status;
251 };
252 static_assert(sizeof(Data) == 20, "ParcelData has wrong size");
253
254 Data data{};
255};
256
257/// Represents a parcel containing one int '0' as its data
258/// Used by DetachBuffer and Disconnect
259class IGBPEmptyResponseParcel : public Parcel {
260protected:
261 void SerializeData() override {
262 Write(data);
263 }
264
265private:
266 struct Data {
267 u32_le unk_0{};
268 };
269
270 Data data{};
271};
272
273class IGBPSetPreallocatedBufferRequestParcel : public Parcel {
274public:
275 explicit IGBPSetPreallocatedBufferRequestParcel(std::vector<u8> buffer_)
276 : Parcel(std::move(buffer_)) {
277 Deserialize();
278 }
279
280 void DeserializeData() override {
281 [[maybe_unused]] const std::u16string token = ReadInterfaceToken();
282 data = Read<Data>();
283 if (data.contains_object != 0) {
284 buffer_container = Read<BufferContainer>();
285 }
286 }
287
288 struct Data {
289 u32_le slot;
290 u32_le contains_object;
291 };
292
293 struct BufferContainer {
294 u32_le graphic_buffer_length;
295 INSERT_PADDING_WORDS(1);
296 NVFlinger::IGBPBuffer buffer{};
297 };
298
299 Data data{};
300 BufferContainer buffer_container{};
301};
302
303class IGBPSetPreallocatedBufferResponseParcel : public Parcel {
304protected:
305 void SerializeData() override {
306 // TODO(Subv): Find out what this means
307 Write<u32>(0);
308 }
309};
310
311class IGBPCancelBufferRequestParcel : public Parcel {
312public:
313 explicit IGBPCancelBufferRequestParcel(std::vector<u8> buffer_) : Parcel(std::move(buffer_)) {
314 Deserialize();
315 }
316
317 void DeserializeData() override {
318 [[maybe_unused]] const std::u16string token = ReadInterfaceToken();
319 data = Read<Data>();
320 }
321
322 struct Data {
323 u32_le slot;
324 Service::Nvidia::MultiFence multi_fence;
325 };
326
327 Data data;
328};
329
330class IGBPCancelBufferResponseParcel : public Parcel {
331protected:
332 void SerializeData() override {
333 Write<u32>(0); // Success
334 }
335};
336
337class IGBPDequeueBufferRequestParcel : public Parcel {
338public:
339 explicit IGBPDequeueBufferRequestParcel(std::vector<u8> buffer_) : Parcel(std::move(buffer_)) {
340 Deserialize();
341 }
342
343 void DeserializeData() override {
344 [[maybe_unused]] const std::u16string token = ReadInterfaceToken();
345 data = Read<Data>();
346 }
347
348 struct Data {
349 u32_le pixel_format;
350 u32_le width;
351 u32_le height;
352 u32_le get_frame_timestamps;
353 u32_le usage;
354 };
355
356 Data data;
357};
358
359class IGBPDequeueBufferResponseParcel : public Parcel {
360public:
361 explicit IGBPDequeueBufferResponseParcel(u32 slot_, Nvidia::MultiFence& multi_fence_)
362 : slot(slot_), multi_fence(multi_fence_) {}
363
364protected:
365 void SerializeData() override {
366 Write(slot);
367 Write<u32_le>(1);
368 WriteObject(multi_fence);
369 Write<u32_le>(0);
370 }
371
372 u32_le slot;
373 Service::Nvidia::MultiFence multi_fence;
374};
375
376class IGBPRequestBufferRequestParcel : public Parcel {
377public:
378 explicit IGBPRequestBufferRequestParcel(std::vector<u8> buffer_) : Parcel(std::move(buffer_)) {
379 Deserialize();
380 }
381
382 void DeserializeData() override {
383 [[maybe_unused]] const std::u16string token = ReadInterfaceToken();
384 slot = Read<u32_le>();
385 }
386
387 u32_le slot;
388};
389
390class IGBPRequestBufferResponseParcel : public Parcel {
391public:
392 explicit IGBPRequestBufferResponseParcel(NVFlinger::IGBPBuffer buffer_) : buffer(buffer_) {}
393 ~IGBPRequestBufferResponseParcel() override = default;
394
395protected:
396 void SerializeData() override {
397 // TODO(Subv): Figure out what this value means, writing non-zero here will make libnx
398 // try to read an IGBPBuffer object from the parcel.
399 Write<u32_le>(1);
400 WriteObject(buffer);
401 Write<u32_le>(0);
402 }
403
404 NVFlinger::IGBPBuffer buffer;
405};
406
407class IGBPQueueBufferRequestParcel : public Parcel {
408public:
409 explicit IGBPQueueBufferRequestParcel(std::vector<u8> buffer_) : Parcel(std::move(buffer_)) {
410 Deserialize();
411 }
412
413 void DeserializeData() override {
414 [[maybe_unused]] const std::u16string token = ReadInterfaceToken();
415 data = Read<Data>();
416 }
417
418 struct Data {
419 u32_le slot;
420 INSERT_PADDING_WORDS(3);
421 u32_le timestamp;
422 s32_le is_auto_timestamp;
423 s32_le crop_top;
424 s32_le crop_left;
425 s32_le crop_right;
426 s32_le crop_bottom;
427 s32_le scaling_mode;
428 NVFlinger::BufferQueue::BufferTransformFlags transform;
429 u32_le sticky_transform;
430 INSERT_PADDING_WORDS(1);
431 u32_le swap_interval;
432 Service::Nvidia::MultiFence multi_fence;
433
434 Common::Rectangle<int> GetCropRect() const {
435 return {crop_left, crop_top, crop_right, crop_bottom};
436 }
437 };
438 static_assert(sizeof(Data) == 96, "ParcelData has wrong size");
439
440 Data data;
441};
442
443class IGBPQueueBufferResponseParcel : public Parcel {
444public:
445 explicit IGBPQueueBufferResponseParcel(u32 width, u32 height) {
446 data.width = width;
447 data.height = height;
448 }
449 ~IGBPQueueBufferResponseParcel() override = default;
450
451protected:
452 void SerializeData() override {
453 Write(data);
454 }
455
456private:
457 struct Data {
458 u32_le width;
459 u32_le height;
460 u32_le transform_hint;
461 u32_le num_pending_buffers;
462 u32_le status;
463 };
464 static_assert(sizeof(Data) == 20, "ParcelData has wrong size");
465
466 Data data{};
467};
468
469class IGBPQueryRequestParcel : public Parcel {
470public:
471 explicit IGBPQueryRequestParcel(std::vector<u8> buffer_) : Parcel(std::move(buffer_)) {
472 Deserialize();
473 }
474
475 void DeserializeData() override {
476 [[maybe_unused]] const std::u16string token = ReadInterfaceToken();
477 type = Read<u32_le>();
478 }
479
480 u32 type;
481};
482
483class IGBPQueryResponseParcel : public Parcel {
484public:
485 explicit IGBPQueryResponseParcel(u32 value_) : value{value_} {}
486 ~IGBPQueryResponseParcel() override = default;
487
488protected:
489 void SerializeData() override {
490 Write(value);
491 }
492 66
493private: 67private:
494 u32_le value; 68 const u32 magic = 2;
69 const u32 process_id = 1;
70 const u32 id;
71 INSERT_PADDING_WORDS(3);
72 std::array<u8, 8> dispdrv = {'d', 'i', 's', 'p', 'd', 'r', 'v', '\0'};
73 INSERT_PADDING_WORDS(2);
495}; 74};
75static_assert(sizeof(NativeWindow) == 0x28, "NativeWindow has wrong size");
496 76
497class IHOSBinderDriver final : public ServiceFramework<IHOSBinderDriver> { 77class IHOSBinderDriver final : public ServiceFramework<IHOSBinderDriver> {
498public: 78public:
499 explicit IHOSBinderDriver(Core::System& system_, NVFlinger::NVFlinger& nv_flinger_) 79 explicit IHOSBinderDriver(Core::System& system_, NVFlinger::HosBinderDriverServer& server_)
500 : ServiceFramework{system_, "IHOSBinderDriver"}, nv_flinger(nv_flinger_) { 80 : ServiceFramework{system_, "IHOSBinderDriver", ServiceThreadType::CreateNew},
81 server(server_) {
501 static const FunctionInfo functions[] = { 82 static const FunctionInfo functions[] = {
502 {0, &IHOSBinderDriver::TransactParcel, "TransactParcel"}, 83 {0, &IHOSBinderDriver::TransactParcel, "TransactParcel"},
503 {1, &IHOSBinderDriver::AdjustRefcount, "AdjustRefcount"}, 84 {1, &IHOSBinderDriver::AdjustRefcount, "AdjustRefcount"},
@@ -508,147 +89,16 @@ public:
508 } 89 }
509 90
510private: 91private:
511 enum class TransactionId {
512 RequestBuffer = 1,
513 SetBufferCount = 2,
514 DequeueBuffer = 3,
515 DetachBuffer = 4,
516 DetachNextBuffer = 5,
517 AttachBuffer = 6,
518 QueueBuffer = 7,
519 CancelBuffer = 8,
520 Query = 9,
521 Connect = 10,
522 Disconnect = 11,
523
524 AllocateBuffers = 13,
525 SetPreallocatedBuffer = 14,
526
527 GetBufferHistory = 17
528 };
529
530 void TransactParcel(Kernel::HLERequestContext& ctx) { 92 void TransactParcel(Kernel::HLERequestContext& ctx) {
531 IPC::RequestParser rp{ctx}; 93 IPC::RequestParser rp{ctx};
532 const u32 id = rp.Pop<u32>(); 94 const u32 id = rp.Pop<u32>();
533 const auto transaction = static_cast<TransactionId>(rp.Pop<u32>()); 95 const auto transaction = static_cast<android::TransactionId>(rp.Pop<u32>());
534 const u32 flags = rp.Pop<u32>(); 96 const u32 flags = rp.Pop<u32>();
535 97
536 LOG_DEBUG(Service_VI, "called. id=0x{:08X} transaction={:X}, flags=0x{:08X}", id, 98 LOG_DEBUG(Service_VI, "called. id=0x{:08X} transaction={:X}, flags=0x{:08X}", id,
537 transaction, flags); 99 transaction, flags);
538 100
539 auto& buffer_queue = *nv_flinger.FindBufferQueue(id); 101 server.TryGetProducer(id)->Transact(ctx, transaction, flags);
540
541 switch (transaction) {
542 case TransactionId::Connect: {
543 IGBPConnectRequestParcel request{ctx.ReadBuffer()};
544 IGBPConnectResponseParcel response{static_cast<u32>(DisplayResolution::UndockedWidth),
545 static_cast<u32>(DisplayResolution::UndockedHeight)};
546
547 buffer_queue.Connect();
548
549 ctx.WriteBuffer(response.Serialize());
550 break;
551 }
552 case TransactionId::SetPreallocatedBuffer: {
553 IGBPSetPreallocatedBufferRequestParcel request{ctx.ReadBuffer()};
554
555 buffer_queue.SetPreallocatedBuffer(request.data.slot, request.buffer_container.buffer);
556
557 IGBPSetPreallocatedBufferResponseParcel response{};
558 ctx.WriteBuffer(response.Serialize());
559 break;
560 }
561 case TransactionId::DequeueBuffer: {
562 IGBPDequeueBufferRequestParcel request{ctx.ReadBuffer()};
563 const u32 width{request.data.width};
564 const u32 height{request.data.height};
565
566 do {
567 if (auto result = buffer_queue.DequeueBuffer(width, height); result) {
568 // Buffer is available
569 IGBPDequeueBufferResponseParcel response{result->first, *result->second};
570 ctx.WriteBuffer(response.Serialize());
571 break;
572 }
573 } while (buffer_queue.IsConnected());
574
575 break;
576 }
577 case TransactionId::RequestBuffer: {
578 IGBPRequestBufferRequestParcel request{ctx.ReadBuffer()};
579
580 auto& buffer = buffer_queue.RequestBuffer(request.slot);
581 IGBPRequestBufferResponseParcel response{buffer};
582 ctx.WriteBuffer(response.Serialize());
583
584 break;
585 }
586 case TransactionId::QueueBuffer: {
587 IGBPQueueBufferRequestParcel request{ctx.ReadBuffer()};
588
589 buffer_queue.QueueBuffer(request.data.slot, request.data.transform,
590 request.data.GetCropRect(), request.data.swap_interval,
591 request.data.multi_fence);
592
593 IGBPQueueBufferResponseParcel response{1280, 720};
594 ctx.WriteBuffer(response.Serialize());
595 break;
596 }
597 case TransactionId::Query: {
598 IGBPQueryRequestParcel request{ctx.ReadBuffer()};
599
600 const u32 value =
601 buffer_queue.Query(static_cast<NVFlinger::BufferQueue::QueryType>(request.type));
602
603 IGBPQueryResponseParcel response{value};
604 ctx.WriteBuffer(response.Serialize());
605 break;
606 }
607 case TransactionId::CancelBuffer: {
608 IGBPCancelBufferRequestParcel request{ctx.ReadBuffer()};
609
610 buffer_queue.CancelBuffer(request.data.slot, request.data.multi_fence);
611
612 IGBPCancelBufferResponseParcel response{};
613 ctx.WriteBuffer(response.Serialize());
614 break;
615 }
616 case TransactionId::Disconnect: {
617 LOG_WARNING(Service_VI, "(STUBBED) called, transaction=Disconnect");
618 const auto buffer = ctx.ReadBuffer();
619
620 buffer_queue.Disconnect();
621
622 IGBPEmptyResponseParcel response{};
623 ctx.WriteBuffer(response.Serialize());
624 break;
625 }
626 case TransactionId::DetachBuffer: {
627 const auto buffer = ctx.ReadBuffer();
628
629 IGBPEmptyResponseParcel response{};
630 ctx.WriteBuffer(response.Serialize());
631 break;
632 }
633 case TransactionId::SetBufferCount: {
634 LOG_WARNING(Service_VI, "(STUBBED) called, transaction=SetBufferCount");
635 [[maybe_unused]] const auto buffer = ctx.ReadBuffer();
636
637 IGBPEmptyResponseParcel response{};
638 ctx.WriteBuffer(response.Serialize());
639 break;
640 }
641 case TransactionId::GetBufferHistory: {
642 LOG_WARNING(Service_VI, "(STUBBED) called, transaction=GetBufferHistory");
643 [[maybe_unused]] const auto buffer = ctx.ReadBuffer();
644
645 IGBPEmptyResponseParcel response{};
646 ctx.WriteBuffer(response.Serialize());
647 break;
648 }
649 default:
650 ASSERT_MSG(false, "Unimplemented");
651 }
652 102
653 IPC::ResponseBuilder rb{ctx, 2}; 103 IPC::ResponseBuilder rb{ctx, 2};
654 rb.Push(ResultSuccess); 104 rb.Push(ResultSuccess);
@@ -674,13 +124,13 @@ private:
674 124
675 LOG_WARNING(Service_VI, "(STUBBED) called id={}, unknown={:08X}", id, unknown); 125 LOG_WARNING(Service_VI, "(STUBBED) called id={}, unknown={:08X}", id, unknown);
676 126
677 // TODO(Subv): Find out what this actually is.
678 IPC::ResponseBuilder rb{ctx, 2, 1}; 127 IPC::ResponseBuilder rb{ctx, 2, 1};
679 rb.Push(ResultSuccess); 128 rb.Push(ResultSuccess);
680 rb.PushCopyObjects(nv_flinger.FindBufferQueue(id)->GetBufferWaitEvent()); 129 rb.PushCopyObjects(server.TryGetProducer(id)->GetNativeHandle());
681 } 130 }
682 131
683 NVFlinger::NVFlinger& nv_flinger; 132private:
133 NVFlinger::HosBinderDriverServer& server;
684}; 134};
685 135
686class ISystemDisplayService final : public ServiceFramework<ISystemDisplayService> { 136class ISystemDisplayService final : public ServiceFramework<ISystemDisplayService> {
@@ -937,7 +387,40 @@ private:
937 387
938class IApplicationDisplayService final : public ServiceFramework<IApplicationDisplayService> { 388class IApplicationDisplayService final : public ServiceFramework<IApplicationDisplayService> {
939public: 389public:
940 explicit IApplicationDisplayService(Core::System& system_, NVFlinger::NVFlinger& nv_flinger_); 390 IApplicationDisplayService(Core::System& system_, NVFlinger::NVFlinger& nv_flinger_,
391 NVFlinger::HosBinderDriverServer& hos_binder_driver_server_)
392 : ServiceFramework{system_, "IApplicationDisplayService"}, nv_flinger{nv_flinger_},
393 hos_binder_driver_server{hos_binder_driver_server_} {
394
395 static const FunctionInfo functions[] = {
396 {100, &IApplicationDisplayService::GetRelayService, "GetRelayService"},
397 {101, &IApplicationDisplayService::GetSystemDisplayService, "GetSystemDisplayService"},
398 {102, &IApplicationDisplayService::GetManagerDisplayService,
399 "GetManagerDisplayService"},
400 {103, &IApplicationDisplayService::GetIndirectDisplayTransactionService,
401 "GetIndirectDisplayTransactionService"},
402 {1000, &IApplicationDisplayService::ListDisplays, "ListDisplays"},
403 {1010, &IApplicationDisplayService::OpenDisplay, "OpenDisplay"},
404 {1011, &IApplicationDisplayService::OpenDefaultDisplay, "OpenDefaultDisplay"},
405 {1020, &IApplicationDisplayService::CloseDisplay, "CloseDisplay"},
406 {1101, &IApplicationDisplayService::SetDisplayEnabled, "SetDisplayEnabled"},
407 {1102, &IApplicationDisplayService::GetDisplayResolution, "GetDisplayResolution"},
408 {2020, &IApplicationDisplayService::OpenLayer, "OpenLayer"},
409 {2021, &IApplicationDisplayService::CloseLayer, "CloseLayer"},
410 {2030, &IApplicationDisplayService::CreateStrayLayer, "CreateStrayLayer"},
411 {2031, &IApplicationDisplayService::DestroyStrayLayer, "DestroyStrayLayer"},
412 {2101, &IApplicationDisplayService::SetLayerScalingMode, "SetLayerScalingMode"},
413 {2102, &IApplicationDisplayService::ConvertScalingMode, "ConvertScalingMode"},
414 {2450, &IApplicationDisplayService::GetIndirectLayerImageMap,
415 "GetIndirectLayerImageMap"},
416 {2451, nullptr, "GetIndirectLayerImageCropMap"},
417 {2460, &IApplicationDisplayService::GetIndirectLayerImageRequiredMemoryInfo,
418 "GetIndirectLayerImageRequiredMemoryInfo"},
419 {5202, &IApplicationDisplayService::GetDisplayVsyncEvent, "GetDisplayVsyncEvent"},
420 {5203, nullptr, "GetDisplayVsyncEventForDebug"},
421 };
422 RegisterHandlers(functions);
423 }
941 424
942private: 425private:
943 enum class ConvertedScaleMode : u64 { 426 enum class ConvertedScaleMode : u64 {
@@ -961,7 +444,7 @@ private:
961 444
962 IPC::ResponseBuilder rb{ctx, 2, 0, 1}; 445 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
963 rb.Push(ResultSuccess); 446 rb.Push(ResultSuccess);
964 rb.PushIpcInterface<IHOSBinderDriver>(system, nv_flinger); 447 rb.PushIpcInterface<IHOSBinderDriver>(system, hos_binder_driver_server);
965 } 448 }
966 449
967 void GetSystemDisplayService(Kernel::HLERequestContext& ctx) { 450 void GetSystemDisplayService(Kernel::HLERequestContext& ctx) {
@@ -985,7 +468,7 @@ private:
985 468
986 IPC::ResponseBuilder rb{ctx, 2, 0, 1}; 469 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
987 rb.Push(ResultSuccess); 470 rb.Push(ResultSuccess);
988 rb.PushIpcInterface<IHOSBinderDriver>(system, nv_flinger); 471 rb.PushIpcInterface<IHOSBinderDriver>(system, hos_binder_driver_server);
989 } 472 }
990 473
991 void OpenDisplay(Kernel::HLERequestContext& ctx) { 474 void OpenDisplay(Kernel::HLERequestContext& ctx) {
@@ -1089,7 +572,7 @@ private:
1089 void ListDisplays(Kernel::HLERequestContext& ctx) { 572 void ListDisplays(Kernel::HLERequestContext& ctx) {
1090 LOG_WARNING(Service_VI, "(STUBBED) called"); 573 LOG_WARNING(Service_VI, "(STUBBED) called");
1091 574
1092 DisplayInfo display_info; 575 const DisplayInfo display_info;
1093 ctx.WriteBuffer(&display_info, sizeof(DisplayInfo)); 576 ctx.WriteBuffer(&display_info, sizeof(DisplayInfo));
1094 IPC::ResponseBuilder rb{ctx, 4}; 577 IPC::ResponseBuilder rb{ctx, 4};
1095 rb.Push(ResultSuccess); 578 rb.Push(ResultSuccess);
@@ -1124,8 +607,8 @@ private:
1124 return; 607 return;
1125 } 608 }
1126 609
1127 NativeWindow native_window{*buffer_queue_id}; 610 const auto parcel = android::Parcel{NativeWindow{*buffer_queue_id}};
1128 const auto buffer_size = ctx.WriteBuffer(native_window.Serialize()); 611 const auto buffer_size = ctx.WriteBuffer(parcel.Serialize());
1129 612
1130 IPC::ResponseBuilder rb{ctx, 4}; 613 IPC::ResponseBuilder rb{ctx, 4};
1131 rb.Push(ResultSuccess); 614 rb.Push(ResultSuccess);
@@ -1170,8 +653,8 @@ private:
1170 return; 653 return;
1171 } 654 }
1172 655
1173 NativeWindow native_window{*buffer_queue_id}; 656 const auto parcel = android::Parcel{NativeWindow{*buffer_queue_id}};
1174 const auto buffer_size = ctx.WriteBuffer(native_window.Serialize()); 657 const auto buffer_size = ctx.WriteBuffer(parcel.Serialize());
1175 658
1176 IPC::ResponseBuilder rb{ctx, 6}; 659 IPC::ResponseBuilder rb{ctx, 6};
1177 rb.Push(ResultSuccess); 660 rb.Push(ResultSuccess);
@@ -1287,39 +770,9 @@ private:
1287 } 770 }
1288 771
1289 NVFlinger::NVFlinger& nv_flinger; 772 NVFlinger::NVFlinger& nv_flinger;
773 NVFlinger::HosBinderDriverServer& hos_binder_driver_server;
1290}; 774};
1291 775
1292IApplicationDisplayService::IApplicationDisplayService(Core::System& system_,
1293 NVFlinger::NVFlinger& nv_flinger_)
1294 : ServiceFramework{system_, "IApplicationDisplayService"}, nv_flinger{nv_flinger_} {
1295 static const FunctionInfo functions[] = {
1296 {100, &IApplicationDisplayService::GetRelayService, "GetRelayService"},
1297 {101, &IApplicationDisplayService::GetSystemDisplayService, "GetSystemDisplayService"},
1298 {102, &IApplicationDisplayService::GetManagerDisplayService, "GetManagerDisplayService"},
1299 {103, &IApplicationDisplayService::GetIndirectDisplayTransactionService,
1300 "GetIndirectDisplayTransactionService"},
1301 {1000, &IApplicationDisplayService::ListDisplays, "ListDisplays"},
1302 {1010, &IApplicationDisplayService::OpenDisplay, "OpenDisplay"},
1303 {1011, &IApplicationDisplayService::OpenDefaultDisplay, "OpenDefaultDisplay"},
1304 {1020, &IApplicationDisplayService::CloseDisplay, "CloseDisplay"},
1305 {1101, &IApplicationDisplayService::SetDisplayEnabled, "SetDisplayEnabled"},
1306 {1102, &IApplicationDisplayService::GetDisplayResolution, "GetDisplayResolution"},
1307 {2020, &IApplicationDisplayService::OpenLayer, "OpenLayer"},
1308 {2021, &IApplicationDisplayService::CloseLayer, "CloseLayer"},
1309 {2030, &IApplicationDisplayService::CreateStrayLayer, "CreateStrayLayer"},
1310 {2031, &IApplicationDisplayService::DestroyStrayLayer, "DestroyStrayLayer"},
1311 {2101, &IApplicationDisplayService::SetLayerScalingMode, "SetLayerScalingMode"},
1312 {2102, &IApplicationDisplayService::ConvertScalingMode, "ConvertScalingMode"},
1313 {2450, &IApplicationDisplayService::GetIndirectLayerImageMap, "GetIndirectLayerImageMap"},
1314 {2451, nullptr, "GetIndirectLayerImageCropMap"},
1315 {2460, &IApplicationDisplayService::GetIndirectLayerImageRequiredMemoryInfo,
1316 "GetIndirectLayerImageRequiredMemoryInfo"},
1317 {5202, &IApplicationDisplayService::GetDisplayVsyncEvent, "GetDisplayVsyncEvent"},
1318 {5203, nullptr, "GetDisplayVsyncEventForDebug"},
1319 };
1320 RegisterHandlers(functions);
1321}
1322
1323static bool IsValidServiceAccess(Permission permission, Policy policy) { 776static bool IsValidServiceAccess(Permission permission, Policy policy) {
1324 if (permission == Permission::User) { 777 if (permission == Permission::User) {
1325 return policy == Policy::User; 778 return policy == Policy::User;
@@ -1333,7 +786,9 @@ static bool IsValidServiceAccess(Permission permission, Policy policy) {
1333} 786}
1334 787
1335void detail::GetDisplayServiceImpl(Kernel::HLERequestContext& ctx, Core::System& system, 788void detail::GetDisplayServiceImpl(Kernel::HLERequestContext& ctx, Core::System& system,
1336 NVFlinger::NVFlinger& nv_flinger, Permission permission) { 789 NVFlinger::NVFlinger& nv_flinger,
790 NVFlinger::HosBinderDriverServer& hos_binder_driver_server,
791 Permission permission) {
1337 IPC::RequestParser rp{ctx}; 792 IPC::RequestParser rp{ctx};
1338 const auto policy = rp.PopEnum<Policy>(); 793 const auto policy = rp.PopEnum<Policy>();
1339 794
@@ -1346,14 +801,18 @@ void detail::GetDisplayServiceImpl(Kernel::HLERequestContext& ctx, Core::System&
1346 801
1347 IPC::ResponseBuilder rb{ctx, 2, 0, 1}; 802 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
1348 rb.Push(ResultSuccess); 803 rb.Push(ResultSuccess);
1349 rb.PushIpcInterface<IApplicationDisplayService>(system, nv_flinger); 804 rb.PushIpcInterface<IApplicationDisplayService>(system, nv_flinger, hos_binder_driver_server);
1350} 805}
1351 806
1352void InstallInterfaces(SM::ServiceManager& service_manager, Core::System& system, 807void InstallInterfaces(SM::ServiceManager& service_manager, Core::System& system,
1353 NVFlinger::NVFlinger& nv_flinger) { 808 NVFlinger::NVFlinger& nv_flinger,
1354 std::make_shared<VI_M>(system, nv_flinger)->InstallAsService(service_manager); 809 NVFlinger::HosBinderDriverServer& hos_binder_driver_server) {
1355 std::make_shared<VI_S>(system, nv_flinger)->InstallAsService(service_manager); 810 std::make_shared<VI_M>(system, nv_flinger, hos_binder_driver_server)
1356 std::make_shared<VI_U>(system, nv_flinger)->InstallAsService(service_manager); 811 ->InstallAsService(service_manager);
812 std::make_shared<VI_S>(system, nv_flinger, hos_binder_driver_server)
813 ->InstallAsService(service_manager);
814 std::make_shared<VI_U>(system, nv_flinger, hos_binder_driver_server)
815 ->InstallAsService(service_manager);
1357} 816}
1358 817
1359} // namespace Service::VI 818} // namespace Service::VI
diff --git a/src/core/hle/service/vi/vi.h b/src/core/hle/service/vi/vi.h
index 2fd7f8e61..d68f2646b 100644
--- a/src/core/hle/service/vi/vi.h
+++ b/src/core/hle/service/vi/vi.h
@@ -15,8 +15,9 @@ class HLERequestContext;
15} 15}
16 16
17namespace Service::NVFlinger { 17namespace Service::NVFlinger {
18class HosBinderDriverServer;
18class NVFlinger; 19class NVFlinger;
19} 20} // namespace Service::NVFlinger
20 21
21namespace Service::SM { 22namespace Service::SM {
22class ServiceManager; 23class ServiceManager;
@@ -47,11 +48,14 @@ enum class Policy {
47 48
48namespace detail { 49namespace detail {
49void GetDisplayServiceImpl(Kernel::HLERequestContext& ctx, Core::System& system, 50void GetDisplayServiceImpl(Kernel::HLERequestContext& ctx, Core::System& system,
50 NVFlinger::NVFlinger& nv_flinger, Permission permission); 51 NVFlinger::NVFlinger& nv_flinger,
52 NVFlinger::HosBinderDriverServer& hos_binder_driver_server,
53 Permission permission);
51} // namespace detail 54} // namespace detail
52 55
53/// Registers all VI services with the specified service manager. 56/// Registers all VI services with the specified service manager.
54void InstallInterfaces(SM::ServiceManager& service_manager, Core::System& system, 57void InstallInterfaces(SM::ServiceManager& service_manager, Core::System& system,
55 NVFlinger::NVFlinger& nv_flinger); 58 NVFlinger::NVFlinger& nv_flinger,
59 NVFlinger::HosBinderDriverServer& hos_binder_driver_server);
56 60
57} // namespace Service::VI 61} // namespace Service::VI
diff --git a/src/core/hle/service/vi/vi_m.cpp b/src/core/hle/service/vi/vi_m.cpp
index 87db1c416..be0255f3d 100644
--- a/src/core/hle/service/vi/vi_m.cpp
+++ b/src/core/hle/service/vi/vi_m.cpp
@@ -8,8 +8,10 @@
8 8
9namespace Service::VI { 9namespace Service::VI {
10 10
11VI_M::VI_M(Core::System& system_, NVFlinger::NVFlinger& nv_flinger_) 11VI_M::VI_M(Core::System& system_, NVFlinger::NVFlinger& nv_flinger_,
12 : ServiceFramework{system_, "vi:m"}, nv_flinger{nv_flinger_} { 12 NVFlinger::HosBinderDriverServer& hos_binder_driver_server_)
13 : ServiceFramework{system_, "vi:m"}, nv_flinger{nv_flinger_}, hos_binder_driver_server{
14 hos_binder_driver_server_} {
13 static const FunctionInfo functions[] = { 15 static const FunctionInfo functions[] = {
14 {2, &VI_M::GetDisplayService, "GetDisplayService"}, 16 {2, &VI_M::GetDisplayService, "GetDisplayService"},
15 {3, nullptr, "GetDisplayServiceWithProxyNameExchange"}, 17 {3, nullptr, "GetDisplayServiceWithProxyNameExchange"},
@@ -22,7 +24,8 @@ VI_M::~VI_M() = default;
22void VI_M::GetDisplayService(Kernel::HLERequestContext& ctx) { 24void VI_M::GetDisplayService(Kernel::HLERequestContext& ctx) {
23 LOG_DEBUG(Service_VI, "called"); 25 LOG_DEBUG(Service_VI, "called");
24 26
25 detail::GetDisplayServiceImpl(ctx, system, nv_flinger, Permission::Manager); 27 detail::GetDisplayServiceImpl(ctx, system, nv_flinger, hos_binder_driver_server,
28 Permission::Manager);
26} 29}
27 30
28} // namespace Service::VI 31} // namespace Service::VI
diff --git a/src/core/hle/service/vi/vi_m.h b/src/core/hle/service/vi/vi_m.h
index d79c41beb..efbd34e09 100644
--- a/src/core/hle/service/vi/vi_m.h
+++ b/src/core/hle/service/vi/vi_m.h
@@ -15,20 +15,23 @@ class HLERequestContext;
15} 15}
16 16
17namespace Service::NVFlinger { 17namespace Service::NVFlinger {
18class HosBinderDriverServer;
18class NVFlinger; 19class NVFlinger;
19} 20} // namespace Service::NVFlinger
20 21
21namespace Service::VI { 22namespace Service::VI {
22 23
23class VI_M final : public ServiceFramework<VI_M> { 24class VI_M final : public ServiceFramework<VI_M> {
24public: 25public:
25 explicit VI_M(Core::System& system_, NVFlinger::NVFlinger& nv_flinger_); 26 explicit VI_M(Core::System& system_, NVFlinger::NVFlinger& nv_flinger_,
27 NVFlinger::HosBinderDriverServer& hos_binder_driver_server_);
26 ~VI_M() override; 28 ~VI_M() override;
27 29
28private: 30private:
29 void GetDisplayService(Kernel::HLERequestContext& ctx); 31 void GetDisplayService(Kernel::HLERequestContext& ctx);
30 32
31 NVFlinger::NVFlinger& nv_flinger; 33 NVFlinger::NVFlinger& nv_flinger;
34 NVFlinger::HosBinderDriverServer& hos_binder_driver_server;
32}; 35};
33 36
34} // namespace Service::VI 37} // namespace Service::VI
diff --git a/src/core/hle/service/vi/vi_s.cpp b/src/core/hle/service/vi/vi_s.cpp
index 5cd22f7df..7996a6811 100644
--- a/src/core/hle/service/vi/vi_s.cpp
+++ b/src/core/hle/service/vi/vi_s.cpp
@@ -8,8 +8,10 @@
8 8
9namespace Service::VI { 9namespace Service::VI {
10 10
11VI_S::VI_S(Core::System& system_, NVFlinger::NVFlinger& nv_flinger_) 11VI_S::VI_S(Core::System& system_, NVFlinger::NVFlinger& nv_flinger_,
12 : ServiceFramework{system_, "vi:s"}, nv_flinger{nv_flinger_} { 12 NVFlinger::HosBinderDriverServer& hos_binder_driver_server_)
13 : ServiceFramework{system_, "vi:s"}, nv_flinger{nv_flinger_}, hos_binder_driver_server{
14 hos_binder_driver_server_} {
13 static const FunctionInfo functions[] = { 15 static const FunctionInfo functions[] = {
14 {1, &VI_S::GetDisplayService, "GetDisplayService"}, 16 {1, &VI_S::GetDisplayService, "GetDisplayService"},
15 {3, nullptr, "GetDisplayServiceWithProxyNameExchange"}, 17 {3, nullptr, "GetDisplayServiceWithProxyNameExchange"},
@@ -22,7 +24,8 @@ VI_S::~VI_S() = default;
22void VI_S::GetDisplayService(Kernel::HLERequestContext& ctx) { 24void VI_S::GetDisplayService(Kernel::HLERequestContext& ctx) {
23 LOG_DEBUG(Service_VI, "called"); 25 LOG_DEBUG(Service_VI, "called");
24 26
25 detail::GetDisplayServiceImpl(ctx, system, nv_flinger, Permission::System); 27 detail::GetDisplayServiceImpl(ctx, system, nv_flinger, hos_binder_driver_server,
28 Permission::System);
26} 29}
27 30
28} // namespace Service::VI 31} // namespace Service::VI
diff --git a/src/core/hle/service/vi/vi_s.h b/src/core/hle/service/vi/vi_s.h
index 5f1f8f290..3812c5061 100644
--- a/src/core/hle/service/vi/vi_s.h
+++ b/src/core/hle/service/vi/vi_s.h
@@ -15,20 +15,23 @@ class HLERequestContext;
15} 15}
16 16
17namespace Service::NVFlinger { 17namespace Service::NVFlinger {
18class HosBinderDriverServer;
18class NVFlinger; 19class NVFlinger;
19} 20} // namespace Service::NVFlinger
20 21
21namespace Service::VI { 22namespace Service::VI {
22 23
23class VI_S final : public ServiceFramework<VI_S> { 24class VI_S final : public ServiceFramework<VI_S> {
24public: 25public:
25 explicit VI_S(Core::System& system_, NVFlinger::NVFlinger& nv_flinger_); 26 explicit VI_S(Core::System& system_, NVFlinger::NVFlinger& nv_flinger_,
27 NVFlinger::HosBinderDriverServer& hos_binder_driver_server_);
26 ~VI_S() override; 28 ~VI_S() override;
27 29
28private: 30private:
29 void GetDisplayService(Kernel::HLERequestContext& ctx); 31 void GetDisplayService(Kernel::HLERequestContext& ctx);
30 32
31 NVFlinger::NVFlinger& nv_flinger; 33 NVFlinger::NVFlinger& nv_flinger;
34 NVFlinger::HosBinderDriverServer& hos_binder_driver_server;
32}; 35};
33 36
34} // namespace Service::VI 37} // namespace Service::VI
diff --git a/src/core/hle/service/vi/vi_u.cpp b/src/core/hle/service/vi/vi_u.cpp
index 0079d51f0..57c888313 100644
--- a/src/core/hle/service/vi/vi_u.cpp
+++ b/src/core/hle/service/vi/vi_u.cpp
@@ -8,8 +8,10 @@
8 8
9namespace Service::VI { 9namespace Service::VI {
10 10
11VI_U::VI_U(Core::System& system_, NVFlinger::NVFlinger& nv_flinger_) 11VI_U::VI_U(Core::System& system_, NVFlinger::NVFlinger& nv_flinger_,
12 : ServiceFramework{system_, "vi:u"}, nv_flinger{nv_flinger_} { 12 NVFlinger::HosBinderDriverServer& hos_binder_driver_server_)
13 : ServiceFramework{system_, "vi:u"}, nv_flinger{nv_flinger_}, hos_binder_driver_server{
14 hos_binder_driver_server_} {
13 static const FunctionInfo functions[] = { 15 static const FunctionInfo functions[] = {
14 {0, &VI_U::GetDisplayService, "GetDisplayService"}, 16 {0, &VI_U::GetDisplayService, "GetDisplayService"},
15 {1, nullptr, "GetDisplayServiceWithProxyNameExchange"}, 17 {1, nullptr, "GetDisplayServiceWithProxyNameExchange"},
@@ -22,7 +24,8 @@ VI_U::~VI_U() = default;
22void VI_U::GetDisplayService(Kernel::HLERequestContext& ctx) { 24void VI_U::GetDisplayService(Kernel::HLERequestContext& ctx) {
23 LOG_DEBUG(Service_VI, "called"); 25 LOG_DEBUG(Service_VI, "called");
24 26
25 detail::GetDisplayServiceImpl(ctx, system, nv_flinger, Permission::User); 27 detail::GetDisplayServiceImpl(ctx, system, nv_flinger, hos_binder_driver_server,
28 Permission::User);
26} 29}
27 30
28} // namespace Service::VI 31} // namespace Service::VI
diff --git a/src/core/hle/service/vi/vi_u.h b/src/core/hle/service/vi/vi_u.h
index 8e3885c73..b08e56576 100644
--- a/src/core/hle/service/vi/vi_u.h
+++ b/src/core/hle/service/vi/vi_u.h
@@ -15,20 +15,23 @@ class HLERequestContext;
15} 15}
16 16
17namespace Service::NVFlinger { 17namespace Service::NVFlinger {
18class HosBinderDriverServer;
18class NVFlinger; 19class NVFlinger;
19} 20} // namespace Service::NVFlinger
20 21
21namespace Service::VI { 22namespace Service::VI {
22 23
23class VI_U final : public ServiceFramework<VI_U> { 24class VI_U final : public ServiceFramework<VI_U> {
24public: 25public:
25 explicit VI_U(Core::System& system_, NVFlinger::NVFlinger& nv_flinger_); 26 explicit VI_U(Core::System& system_, NVFlinger::NVFlinger& nv_flinger_,
27 NVFlinger::HosBinderDriverServer& hos_binder_driver_server_);
26 ~VI_U() override; 28 ~VI_U() override;
27 29
28private: 30private:
29 void GetDisplayService(Kernel::HLERequestContext& ctx); 31 void GetDisplayService(Kernel::HLERequestContext& ctx);
30 32
31 NVFlinger::NVFlinger& nv_flinger; 33 NVFlinger::NVFlinger& nv_flinger;
34 NVFlinger::HosBinderDriverServer& hos_binder_driver_server;
32}; 35};
33 36
34} // namespace Service::VI 37} // namespace Service::VI
diff --git a/src/core/network/network.cpp b/src/core/network/network.cpp
index a3e0664b9..0784a165d 100644
--- a/src/core/network/network.cpp
+++ b/src/core/network/network.cpp
@@ -600,6 +600,10 @@ Errno Socket::SetReuseAddr(bool enable) {
600 return SetSockOpt<u32>(fd, SO_REUSEADDR, enable ? 1 : 0); 600 return SetSockOpt<u32>(fd, SO_REUSEADDR, enable ? 1 : 0);
601} 601}
602 602
603Errno Socket::SetKeepAlive(bool enable) {
604 return SetSockOpt<u32>(fd, SO_KEEPALIVE, enable ? 1 : 0);
605}
606
603Errno Socket::SetBroadcast(bool enable) { 607Errno Socket::SetBroadcast(bool enable) {
604 return SetSockOpt<u32>(fd, SO_BROADCAST, enable ? 1 : 0); 608 return SetSockOpt<u32>(fd, SO_BROADCAST, enable ? 1 : 0);
605} 609}
diff --git a/src/core/network/sockets.h b/src/core/network/sockets.h
index 5e39e7c54..caaefce7c 100644
--- a/src/core/network/sockets.h
+++ b/src/core/network/sockets.h
@@ -67,6 +67,8 @@ public:
67 67
68 Errno SetReuseAddr(bool enable); 68 Errno SetReuseAddr(bool enable);
69 69
70 Errno SetKeepAlive(bool enable);
71
70 Errno SetBroadcast(bool enable); 72 Errno SetBroadcast(bool enable);
71 73
72 Errno SetSndBuf(u32 value); 74 Errno SetSndBuf(u32 value);
diff --git a/src/core/perf_stats.cpp b/src/core/perf_stats.cpp
index 52c43c857..6ef459b7a 100644
--- a/src/core/perf_stats.cpp
+++ b/src/core/perf_stats.cpp
@@ -53,13 +53,13 @@ PerfStats::~PerfStats() {
53} 53}
54 54
55void PerfStats::BeginSystemFrame() { 55void PerfStats::BeginSystemFrame() {
56 std::lock_guard lock{object_mutex}; 56 std::scoped_lock lock{object_mutex};
57 57
58 frame_begin = Clock::now(); 58 frame_begin = Clock::now();
59} 59}
60 60
61void PerfStats::EndSystemFrame() { 61void PerfStats::EndSystemFrame() {
62 std::lock_guard lock{object_mutex}; 62 std::scoped_lock lock{object_mutex};
63 63
64 auto frame_end = Clock::now(); 64 auto frame_end = Clock::now();
65 const auto frame_time = frame_end - frame_begin; 65 const auto frame_time = frame_end - frame_begin;
@@ -79,7 +79,7 @@ void PerfStats::EndGameFrame() {
79} 79}
80 80
81double PerfStats::GetMeanFrametime() const { 81double PerfStats::GetMeanFrametime() const {
82 std::lock_guard lock{object_mutex}; 82 std::scoped_lock lock{object_mutex};
83 83
84 if (current_index <= IgnoreFrames) { 84 if (current_index <= IgnoreFrames) {
85 return 0; 85 return 0;
@@ -91,7 +91,7 @@ double PerfStats::GetMeanFrametime() const {
91} 91}
92 92
93PerfStatsResults PerfStats::GetAndResetStats(microseconds current_system_time_us) { 93PerfStatsResults PerfStats::GetAndResetStats(microseconds current_system_time_us) {
94 std::lock_guard lock{object_mutex}; 94 std::scoped_lock lock{object_mutex};
95 95
96 const auto now = Clock::now(); 96 const auto now = Clock::now();
97 // Walltime elapsed since stats were reset 97 // Walltime elapsed since stats were reset
@@ -120,7 +120,7 @@ PerfStatsResults PerfStats::GetAndResetStats(microseconds current_system_time_us
120} 120}
121 121
122double PerfStats::GetLastFrameTimeScale() const { 122double PerfStats::GetLastFrameTimeScale() const {
123 std::lock_guard lock{object_mutex}; 123 std::scoped_lock lock{object_mutex};
124 124
125 constexpr double FRAME_LENGTH = 1.0 / 60; 125 constexpr double FRAME_LENGTH = 1.0 / 60;
126 return duration_cast<DoubleSecs>(previous_frame_length).count() / FRAME_LENGTH; 126 return duration_cast<DoubleSecs>(previous_frame_length).count() / FRAME_LENGTH;
diff --git a/src/core/tools/freezer.cpp b/src/core/tools/freezer.cpp
index 032c71aff..c81dc0e52 100644
--- a/src/core/tools/freezer.cpp
+++ b/src/core/tools/freezer.cpp
@@ -80,7 +80,7 @@ bool Freezer::IsActive() const {
80} 80}
81 81
82void Freezer::Clear() { 82void Freezer::Clear() {
83 std::lock_guard lock{entries_mutex}; 83 std::scoped_lock lock{entries_mutex};
84 84
85 LOG_DEBUG(Common_Memory, "Clearing all frozen memory values."); 85 LOG_DEBUG(Common_Memory, "Clearing all frozen memory values.");
86 86
@@ -88,7 +88,7 @@ void Freezer::Clear() {
88} 88}
89 89
90u64 Freezer::Freeze(VAddr address, u32 width) { 90u64 Freezer::Freeze(VAddr address, u32 width) {
91 std::lock_guard lock{entries_mutex}; 91 std::scoped_lock lock{entries_mutex};
92 92
93 const auto current_value = MemoryReadWidth(memory, width, address); 93 const auto current_value = MemoryReadWidth(memory, width, address);
94 entries.push_back({address, width, current_value}); 94 entries.push_back({address, width, current_value});
@@ -101,7 +101,7 @@ u64 Freezer::Freeze(VAddr address, u32 width) {
101} 101}
102 102
103void Freezer::Unfreeze(VAddr address) { 103void Freezer::Unfreeze(VAddr address) {
104 std::lock_guard lock{entries_mutex}; 104 std::scoped_lock lock{entries_mutex};
105 105
106 LOG_DEBUG(Common_Memory, "Unfreezing memory for address={:016X}", address); 106 LOG_DEBUG(Common_Memory, "Unfreezing memory for address={:016X}", address);
107 107
@@ -109,13 +109,13 @@ void Freezer::Unfreeze(VAddr address) {
109} 109}
110 110
111bool Freezer::IsFrozen(VAddr address) const { 111bool Freezer::IsFrozen(VAddr address) const {
112 std::lock_guard lock{entries_mutex}; 112 std::scoped_lock lock{entries_mutex};
113 113
114 return FindEntry(address) != entries.cend(); 114 return FindEntry(address) != entries.cend();
115} 115}
116 116
117void Freezer::SetFrozenValue(VAddr address, u64 value) { 117void Freezer::SetFrozenValue(VAddr address, u64 value) {
118 std::lock_guard lock{entries_mutex}; 118 std::scoped_lock lock{entries_mutex};
119 119
120 const auto iter = FindEntry(address); 120 const auto iter = FindEntry(address);
121 121
@@ -132,7 +132,7 @@ void Freezer::SetFrozenValue(VAddr address, u64 value) {
132} 132}
133 133
134std::optional<Freezer::Entry> Freezer::GetEntry(VAddr address) const { 134std::optional<Freezer::Entry> Freezer::GetEntry(VAddr address) const {
135 std::lock_guard lock{entries_mutex}; 135 std::scoped_lock lock{entries_mutex};
136 136
137 const auto iter = FindEntry(address); 137 const auto iter = FindEntry(address);
138 138
@@ -144,7 +144,7 @@ std::optional<Freezer::Entry> Freezer::GetEntry(VAddr address) const {
144} 144}
145 145
146std::vector<Freezer::Entry> Freezer::GetEntries() const { 146std::vector<Freezer::Entry> Freezer::GetEntries() const {
147 std::lock_guard lock{entries_mutex}; 147 std::scoped_lock lock{entries_mutex};
148 148
149 return entries; 149 return entries;
150} 150}
@@ -165,7 +165,7 @@ void Freezer::FrameCallback(std::uintptr_t, std::chrono::nanoseconds ns_late) {
165 return; 165 return;
166 } 166 }
167 167
168 std::lock_guard lock{entries_mutex}; 168 std::scoped_lock lock{entries_mutex};
169 169
170 for (const auto& entry : entries) { 170 for (const auto& entry : entries) {
171 LOG_DEBUG(Common_Memory, 171 LOG_DEBUG(Common_Memory,
@@ -178,7 +178,7 @@ void Freezer::FrameCallback(std::uintptr_t, std::chrono::nanoseconds ns_late) {
178} 178}
179 179
180void Freezer::FillEntryReads() { 180void Freezer::FillEntryReads() {
181 std::lock_guard lock{entries_mutex}; 181 std::scoped_lock lock{entries_mutex};
182 182
183 LOG_DEBUG(Common_Memory, "Updating memory freeze entries to current values."); 183 LOG_DEBUG(Common_Memory, "Updating memory freeze entries to current values.");
184 184
diff --git a/src/input_common/drivers/sdl_driver.cpp b/src/input_common/drivers/sdl_driver.cpp
index 64edf53e4..a5c63e74a 100644
--- a/src/input_common/drivers/sdl_driver.cpp
+++ b/src/input_common/drivers/sdl_driver.cpp
@@ -62,7 +62,7 @@ public:
62 62
63 bool UpdateMotion(SDL_ControllerSensorEvent event) { 63 bool UpdateMotion(SDL_ControllerSensorEvent event) {
64 constexpr float gravity_constant = 9.80665f; 64 constexpr float gravity_constant = 9.80665f;
65 std::lock_guard lock{mutex}; 65 std::scoped_lock lock{mutex};
66 const u64 time_difference = event.timestamp - last_motion_update; 66 const u64 time_difference = event.timestamp - last_motion_update;
67 last_motion_update = event.timestamp; 67 last_motion_update = event.timestamp;
68 switch (event.sensor) { 68 switch (event.sensor) {
@@ -241,7 +241,7 @@ private:
241}; 241};
242 242
243std::shared_ptr<SDLJoystick> SDLDriver::GetSDLJoystickByGUID(const std::string& guid, int port) { 243std::shared_ptr<SDLJoystick> SDLDriver::GetSDLJoystickByGUID(const std::string& guid, int port) {
244 std::lock_guard lock{joystick_map_mutex}; 244 std::scoped_lock lock{joystick_map_mutex};
245 const auto it = joystick_map.find(guid); 245 const auto it = joystick_map.find(guid);
246 246
247 if (it != joystick_map.end()) { 247 if (it != joystick_map.end()) {
@@ -263,7 +263,7 @@ std::shared_ptr<SDLJoystick> SDLDriver::GetSDLJoystickBySDLID(SDL_JoystickID sdl
263 auto sdl_joystick = SDL_JoystickFromInstanceID(sdl_id); 263 auto sdl_joystick = SDL_JoystickFromInstanceID(sdl_id);
264 const std::string guid = GetGUID(sdl_joystick); 264 const std::string guid = GetGUID(sdl_joystick);
265 265
266 std::lock_guard lock{joystick_map_mutex}; 266 std::scoped_lock lock{joystick_map_mutex};
267 const auto map_it = joystick_map.find(guid); 267 const auto map_it = joystick_map.find(guid);
268 268
269 if (map_it == joystick_map.end()) { 269 if (map_it == joystick_map.end()) {
@@ -297,7 +297,7 @@ void SDLDriver::InitJoystick(int joystick_index) {
297 297
298 const std::string guid = GetGUID(sdl_joystick); 298 const std::string guid = GetGUID(sdl_joystick);
299 299
300 std::lock_guard lock{joystick_map_mutex}; 300 std::scoped_lock lock{joystick_map_mutex};
301 if (joystick_map.find(guid) == joystick_map.end()) { 301 if (joystick_map.find(guid) == joystick_map.end()) {
302 auto joystick = std::make_shared<SDLJoystick>(guid, 0, sdl_joystick, sdl_gamecontroller); 302 auto joystick = std::make_shared<SDLJoystick>(guid, 0, sdl_joystick, sdl_gamecontroller);
303 PreSetController(joystick->GetPadIdentifier()); 303 PreSetController(joystick->GetPadIdentifier());
@@ -326,7 +326,7 @@ void SDLDriver::InitJoystick(int joystick_index) {
326void SDLDriver::CloseJoystick(SDL_Joystick* sdl_joystick) { 326void SDLDriver::CloseJoystick(SDL_Joystick* sdl_joystick) {
327 const std::string guid = GetGUID(sdl_joystick); 327 const std::string guid = GetGUID(sdl_joystick);
328 328
329 std::lock_guard lock{joystick_map_mutex}; 329 std::scoped_lock lock{joystick_map_mutex};
330 // This call to guid is safe since the joystick is guaranteed to be in the map 330 // This call to guid is safe since the joystick is guaranteed to be in the map
331 const auto& joystick_guid_list = joystick_map[guid]; 331 const auto& joystick_guid_list = joystick_map[guid];
332 const auto joystick_it = std::find_if(joystick_guid_list.begin(), joystick_guid_list.end(), 332 const auto joystick_it = std::find_if(joystick_guid_list.begin(), joystick_guid_list.end(),
@@ -392,7 +392,7 @@ void SDLDriver::HandleGameControllerEvent(const SDL_Event& event) {
392} 392}
393 393
394void SDLDriver::CloseJoysticks() { 394void SDLDriver::CloseJoysticks() {
395 std::lock_guard lock{joystick_map_mutex}; 395 std::scoped_lock lock{joystick_map_mutex};
396 joystick_map.clear(); 396 joystick_map.clear();
397} 397}
398 398
diff --git a/src/input_common/helpers/udp_protocol.h b/src/input_common/helpers/udp_protocol.h
index 9c205e944..597f51cd3 100644
--- a/src/input_common/helpers/udp_protocol.h
+++ b/src/input_common/helpers/udp_protocol.h
@@ -8,8 +8,17 @@
8#include <optional> 8#include <optional>
9#include <type_traits> 9#include <type_traits>
10 10
11#ifdef _MSC_VER
12#pragma warning(push)
13#pragma warning(disable : 4701) // Potentially uninitialized local variable 'result' used
14#endif
15
11#include <boost/crc.hpp> 16#include <boost/crc.hpp>
12 17
18#ifdef _MSC_VER
19#pragma warning(pop)
20#endif
21
13#include "common/swap.h" 22#include "common/swap.h"
14 23
15namespace InputCommon::CemuhookUDP { 24namespace InputCommon::CemuhookUDP {
diff --git a/src/input_common/input_engine.cpp b/src/input_common/input_engine.cpp
index 738022ece..a16cca33d 100644
--- a/src/input_common/input_engine.cpp
+++ b/src/input_common/input_engine.cpp
@@ -8,37 +8,37 @@
8namespace InputCommon { 8namespace InputCommon {
9 9
10void InputEngine::PreSetController(const PadIdentifier& identifier) { 10void InputEngine::PreSetController(const PadIdentifier& identifier) {
11 std::lock_guard lock{mutex}; 11 std::scoped_lock lock{mutex};
12 controller_list.try_emplace(identifier); 12 controller_list.try_emplace(identifier);
13} 13}
14 14
15void InputEngine::PreSetButton(const PadIdentifier& identifier, int button) { 15void InputEngine::PreSetButton(const PadIdentifier& identifier, int button) {
16 std::lock_guard lock{mutex}; 16 std::scoped_lock lock{mutex};
17 ControllerData& controller = controller_list.at(identifier); 17 ControllerData& controller = controller_list.at(identifier);
18 controller.buttons.try_emplace(button, false); 18 controller.buttons.try_emplace(button, false);
19} 19}
20 20
21void InputEngine::PreSetHatButton(const PadIdentifier& identifier, int button) { 21void InputEngine::PreSetHatButton(const PadIdentifier& identifier, int button) {
22 std::lock_guard lock{mutex}; 22 std::scoped_lock lock{mutex};
23 ControllerData& controller = controller_list.at(identifier); 23 ControllerData& controller = controller_list.at(identifier);
24 controller.hat_buttons.try_emplace(button, u8{0}); 24 controller.hat_buttons.try_emplace(button, u8{0});
25} 25}
26 26
27void InputEngine::PreSetAxis(const PadIdentifier& identifier, int axis) { 27void InputEngine::PreSetAxis(const PadIdentifier& identifier, int axis) {
28 std::lock_guard lock{mutex}; 28 std::scoped_lock lock{mutex};
29 ControllerData& controller = controller_list.at(identifier); 29 ControllerData& controller = controller_list.at(identifier);
30 controller.axes.try_emplace(axis, 0.0f); 30 controller.axes.try_emplace(axis, 0.0f);
31} 31}
32 32
33void InputEngine::PreSetMotion(const PadIdentifier& identifier, int motion) { 33void InputEngine::PreSetMotion(const PadIdentifier& identifier, int motion) {
34 std::lock_guard lock{mutex}; 34 std::scoped_lock lock{mutex};
35 ControllerData& controller = controller_list.at(identifier); 35 ControllerData& controller = controller_list.at(identifier);
36 controller.motions.try_emplace(motion); 36 controller.motions.try_emplace(motion);
37} 37}
38 38
39void InputEngine::SetButton(const PadIdentifier& identifier, int button, bool value) { 39void InputEngine::SetButton(const PadIdentifier& identifier, int button, bool value) {
40 { 40 {
41 std::lock_guard lock{mutex}; 41 std::scoped_lock lock{mutex};
42 ControllerData& controller = controller_list.at(identifier); 42 ControllerData& controller = controller_list.at(identifier);
43 if (!configuring) { 43 if (!configuring) {
44 controller.buttons.insert_or_assign(button, value); 44 controller.buttons.insert_or_assign(button, value);
@@ -49,7 +49,7 @@ void InputEngine::SetButton(const PadIdentifier& identifier, int button, bool va
49 49
50void InputEngine::SetHatButton(const PadIdentifier& identifier, int button, u8 value) { 50void InputEngine::SetHatButton(const PadIdentifier& identifier, int button, u8 value) {
51 { 51 {
52 std::lock_guard lock{mutex}; 52 std::scoped_lock lock{mutex};
53 ControllerData& controller = controller_list.at(identifier); 53 ControllerData& controller = controller_list.at(identifier);
54 if (!configuring) { 54 if (!configuring) {
55 controller.hat_buttons.insert_or_assign(button, value); 55 controller.hat_buttons.insert_or_assign(button, value);
@@ -60,7 +60,7 @@ void InputEngine::SetHatButton(const PadIdentifier& identifier, int button, u8 v
60 60
61void InputEngine::SetAxis(const PadIdentifier& identifier, int axis, f32 value) { 61void InputEngine::SetAxis(const PadIdentifier& identifier, int axis, f32 value) {
62 { 62 {
63 std::lock_guard lock{mutex}; 63 std::scoped_lock lock{mutex};
64 ControllerData& controller = controller_list.at(identifier); 64 ControllerData& controller = controller_list.at(identifier);
65 if (!configuring) { 65 if (!configuring) {
66 controller.axes.insert_or_assign(axis, value); 66 controller.axes.insert_or_assign(axis, value);
@@ -71,7 +71,7 @@ void InputEngine::SetAxis(const PadIdentifier& identifier, int axis, f32 value)
71 71
72void InputEngine::SetBattery(const PadIdentifier& identifier, Common::Input::BatteryLevel value) { 72void InputEngine::SetBattery(const PadIdentifier& identifier, Common::Input::BatteryLevel value) {
73 { 73 {
74 std::lock_guard lock{mutex}; 74 std::scoped_lock lock{mutex};
75 ControllerData& controller = controller_list.at(identifier); 75 ControllerData& controller = controller_list.at(identifier);
76 if (!configuring) { 76 if (!configuring) {
77 controller.battery = value; 77 controller.battery = value;
@@ -82,7 +82,7 @@ void InputEngine::SetBattery(const PadIdentifier& identifier, Common::Input::Bat
82 82
83void InputEngine::SetMotion(const PadIdentifier& identifier, int motion, const BasicMotion& value) { 83void InputEngine::SetMotion(const PadIdentifier& identifier, int motion, const BasicMotion& value) {
84 { 84 {
85 std::lock_guard lock{mutex}; 85 std::scoped_lock lock{mutex};
86 ControllerData& controller = controller_list.at(identifier); 86 ControllerData& controller = controller_list.at(identifier);
87 if (!configuring) { 87 if (!configuring) {
88 controller.motions.insert_or_assign(motion, value); 88 controller.motions.insert_or_assign(motion, value);
@@ -92,7 +92,7 @@ void InputEngine::SetMotion(const PadIdentifier& identifier, int motion, const B
92} 92}
93 93
94bool InputEngine::GetButton(const PadIdentifier& identifier, int button) const { 94bool InputEngine::GetButton(const PadIdentifier& identifier, int button) const {
95 std::lock_guard lock{mutex}; 95 std::scoped_lock lock{mutex};
96 const auto controller_iter = controller_list.find(identifier); 96 const auto controller_iter = controller_list.find(identifier);
97 if (controller_iter == controller_list.cend()) { 97 if (controller_iter == controller_list.cend()) {
98 LOG_ERROR(Input, "Invalid identifier guid={}, pad={}, port={}", identifier.guid.RawString(), 98 LOG_ERROR(Input, "Invalid identifier guid={}, pad={}, port={}", identifier.guid.RawString(),
@@ -109,7 +109,7 @@ bool InputEngine::GetButton(const PadIdentifier& identifier, int button) const {
109} 109}
110 110
111bool InputEngine::GetHatButton(const PadIdentifier& identifier, int button, u8 direction) const { 111bool InputEngine::GetHatButton(const PadIdentifier& identifier, int button, u8 direction) const {
112 std::lock_guard lock{mutex}; 112 std::scoped_lock lock{mutex};
113 const auto controller_iter = controller_list.find(identifier); 113 const auto controller_iter = controller_list.find(identifier);
114 if (controller_iter == controller_list.cend()) { 114 if (controller_iter == controller_list.cend()) {
115 LOG_ERROR(Input, "Invalid identifier guid={}, pad={}, port={}", identifier.guid.RawString(), 115 LOG_ERROR(Input, "Invalid identifier guid={}, pad={}, port={}", identifier.guid.RawString(),
@@ -126,7 +126,7 @@ bool InputEngine::GetHatButton(const PadIdentifier& identifier, int button, u8 d
126} 126}
127 127
128f32 InputEngine::GetAxis(const PadIdentifier& identifier, int axis) const { 128f32 InputEngine::GetAxis(const PadIdentifier& identifier, int axis) const {
129 std::lock_guard lock{mutex}; 129 std::scoped_lock lock{mutex};
130 const auto controller_iter = controller_list.find(identifier); 130 const auto controller_iter = controller_list.find(identifier);
131 if (controller_iter == controller_list.cend()) { 131 if (controller_iter == controller_list.cend()) {
132 LOG_ERROR(Input, "Invalid identifier guid={}, pad={}, port={}", identifier.guid.RawString(), 132 LOG_ERROR(Input, "Invalid identifier guid={}, pad={}, port={}", identifier.guid.RawString(),
@@ -143,7 +143,7 @@ f32 InputEngine::GetAxis(const PadIdentifier& identifier, int axis) const {
143} 143}
144 144
145Common::Input::BatteryLevel InputEngine::GetBattery(const PadIdentifier& identifier) const { 145Common::Input::BatteryLevel InputEngine::GetBattery(const PadIdentifier& identifier) const {
146 std::lock_guard lock{mutex}; 146 std::scoped_lock lock{mutex};
147 const auto controller_iter = controller_list.find(identifier); 147 const auto controller_iter = controller_list.find(identifier);
148 if (controller_iter == controller_list.cend()) { 148 if (controller_iter == controller_list.cend()) {
149 LOG_ERROR(Input, "Invalid identifier guid={}, pad={}, port={}", identifier.guid.RawString(), 149 LOG_ERROR(Input, "Invalid identifier guid={}, pad={}, port={}", identifier.guid.RawString(),
@@ -155,7 +155,7 @@ Common::Input::BatteryLevel InputEngine::GetBattery(const PadIdentifier& identif
155} 155}
156 156
157BasicMotion InputEngine::GetMotion(const PadIdentifier& identifier, int motion) const { 157BasicMotion InputEngine::GetMotion(const PadIdentifier& identifier, int motion) const {
158 std::lock_guard lock{mutex}; 158 std::scoped_lock lock{mutex};
159 const auto controller_iter = controller_list.find(identifier); 159 const auto controller_iter = controller_list.find(identifier);
160 if (controller_iter == controller_list.cend()) { 160 if (controller_iter == controller_list.cend()) {
161 LOG_ERROR(Input, "Invalid identifier guid={}, pad={}, port={}", identifier.guid.RawString(), 161 LOG_ERROR(Input, "Invalid identifier guid={}, pad={}, port={}", identifier.guid.RawString(),
@@ -186,7 +186,7 @@ void InputEngine::ResetAnalogState() {
186} 186}
187 187
188void InputEngine::TriggerOnButtonChange(const PadIdentifier& identifier, int button, bool value) { 188void InputEngine::TriggerOnButtonChange(const PadIdentifier& identifier, int button, bool value) {
189 std::lock_guard lock{mutex_callback}; 189 std::scoped_lock lock{mutex_callback};
190 for (const auto& poller_pair : callback_list) { 190 for (const auto& poller_pair : callback_list) {
191 const InputIdentifier& poller = poller_pair.second; 191 const InputIdentifier& poller = poller_pair.second;
192 if (!IsInputIdentifierEqual(poller, identifier, EngineInputType::Button, button)) { 192 if (!IsInputIdentifierEqual(poller, identifier, EngineInputType::Button, button)) {
@@ -214,7 +214,7 @@ void InputEngine::TriggerOnButtonChange(const PadIdentifier& identifier, int but
214} 214}
215 215
216void InputEngine::TriggerOnHatButtonChange(const PadIdentifier& identifier, int button, u8 value) { 216void InputEngine::TriggerOnHatButtonChange(const PadIdentifier& identifier, int button, u8 value) {
217 std::lock_guard lock{mutex_callback}; 217 std::scoped_lock lock{mutex_callback};
218 for (const auto& poller_pair : callback_list) { 218 for (const auto& poller_pair : callback_list) {
219 const InputIdentifier& poller = poller_pair.second; 219 const InputIdentifier& poller = poller_pair.second;
220 if (!IsInputIdentifierEqual(poller, identifier, EngineInputType::HatButton, button)) { 220 if (!IsInputIdentifierEqual(poller, identifier, EngineInputType::HatButton, button)) {
@@ -243,7 +243,7 @@ void InputEngine::TriggerOnHatButtonChange(const PadIdentifier& identifier, int
243} 243}
244 244
245void InputEngine::TriggerOnAxisChange(const PadIdentifier& identifier, int axis, f32 value) { 245void InputEngine::TriggerOnAxisChange(const PadIdentifier& identifier, int axis, f32 value) {
246 std::lock_guard lock{mutex_callback}; 246 std::scoped_lock lock{mutex_callback};
247 for (const auto& poller_pair : callback_list) { 247 for (const auto& poller_pair : callback_list) {
248 const InputIdentifier& poller = poller_pair.second; 248 const InputIdentifier& poller = poller_pair.second;
249 if (!IsInputIdentifierEqual(poller, identifier, EngineInputType::Analog, axis)) { 249 if (!IsInputIdentifierEqual(poller, identifier, EngineInputType::Analog, axis)) {
@@ -270,7 +270,7 @@ void InputEngine::TriggerOnAxisChange(const PadIdentifier& identifier, int axis,
270 270
271void InputEngine::TriggerOnBatteryChange(const PadIdentifier& identifier, 271void InputEngine::TriggerOnBatteryChange(const PadIdentifier& identifier,
272 [[maybe_unused]] Common::Input::BatteryLevel value) { 272 [[maybe_unused]] Common::Input::BatteryLevel value) {
273 std::lock_guard lock{mutex_callback}; 273 std::scoped_lock lock{mutex_callback};
274 for (const auto& poller_pair : callback_list) { 274 for (const auto& poller_pair : callback_list) {
275 const InputIdentifier& poller = poller_pair.second; 275 const InputIdentifier& poller = poller_pair.second;
276 if (!IsInputIdentifierEqual(poller, identifier, EngineInputType::Battery, 0)) { 276 if (!IsInputIdentifierEqual(poller, identifier, EngineInputType::Battery, 0)) {
@@ -284,7 +284,7 @@ void InputEngine::TriggerOnBatteryChange(const PadIdentifier& identifier,
284 284
285void InputEngine::TriggerOnMotionChange(const PadIdentifier& identifier, int motion, 285void InputEngine::TriggerOnMotionChange(const PadIdentifier& identifier, int motion,
286 const BasicMotion& value) { 286 const BasicMotion& value) {
287 std::lock_guard lock{mutex_callback}; 287 std::scoped_lock lock{mutex_callback};
288 for (const auto& poller_pair : callback_list) { 288 for (const auto& poller_pair : callback_list) {
289 const InputIdentifier& poller = poller_pair.second; 289 const InputIdentifier& poller = poller_pair.second;
290 if (!IsInputIdentifierEqual(poller, identifier, EngineInputType::Motion, motion)) { 290 if (!IsInputIdentifierEqual(poller, identifier, EngineInputType::Motion, motion)) {
@@ -346,18 +346,18 @@ const std::string& InputEngine::GetEngineName() const {
346} 346}
347 347
348int InputEngine::SetCallback(InputIdentifier input_identifier) { 348int InputEngine::SetCallback(InputIdentifier input_identifier) {
349 std::lock_guard lock{mutex_callback}; 349 std::scoped_lock lock{mutex_callback};
350 callback_list.insert_or_assign(last_callback_key, std::move(input_identifier)); 350 callback_list.insert_or_assign(last_callback_key, std::move(input_identifier));
351 return last_callback_key++; 351 return last_callback_key++;
352} 352}
353 353
354void InputEngine::SetMappingCallback(MappingCallback callback) { 354void InputEngine::SetMappingCallback(MappingCallback callback) {
355 std::lock_guard lock{mutex_callback}; 355 std::scoped_lock lock{mutex_callback};
356 mapping_callback = std::move(callback); 356 mapping_callback = std::move(callback);
357} 357}
358 358
359void InputEngine::DeleteCallback(int key) { 359void InputEngine::DeleteCallback(int key) {
360 std::lock_guard lock{mutex_callback}; 360 std::scoped_lock lock{mutex_callback};
361 const auto& iterator = callback_list.find(key); 361 const auto& iterator = callback_list.find(key);
362 if (iterator == callback_list.end()) { 362 if (iterator == callback_list.end()) {
363 LOG_ERROR(Input, "Tried to delete non-existent callback {}", key); 363 LOG_ERROR(Input, "Tried to delete non-existent callback {}", key);
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 0c1fbc7b1..282668b36 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
@@ -35,6 +35,15 @@ std::string_view OutputVertexIndex(EmitContext& ctx) {
35 return ctx.stage == Stage::TessellationControl ? "[gl_InvocationID]" : ""; 35 return ctx.stage == Stage::TessellationControl ? "[gl_InvocationID]" : "";
36} 36}
37 37
38std::string ChooseCbuf(EmitContext& ctx, const IR::Value& binding, std::string_view index) {
39 if (binding.IsImmediate()) {
40 return fmt::format("{}_cbuf{}[{}]", ctx.stage_name, binding.U32(), index);
41 } else {
42 const auto binding_var{ctx.var_alloc.Consume(binding)};
43 return fmt::format("GetCbufIndirect({},{})", binding_var, index);
44 }
45}
46
38void GetCbuf(EmitContext& ctx, std::string_view ret, const IR::Value& binding, 47void GetCbuf(EmitContext& ctx, std::string_view ret, const IR::Value& binding,
39 const IR::Value& offset, u32 num_bits, std::string_view cast = {}, 48 const IR::Value& offset, u32 num_bits, std::string_view cast = {},
40 std::string_view bit_offset = {}) { 49 std::string_view bit_offset = {}) {
@@ -55,8 +64,8 @@ void GetCbuf(EmitContext& ctx, std::string_view ret, const IR::Value& binding,
55 const auto swizzle{is_immediate ? fmt::format(".{}", OffsetSwizzle(offset.U32())) 64 const auto swizzle{is_immediate ? fmt::format(".{}", OffsetSwizzle(offset.U32()))
56 : fmt::format("[({}>>2)%4]", offset_var)}; 65 : fmt::format("[({}>>2)%4]", offset_var)};
57 66
58 const auto cbuf{fmt::format("{}_cbuf{}", ctx.stage_name, binding.U32())}; 67 const auto cbuf{ChooseCbuf(ctx, binding, index)};
59 const auto cbuf_cast{fmt::format("{}({}[{}]{{}})", cast, cbuf, index)}; 68 const auto cbuf_cast{fmt::format("{}({}{{}})", cast, cbuf)};
60 const auto extraction{num_bits == 32 ? cbuf_cast 69 const auto extraction{num_bits == 32 ? cbuf_cast
61 : fmt::format("bitfieldExtract({},int({}),{})", cbuf_cast, 70 : fmt::format("bitfieldExtract({},int({}),{})", cbuf_cast,
62 bit_offset, num_bits)}; 71 bit_offset, num_bits)};
@@ -140,9 +149,9 @@ void EmitGetCbufF32(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
140 149
141void EmitGetCbufU32x2(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding, 150void EmitGetCbufU32x2(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
142 const IR::Value& offset) { 151 const IR::Value& offset) {
143 const auto cbuf{fmt::format("{}_cbuf{}", ctx.stage_name, binding.U32())};
144 const auto cast{ctx.profile.has_gl_cbuf_ftou_bug ? "" : "ftou"}; 152 const auto cast{ctx.profile.has_gl_cbuf_ftou_bug ? "" : "ftou"};
145 if (offset.IsImmediate()) { 153 if (offset.IsImmediate()) {
154 const auto cbuf{fmt::format("{}_cbuf{}", ctx.stage_name, binding.U32())};
146 static constexpr u32 cbuf_size{0x10000}; 155 static constexpr u32 cbuf_size{0x10000};
147 const u32 u32_offset{offset.U32()}; 156 const u32 u32_offset{offset.U32()};
148 const s32 signed_offset{static_cast<s32>(offset.U32())}; 157 const s32 signed_offset{static_cast<s32>(offset.U32())};
@@ -162,17 +171,17 @@ void EmitGetCbufU32x2(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding
162 return; 171 return;
163 } 172 }
164 const auto offset_var{ctx.var_alloc.Consume(offset)}; 173 const auto offset_var{ctx.var_alloc.Consume(offset)};
174 const auto cbuf{ChooseCbuf(ctx, binding, fmt::format("{}>>4", offset_var))};
165 if (!ctx.profile.has_gl_component_indexing_bug) { 175 if (!ctx.profile.has_gl_component_indexing_bug) {
166 ctx.AddU32x2("{}=uvec2({}({}[{}>>4][({}>>2)%4]),{}({}[({}+4)>>4][(({}+4)>>2)%4]));", inst, 176 ctx.AddU32x2("{}=uvec2({}({}[({}>>2)%4]),{}({}[(({}+4)>>2)%4]));", inst, cast, cbuf,
167 cast, cbuf, offset_var, offset_var, cast, cbuf, offset_var, offset_var); 177 offset_var, cast, cbuf, offset_var);
168 return; 178 return;
169 } 179 }
170 const auto ret{ctx.var_alloc.Define(inst, GlslVarType::U32x2)}; 180 const auto ret{ctx.var_alloc.Define(inst, GlslVarType::U32x2)};
171 const auto cbuf_offset{fmt::format("{}>>2", offset_var)}; 181 const auto cbuf_offset{fmt::format("{}>>2", offset_var)};
172 for (u32 swizzle = 0; swizzle < 4; ++swizzle) { 182 for (u32 swizzle = 0; swizzle < 4; ++swizzle) {
173 ctx.Add("if(({}&3)=={}){}=uvec2({}({}[{}>>4].{}),{}({}[({}+4)>>4].{}));", cbuf_offset, 183 ctx.Add("if(({}&3)=={}){}=uvec2({}({}.{}),{}({}.{}));", cbuf_offset, swizzle, ret, cast,
174 swizzle, ret, cast, cbuf, offset_var, "xyzw"[swizzle], cast, cbuf, offset_var, 184 cbuf, "xyzw"[swizzle], cast, cbuf, "xyzw"[(swizzle + 1) % 4]);
175 "xyzw"[(swizzle + 1) % 4]);
176 } 185 }
177} 186}
178 187
diff --git a/src/shader_recompiler/backend/glsl/glsl_emit_context.cpp b/src/shader_recompiler/backend/glsl/glsl_emit_context.cpp
index e816a93ec..17266f40d 100644
--- a/src/shader_recompiler/backend/glsl/glsl_emit_context.cpp
+++ b/src/shader_recompiler/backend/glsl/glsl_emit_context.cpp
@@ -359,6 +359,7 @@ EmitContext::EmitContext(IR::Program& program, Bindings& bindings, const Profile
359 header += "layout(location=0) uniform vec4 scaling;"; 359 header += "layout(location=0) uniform vec4 scaling;";
360 } 360 }
361 DefineConstantBuffers(bindings); 361 DefineConstantBuffers(bindings);
362 DefineConstantBufferIndirect();
362 DefineStorageBuffers(bindings); 363 DefineStorageBuffers(bindings);
363 SetupImages(bindings); 364 SetupImages(bindings);
364 SetupTextures(bindings); 365 SetupTextures(bindings);
@@ -436,6 +437,24 @@ void EmitContext::DefineConstantBuffers(Bindings& bindings) {
436 } 437 }
437} 438}
438 439
440void EmitContext::DefineConstantBufferIndirect() {
441 if (!info.uses_cbuf_indirect) {
442 return;
443 }
444
445 header += profile.has_gl_cbuf_ftou_bug ? "uvec4 " : "vec4 ";
446 header += "GetCbufIndirect(uint binding, uint offset){"
447 "switch(binding){"
448 "default:";
449
450 for (const auto& desc : info.constant_buffer_descriptors) {
451 header +=
452 fmt::format("case {}:return {}_cbuf{}[offset];", desc.index, stage_name, desc.index);
453 }
454
455 header += "}}";
456}
457
439void EmitContext::DefineStorageBuffers(Bindings& bindings) { 458void EmitContext::DefineStorageBuffers(Bindings& bindings) {
440 if (info.storage_buffers_descriptors.empty()) { 459 if (info.storage_buffers_descriptors.empty()) {
441 return; 460 return;
diff --git a/src/shader_recompiler/backend/glsl/glsl_emit_context.h b/src/shader_recompiler/backend/glsl/glsl_emit_context.h
index d9b639d29..2b13db6e6 100644
--- a/src/shader_recompiler/backend/glsl/glsl_emit_context.h
+++ b/src/shader_recompiler/backend/glsl/glsl_emit_context.h
@@ -162,6 +162,7 @@ public:
162private: 162private:
163 void SetupExtensions(); 163 void SetupExtensions();
164 void DefineConstantBuffers(Bindings& bindings); 164 void DefineConstantBuffers(Bindings& bindings);
165 void DefineConstantBufferIndirect();
165 void DefineStorageBuffers(Bindings& bindings); 166 void DefineStorageBuffers(Bindings& bindings);
166 void DefineGenericOutput(size_t index, u32 invocations); 167 void DefineGenericOutput(size_t index, u32 invocations);
167 void DefineHelperFunctions(); 168 void DefineHelperFunctions();
diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_atomic.cpp b/src/shader_recompiler/backend/spirv/emit_spirv_atomic.cpp
index d3cbb14a9..cb47d253c 100644
--- a/src/shader_recompiler/backend/spirv/emit_spirv_atomic.cpp
+++ b/src/shader_recompiler/backend/spirv/emit_spirv_atomic.cpp
@@ -2,6 +2,8 @@
2// Licensed under GPLv2 or any later version 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <bit>
6
5#include "shader_recompiler/backend/spirv/emit_spirv.h" 7#include "shader_recompiler/backend/spirv/emit_spirv.h"
6#include "shader_recompiler/backend/spirv/emit_spirv_instructions.h" 8#include "shader_recompiler/backend/spirv/emit_spirv_instructions.h"
7#include "shader_recompiler/backend/spirv/spirv_emit_context.h" 9#include "shader_recompiler/backend/spirv/spirv_emit_context.h"
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 3c2a5e16f..aa7082978 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
@@ -2,6 +2,7 @@
2// Licensed under GPLv2 or any later version 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <bit>
5#include <tuple> 6#include <tuple>
6#include <utility> 7#include <utility>
7 8
diff --git a/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp b/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp
index 53be98ced..9c83cd2e4 100644
--- a/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp
+++ b/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp
@@ -4,6 +4,7 @@
4 4
5#include <algorithm> 5#include <algorithm>
6#include <array> 6#include <array>
7#include <bit>
7#include <climits> 8#include <climits>
8 9
9#include <boost/container/static_vector.hpp> 10#include <boost/container/static_vector.hpp>
@@ -1042,15 +1043,15 @@ void EmitContext::DefineConstantBufferIndirectFunctions(const Info& info) {
1042 const Id merge_label{OpLabel()}; 1043 const Id merge_label{OpLabel()};
1043 const Id uniform_type{uniform_types.*member_ptr}; 1044 const Id uniform_type{uniform_types.*member_ptr};
1044 1045
1045 std::array<Id, Info::MAX_CBUFS> buf_labels; 1046 std::array<Id, Info::MAX_INDIRECT_CBUFS> buf_labels;
1046 std::array<Sirit::Literal, Info::MAX_CBUFS> buf_literals; 1047 std::array<Sirit::Literal, Info::MAX_INDIRECT_CBUFS> buf_literals;
1047 for (u32 i = 0; i < Info::MAX_CBUFS; i++) { 1048 for (u32 i = 0; i < Info::MAX_INDIRECT_CBUFS; i++) {
1048 buf_labels[i] = OpLabel(); 1049 buf_labels[i] = OpLabel();
1049 buf_literals[i] = Sirit::Literal{i}; 1050 buf_literals[i] = Sirit::Literal{i};
1050 } 1051 }
1051 OpSelectionMerge(merge_label, spv::SelectionControlMask::MaskNone); 1052 OpSelectionMerge(merge_label, spv::SelectionControlMask::MaskNone);
1052 OpSwitch(binding, buf_labels[0], buf_literals, buf_labels); 1053 OpSwitch(binding, buf_labels[0], buf_literals, buf_labels);
1053 for (u32 i = 0; i < Info::MAX_CBUFS; i++) { 1054 for (u32 i = 0; i < Info::MAX_INDIRECT_CBUFS; i++) {
1054 AddLabel(buf_labels[i]); 1055 AddLabel(buf_labels[i]);
1055 const Id cbuf{cbufs[i].*member_ptr}; 1056 const Id cbuf{cbufs[i].*member_ptr};
1056 const Id access_chain{OpAccessChain(uniform_type, cbuf, u32_zero_value, offset)}; 1057 const Id access_chain{OpAccessChain(uniform_type, cbuf, u32_zero_value, offset)};
@@ -1063,22 +1064,23 @@ void EmitContext::DefineConstantBufferIndirectFunctions(const Info& info) {
1063 return func; 1064 return func;
1064 }}; 1065 }};
1065 IR::Type types{info.used_indirect_cbuf_types}; 1066 IR::Type types{info.used_indirect_cbuf_types};
1066 if (True(types & IR::Type::U8)) { 1067 bool supports_aliasing = profile.support_descriptor_aliasing;
1068 if (supports_aliasing && True(types & IR::Type::U8)) {
1067 load_const_func_u8 = make_accessor(U8, &UniformDefinitions::U8); 1069 load_const_func_u8 = make_accessor(U8, &UniformDefinitions::U8);
1068 } 1070 }
1069 if (True(types & IR::Type::U16)) { 1071 if (supports_aliasing && True(types & IR::Type::U16)) {
1070 load_const_func_u16 = make_accessor(U16, &UniformDefinitions::U16); 1072 load_const_func_u16 = make_accessor(U16, &UniformDefinitions::U16);
1071 } 1073 }
1072 if (True(types & IR::Type::F32)) { 1074 if (supports_aliasing && True(types & IR::Type::F32)) {
1073 load_const_func_f32 = make_accessor(F32[1], &UniformDefinitions::F32); 1075 load_const_func_f32 = make_accessor(F32[1], &UniformDefinitions::F32);
1074 } 1076 }
1075 if (True(types & IR::Type::U32)) { 1077 if (supports_aliasing && True(types & IR::Type::U32)) {
1076 load_const_func_u32 = make_accessor(U32[1], &UniformDefinitions::U32); 1078 load_const_func_u32 = make_accessor(U32[1], &UniformDefinitions::U32);
1077 } 1079 }
1078 if (True(types & IR::Type::U32x2)) { 1080 if (supports_aliasing && True(types & IR::Type::U32x2)) {
1079 load_const_func_u32x2 = make_accessor(U32[2], &UniformDefinitions::U32x2); 1081 load_const_func_u32x2 = make_accessor(U32[2], &UniformDefinitions::U32x2);
1080 } 1082 }
1081 if (True(types & IR::Type::U32x4)) { 1083 if (!supports_aliasing || True(types & IR::Type::U32x4)) {
1082 load_const_func_u32x4 = make_accessor(U32[4], &UniformDefinitions::U32x4); 1084 load_const_func_u32x4 = make_accessor(U32[4], &UniformDefinitions::U32x4);
1083 } 1085 }
1084} 1086}
diff --git a/src/shader_recompiler/frontend/ir/microinstruction.cpp b/src/shader_recompiler/frontend/ir/microinstruction.cpp
index 631446cf7..4a2564f47 100644
--- a/src/shader_recompiler/frontend/ir/microinstruction.cpp
+++ b/src/shader_recompiler/frontend/ir/microinstruction.cpp
@@ -326,6 +326,11 @@ void Inst::AddPhiOperand(Block* predecessor, const Value& value) {
326 phi_args.emplace_back(predecessor, value); 326 phi_args.emplace_back(predecessor, value);
327} 327}
328 328
329void Inst::ErasePhiOperand(size_t index) {
330 const auto operand_it{phi_args.begin() + static_cast<ptrdiff_t>(index)};
331 phi_args.erase(operand_it);
332}
333
329void Inst::OrderPhiArgs() { 334void Inst::OrderPhiArgs() {
330 if (op != Opcode::Phi) { 335 if (op != Opcode::Phi) {
331 throw LogicError("{} is not a Phi instruction", op); 336 throw LogicError("{} is not a Phi instruction", op);
diff --git a/src/shader_recompiler/frontend/ir/value.h b/src/shader_recompiler/frontend/ir/value.h
index 947579852..14f6e55bc 100644
--- a/src/shader_recompiler/frontend/ir/value.h
+++ b/src/shader_recompiler/frontend/ir/value.h
@@ -179,9 +179,13 @@ public:
179 179
180 /// Get a pointer to the block of a phi argument. 180 /// Get a pointer to the block of a phi argument.
181 [[nodiscard]] Block* PhiBlock(size_t index) const; 181 [[nodiscard]] Block* PhiBlock(size_t index) const;
182
182 /// Add phi operand to a phi instruction. 183 /// Add phi operand to a phi instruction.
183 void AddPhiOperand(Block* predecessor, const Value& value); 184 void AddPhiOperand(Block* predecessor, const Value& value);
184 185
186 // Erase the phi operand at the given index.
187 void ErasePhiOperand(size_t index);
188
185 /// Orders the Phi arguments from farthest away to nearest. 189 /// Orders the Phi arguments from farthest away to nearest.
186 void OrderPhiArgs(); 190 void OrderPhiArgs();
187 191
diff --git a/src/shader_recompiler/frontend/maxwell/translate/impl/exit_program.cpp b/src/shader_recompiler/frontend/maxwell/translate/impl/exit_program.cpp
index 6cd13ec48..62d20ebe4 100644
--- a/src/shader_recompiler/frontend/maxwell/translate/impl/exit_program.cpp
+++ b/src/shader_recompiler/frontend/maxwell/translate/impl/exit_program.cpp
@@ -11,9 +11,13 @@ void ExitFragment(TranslatorVisitor& v) {
11 const ProgramHeader sph{v.env.SPH()}; 11 const ProgramHeader sph{v.env.SPH()};
12 IR::Reg src_reg{IR::Reg::R0}; 12 IR::Reg src_reg{IR::Reg::R0};
13 for (u32 render_target = 0; render_target < 8; ++render_target) { 13 for (u32 render_target = 0; render_target < 8; ++render_target) {
14 if (!sph.ps.HasOutputComponents(render_target)) {
15 continue;
16 }
14 const std::array<bool, 4> mask{sph.ps.EnabledOutputComponents(render_target)}; 17 const std::array<bool, 4> mask{sph.ps.EnabledOutputComponents(render_target)};
15 for (u32 component = 0; component < 4; ++component) { 18 for (u32 component = 0; component < 4; ++component) {
16 if (!mask[component]) { 19 if (!mask[component]) {
20 ++src_reg;
17 continue; 21 continue;
18 } 22 }
19 v.ir.SetFragColor(render_target, component, v.F(src_reg)); 23 v.ir.SetFragColor(render_target, component, v.F(src_reg));
diff --git a/src/shader_recompiler/frontend/maxwell/translate/impl/texture_load.cpp b/src/shader_recompiler/frontend/maxwell/translate/impl/texture_load.cpp
index 57b4f0eee..60732215b 100644
--- a/src/shader_recompiler/frontend/maxwell/translate/impl/texture_load.cpp
+++ b/src/shader_recompiler/frontend/maxwell/translate/impl/texture_load.cpp
@@ -132,7 +132,7 @@ void Impl(TranslatorVisitor& v, u64 insn, bool is_bindless) {
132 multisample = v.X(meta_reg++); 132 multisample = v.X(meta_reg++);
133 } 133 }
134 if (tld.clamp != 0) { 134 if (tld.clamp != 0) {
135 throw NotImplementedException("TLD.CL - CLAMP is not implmented"); 135 throw NotImplementedException("TLD.CL - CLAMP is not implemented");
136 } 136 }
137 IR::TextureInstInfo info{}; 137 IR::TextureInstInfo info{};
138 info.type.Assign(GetType(tld.type)); 138 info.type.Assign(GetType(tld.type));
diff --git a/src/shader_recompiler/frontend/maxwell/translate/impl/texture_mipmap_level.cpp b/src/shader_recompiler/frontend/maxwell/translate/impl/texture_mipmap_level.cpp
index 311a9e763..f89ce1b68 100644
--- a/src/shader_recompiler/frontend/maxwell/translate/impl/texture_mipmap_level.cpp
+++ b/src/shader_recompiler/frontend/maxwell/translate/impl/texture_mipmap_level.cpp
@@ -81,7 +81,7 @@ void Impl(TranslatorVisitor& v, u64 insn, bool is_bindless) {
81 } const tmml{insn}; 81 } const tmml{insn};
82 82
83 if ((tmml.mask & 0b1100) != 0) { 83 if ((tmml.mask & 0b1100) != 0) {
84 throw NotImplementedException("TMML BA results are not implmented"); 84 throw NotImplementedException("TMML BA results are not implemented");
85 } 85 }
86 const IR::Value coords{MakeCoords(v, tmml.coord_reg, tmml.type)}; 86 const IR::Value coords{MakeCoords(v, tmml.coord_reg, tmml.type)};
87 87
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 0b2c60842..16278faab 100644
--- a/src/shader_recompiler/ir_opt/collect_shader_info_pass.cpp
+++ b/src/shader_recompiler/ir_opt/collect_shader_info_pass.cpp
@@ -32,13 +32,8 @@ void AddConstantBufferDescriptor(Info& info, u32 index, u32 count) {
32void AddRegisterIndexedLdc(Info& info) { 32void AddRegisterIndexedLdc(Info& info) {
33 info.uses_cbuf_indirect = true; 33 info.uses_cbuf_indirect = true;
34 34
35 // The shader can use any possible constant buffer 35 for (u32 i = 0; i < Info::MAX_INDIRECT_CBUFS; i++) {
36 info.constant_buffer_mask = (1 << Info::MAX_CBUFS) - 1; 36 AddConstantBufferDescriptor(info, i, 1);
37
38 auto& cbufs{info.constant_buffer_descriptors};
39 cbufs.clear();
40 for (u32 i = 0; i < Info::MAX_CBUFS; i++) {
41 cbufs.push_back(ConstantBufferDescriptor{.index = i, .count = 1});
42 37
43 // The shader can use any possible access size 38 // The shader can use any possible access size
44 info.constant_buffer_used_sizes[i] = 0x10'000; 39 info.constant_buffer_used_sizes[i] = 0x10'000;
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 400836301..6697fde85 100644
--- a/src/shader_recompiler/ir_opt/dead_code_elimination_pass.cpp
+++ b/src/shader_recompiler/ir_opt/dead_code_elimination_pass.cpp
@@ -2,24 +2,104 @@
2// Licensed under GPLv2 or any later version 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <algorithm>
6
7#include <boost/container/small_vector.hpp>
8
5#include "shader_recompiler/frontend/ir/basic_block.h" 9#include "shader_recompiler/frontend/ir/basic_block.h"
6#include "shader_recompiler/frontend/ir/value.h" 10#include "shader_recompiler/frontend/ir/value.h"
7#include "shader_recompiler/ir_opt/passes.h" 11#include "shader_recompiler/ir_opt/passes.h"
8 12
9namespace Shader::Optimization { 13namespace Shader::Optimization {
10 14namespace {
11void DeadCodeEliminationPass(IR::Program& program) { 15template <bool TEST_USES>
16void DeadInstElimination(IR::Block* const block) {
12 // We iterate over the instructions in reverse order. 17 // We iterate over the instructions in reverse order.
13 // This is because removing an instruction reduces the number of uses for earlier instructions. 18 // This is because removing an instruction reduces the number of uses for earlier instructions.
14 for (IR::Block* const block : program.post_order_blocks) { 19 auto it{block->end()};
15 auto it{block->end()}; 20 while (it != block->begin()) {
16 while (it != block->begin()) { 21 --it;
17 --it; 22 if constexpr (TEST_USES) {
18 if (!it->HasUses() && !it->MayHaveSideEffects()) { 23 if (it->HasUses() || it->MayHaveSideEffects()) {
19 it->Invalidate(); 24 continue;
20 it = block->Instructions().erase(it); 25 }
26 }
27 it->Invalidate();
28 it = block->Instructions().erase(it);
29 }
30}
31
32void DeletedPhiArgElimination(IR::Program& program, std::span<const IR::Block*> dead_blocks) {
33 for (IR::Block* const block : program.blocks) {
34 for (IR::Inst& phi : *block) {
35 if (!IR::IsPhi(phi)) {
36 continue;
37 }
38 for (size_t i = 0; i < phi.NumArgs(); ++i) {
39 if (std::ranges::find(dead_blocks, phi.PhiBlock(i)) == dead_blocks.end()) {
40 continue;
41 }
42 // Phi operand at this index is an unreachable block
43 phi.ErasePhiOperand(i);
44 --i;
45 }
46 }
47 }
48}
49
50void DeadBranchElimination(IR::Program& program) {
51 boost::container::small_vector<const IR::Block*, 3> dead_blocks;
52 const auto begin_it{program.syntax_list.begin()};
53 for (auto node_it = begin_it; node_it != program.syntax_list.end(); ++node_it) {
54 if (node_it->type != IR::AbstractSyntaxNode::Type::If) {
55 continue;
56 }
57 IR::Inst* const cond_ref{node_it->data.if_node.cond.Inst()};
58 const IR::U1 cond{cond_ref->Arg(0)};
59 if (!cond.IsImmediate()) {
60 continue;
61 }
62 if (cond.U1()) {
63 continue;
64 }
65 // False immediate condition. Remove condition ref, erase the entire branch.
66 cond_ref->Invalidate();
67 // Account for nested if-statements within the if(false) branch
68 u32 nested_ifs{1u};
69 while (node_it->type != IR::AbstractSyntaxNode::Type::EndIf || nested_ifs > 0) {
70 node_it = program.syntax_list.erase(node_it);
71 switch (node_it->type) {
72 case IR::AbstractSyntaxNode::Type::If:
73 ++nested_ifs;
74 break;
75 case IR::AbstractSyntaxNode::Type::EndIf:
76 --nested_ifs;
77 break;
78 case IR::AbstractSyntaxNode::Type::Block: {
79 IR::Block* const block{node_it->data.block};
80 DeadInstElimination<false>(block);
81 dead_blocks.push_back(block);
82 break;
83 }
84 default:
85 break;
21 } 86 }
22 } 87 }
88 // Erase EndIf node of the if(false) branch
89 node_it = program.syntax_list.erase(node_it);
90 // Account for loop increment
91 --node_it;
92 }
93 if (!dead_blocks.empty()) {
94 DeletedPhiArgElimination(program, std::span(dead_blocks.data(), dead_blocks.size()));
95 }
96}
97} // namespace
98
99void DeadCodeEliminationPass(IR::Program& program) {
100 DeadBranchElimination(program);
101 for (IR::Block* const block : program.post_order_blocks) {
102 DeadInstElimination<true>(block);
23 } 103 }
24} 104}
25 105
diff --git a/src/shader_recompiler/program_header.h b/src/shader_recompiler/program_header.h
index bd6c2bfb5..0cd6597ef 100644
--- a/src/shader_recompiler/program_header.h
+++ b/src/shader_recompiler/program_header.h
@@ -196,6 +196,11 @@ struct ProgramHeader {
196 return {(bits & 1) != 0, (bits & 2) != 0, (bits & 4) != 0, (bits & 8) != 0}; 196 return {(bits & 1) != 0, (bits & 2) != 0, (bits & 4) != 0, (bits & 8) != 0};
197 } 197 }
198 198
199 [[nodiscard]] bool HasOutputComponents(u32 rt) const noexcept {
200 const u32 bits{omap.target >> (rt * 4)};
201 return (bits & 0xf) != 0;
202 }
203
199 [[nodiscard]] std::array<PixelImap, 4> GenericInputMap(u32 attribute) const { 204 [[nodiscard]] std::array<PixelImap, 4> GenericInputMap(u32 attribute) const {
200 const auto& vector{imap_generic_vector[attribute]}; 205 const auto& vector{imap_generic_vector[attribute]};
201 return {vector.x, vector.y, vector.z, vector.w}; 206 return {vector.x, vector.y, vector.z, vector.w};
diff --git a/src/shader_recompiler/shader_info.h b/src/shader_recompiler/shader_info.h
index 9d36bd9eb..a3a09c71c 100644
--- a/src/shader_recompiler/shader_info.h
+++ b/src/shader_recompiler/shader_info.h
@@ -105,6 +105,7 @@ struct ImageDescriptor {
105using ImageDescriptors = boost::container::small_vector<ImageDescriptor, 4>; 105using ImageDescriptors = boost::container::small_vector<ImageDescriptor, 4>;
106 106
107struct Info { 107struct Info {
108 static constexpr size_t MAX_INDIRECT_CBUFS{14};
108 static constexpr size_t MAX_CBUFS{18}; 109 static constexpr size_t MAX_CBUFS{18};
109 static constexpr size_t MAX_SSBOS{32}; 110 static constexpr size_t MAX_SSBOS{32};
110 111
diff --git a/src/video_core/buffer_cache/buffer_base.h b/src/video_core/buffer_cache/buffer_base.h
index be2113f5a..10975884b 100644
--- a/src/video_core/buffer_cache/buffer_base.h
+++ b/src/video_core/buffer_cache/buffer_base.h
@@ -212,7 +212,7 @@ public:
212 void FlushCachedWrites() noexcept { 212 void FlushCachedWrites() noexcept {
213 flags &= ~BufferFlagBits::CachedWrites; 213 flags &= ~BufferFlagBits::CachedWrites;
214 const u64 num_words = NumWords(); 214 const u64 num_words = NumWords();
215 const u64* const cached_words = Array<Type::CachedCPU>(); 215 u64* const cached_words = Array<Type::CachedCPU>();
216 u64* const untracked_words = Array<Type::Untracked>(); 216 u64* const untracked_words = Array<Type::Untracked>();
217 u64* const cpu_words = Array<Type::CPU>(); 217 u64* const cpu_words = Array<Type::CPU>();
218 for (u64 word_index = 0; word_index < num_words; ++word_index) { 218 for (u64 word_index = 0; word_index < num_words; ++word_index) {
@@ -220,6 +220,7 @@ public:
220 NotifyRasterizer<false>(word_index, untracked_words[word_index], cached_bits); 220 NotifyRasterizer<false>(word_index, untracked_words[word_index], cached_bits);
221 untracked_words[word_index] |= cached_bits; 221 untracked_words[word_index] |= cached_bits;
222 cpu_words[word_index] |= cached_bits; 222 cpu_words[word_index] |= cached_bits;
223 cached_words[word_index] = 0;
223 } 224 }
224 } 225 }
225 226
diff --git a/src/video_core/buffer_cache/buffer_cache.h b/src/video_core/buffer_cache/buffer_cache.h
index 200d792dd..3f2bf6294 100644
--- a/src/video_core/buffer_cache/buffer_cache.h
+++ b/src/video_core/buffer_cache/buffer_cache.h
@@ -76,8 +76,9 @@ class BufferCache {
76 76
77 static constexpr BufferId NULL_BUFFER_ID{0}; 77 static constexpr BufferId NULL_BUFFER_ID{0};
78 78
79 static constexpr u64 EXPECTED_MEMORY = 512_MiB; 79 static constexpr s64 DEFAULT_EXPECTED_MEMORY = 512_MiB;
80 static constexpr u64 CRITICAL_MEMORY = 1_GiB; 80 static constexpr s64 DEFAULT_CRITICAL_MEMORY = 1_GiB;
81 static constexpr s64 TARGET_THRESHOLD = 4_GiB;
81 82
82 using Maxwell = Tegra::Engines::Maxwell3D::Regs; 83 using Maxwell = Tegra::Engines::Maxwell3D::Regs;
83 84
@@ -436,6 +437,8 @@ private:
436 Common::LeastRecentlyUsedCache<LRUItemParams> lru_cache; 437 Common::LeastRecentlyUsedCache<LRUItemParams> lru_cache;
437 u64 frame_tick = 0; 438 u64 frame_tick = 0;
438 u64 total_used_memory = 0; 439 u64 total_used_memory = 0;
440 u64 minimum_memory = 0;
441 u64 critical_memory = 0;
439 442
440 std::array<BufferId, ((1ULL << 39) >> PAGE_BITS)> page_table; 443 std::array<BufferId, ((1ULL << 39) >> PAGE_BITS)> page_table;
441}; 444};
@@ -451,11 +454,30 @@ BufferCache<P>::BufferCache(VideoCore::RasterizerInterface& rasterizer_,
451 // Ensure the first slot is used for the null buffer 454 // Ensure the first slot is used for the null buffer
452 void(slot_buffers.insert(runtime, NullBufferParams{})); 455 void(slot_buffers.insert(runtime, NullBufferParams{}));
453 common_ranges.clear(); 456 common_ranges.clear();
457
458 if (!runtime.CanReportMemoryUsage()) {
459 minimum_memory = DEFAULT_EXPECTED_MEMORY;
460 critical_memory = DEFAULT_CRITICAL_MEMORY;
461 return;
462 }
463
464 const s64 device_memory = static_cast<s64>(runtime.GetDeviceLocalMemory());
465 const s64 min_spacing_expected = device_memory - 1_GiB - 512_MiB;
466 const s64 min_spacing_critical = device_memory - 1_GiB;
467 const s64 mem_threshold = std::min(device_memory, TARGET_THRESHOLD);
468 const s64 min_vacancy_expected = (6 * mem_threshold) / 10;
469 const s64 min_vacancy_critical = (3 * mem_threshold) / 10;
470 minimum_memory = static_cast<u64>(
471 std::max(std::min(device_memory - min_vacancy_expected, min_spacing_expected),
472 DEFAULT_EXPECTED_MEMORY));
473 critical_memory = static_cast<u64>(
474 std::max(std::min(device_memory - min_vacancy_critical, min_spacing_critical),
475 DEFAULT_CRITICAL_MEMORY));
454} 476}
455 477
456template <class P> 478template <class P>
457void BufferCache<P>::RunGarbageCollector() { 479void BufferCache<P>::RunGarbageCollector() {
458 const bool aggressive_gc = total_used_memory >= CRITICAL_MEMORY; 480 const bool aggressive_gc = total_used_memory >= critical_memory;
459 const u64 ticks_to_destroy = aggressive_gc ? 60 : 120; 481 const u64 ticks_to_destroy = aggressive_gc ? 60 : 120;
460 int num_iterations = aggressive_gc ? 64 : 32; 482 int num_iterations = aggressive_gc ? 64 : 32;
461 const auto clean_up = [this, &num_iterations](BufferId buffer_id) { 483 const auto clean_up = [this, &num_iterations](BufferId buffer_id) {
@@ -486,7 +508,11 @@ void BufferCache<P>::TickFrame() {
486 const bool skip_preferred = hits * 256 < shots * 251; 508 const bool skip_preferred = hits * 256 < shots * 251;
487 uniform_buffer_skip_cache_size = skip_preferred ? DEFAULT_SKIP_CACHE_SIZE : 0; 509 uniform_buffer_skip_cache_size = skip_preferred ? DEFAULT_SKIP_CACHE_SIZE : 0;
488 510
489 if (total_used_memory >= EXPECTED_MEMORY) { 511 // If we can obtain the memory info, use it instead of the estimate.
512 if (runtime.CanReportMemoryUsage()) {
513 total_used_memory = runtime.GetDeviceMemoryUsage();
514 }
515 if (total_used_memory >= minimum_memory) {
490 RunGarbageCollector(); 516 RunGarbageCollector();
491 } 517 }
492 ++frame_tick; 518 ++frame_tick;
@@ -1285,7 +1311,20 @@ void BufferCache<P>::UpdateVertexBuffer(u32 index) {
1285 const GPUVAddr gpu_addr_begin = array.StartAddress(); 1311 const GPUVAddr gpu_addr_begin = array.StartAddress();
1286 const GPUVAddr gpu_addr_end = limit.LimitAddress() + 1; 1312 const GPUVAddr gpu_addr_end = limit.LimitAddress() + 1;
1287 const std::optional<VAddr> cpu_addr = gpu_memory.GpuToCpuAddress(gpu_addr_begin); 1313 const std::optional<VAddr> cpu_addr = gpu_memory.GpuToCpuAddress(gpu_addr_begin);
1288 const u32 address_size = static_cast<u32>(gpu_addr_end - gpu_addr_begin); 1314 u32 address_size = static_cast<u32>(gpu_addr_end - gpu_addr_begin);
1315 if (address_size >= 64_MiB) {
1316 // Reported vertex buffer size is very large, cap to mapped buffer size
1317 GPUVAddr submapped_addr_end = gpu_addr_begin;
1318
1319 const auto ranges{gpu_memory.GetSubmappedRange(gpu_addr_begin, address_size)};
1320 if (ranges.size() > 0) {
1321 const auto& [addr, size] = *ranges.begin();
1322 submapped_addr_end = addr + size;
1323 }
1324
1325 address_size =
1326 std::min(address_size, static_cast<u32>(submapped_addr_end - gpu_addr_begin));
1327 }
1289 const u32 size = address_size; // TODO: Analyze stride and number of vertices 1328 const u32 size = address_size; // TODO: Analyze stride and number of vertices
1290 if (array.enable == 0 || size == 0 || !cpu_addr) { 1329 if (array.enable == 0 || size == 0 || !cpu_addr) {
1291 vertex_buffers[index] = NULL_BINDING; 1330 vertex_buffers[index] = NULL_BINDING;
diff --git a/src/video_core/command_classes/codecs/codec.cpp b/src/video_core/command_classes/codecs/codec.cpp
index 81fac94bf..40f7755e8 100644
--- a/src/video_core/command_classes/codecs/codec.cpp
+++ b/src/video_core/command_classes/codecs/codec.cpp
@@ -56,6 +56,18 @@ AVPixelFormat GetGpuFormat(AVCodecContext* av_codec_ctx, const AVPixelFormat* pi
56 av_codec_ctx->pix_fmt = PREFERRED_CPU_FMT; 56 av_codec_ctx->pix_fmt = PREFERRED_CPU_FMT;
57 return PREFERRED_CPU_FMT; 57 return PREFERRED_CPU_FMT;
58} 58}
59
60// List all the currently available hwcontext in ffmpeg
61std::vector<AVHWDeviceType> ListSupportedContexts() {
62 std::vector<AVHWDeviceType> contexts{};
63 AVHWDeviceType current_device_type = AV_HWDEVICE_TYPE_NONE;
64 do {
65 current_device_type = av_hwdevice_iterate_types(current_device_type);
66 contexts.push_back(current_device_type);
67 } while (current_device_type != AV_HWDEVICE_TYPE_NONE);
68 return contexts;
69}
70
59} // namespace 71} // namespace
60 72
61void AVFrameDeleter(AVFrame* ptr) { 73void AVFrameDeleter(AVFrame* ptr) {
@@ -76,17 +88,6 @@ Codec::~Codec() {
76 av_buffer_unref(&av_gpu_decoder); 88 av_buffer_unref(&av_gpu_decoder);
77} 89}
78 90
79// List all the currently available hwcontext in ffmpeg
80static std::vector<AVHWDeviceType> ListSupportedContexts() {
81 std::vector<AVHWDeviceType> contexts{};
82 AVHWDeviceType current_device_type = AV_HWDEVICE_TYPE_NONE;
83 do {
84 current_device_type = av_hwdevice_iterate_types(current_device_type);
85 contexts.push_back(current_device_type);
86 } while (current_device_type != AV_HWDEVICE_TYPE_NONE);
87 return contexts;
88}
89
90bool Codec::CreateGpuAvDevice() { 91bool Codec::CreateGpuAvDevice() {
91 static constexpr auto HW_CONFIG_METHOD = AV_CODEC_HW_CONFIG_METHOD_HW_DEVICE_CTX; 92 static constexpr auto HW_CONFIG_METHOD = AV_CODEC_HW_CONFIG_METHOD_HW_DEVICE_CTX;
92 static const auto supported_contexts = ListSupportedContexts(); 93 static const auto supported_contexts = ListSupportedContexts();
@@ -96,6 +97,8 @@ bool Codec::CreateGpuAvDevice() {
96 LOG_DEBUG(Service_NVDRV, "{} explicitly unsupported", av_hwdevice_get_type_name(type)); 97 LOG_DEBUG(Service_NVDRV, "{} explicitly unsupported", av_hwdevice_get_type_name(type));
97 continue; 98 continue;
98 } 99 }
100 // Avoid memory leak from not cleaning up after av_hwdevice_ctx_create
101 av_buffer_unref(&av_gpu_decoder);
99 const int hwdevice_res = av_hwdevice_ctx_create(&av_gpu_decoder, type, nullptr, nullptr, 0); 102 const int hwdevice_res = av_hwdevice_ctx_create(&av_gpu_decoder, type, nullptr, nullptr, 0);
100 if (hwdevice_res < 0) { 103 if (hwdevice_res < 0) {
101 LOG_DEBUG(Service_NVDRV, "{} av_hwdevice_ctx_create failed {}", 104 LOG_DEBUG(Service_NVDRV, "{} av_hwdevice_ctx_create failed {}",
@@ -127,15 +130,19 @@ bool Codec::CreateGpuAvDevice() {
127 av_codec->name, av_hwdevice_get_type_name(type)); 130 av_codec->name, av_hwdevice_get_type_name(type));
128 break; 131 break;
129 } 132 }
130 if (config->methods & HW_CONFIG_METHOD && config->device_type == type) { 133 if ((config->methods & HW_CONFIG_METHOD) != 0 && config->device_type == type) {
131 av_codec_ctx->pix_fmt = config->pix_fmt; 134#if defined(__unix__)
132 if (config->methods & AV_CODEC_HW_CONFIG_METHOD_HW_FRAMES_CTX) { 135 // Some linux decoding backends are reported to crash with this config method
136 // TODO(ameerj): Properly support this method
137 if ((config->methods & AV_CODEC_HW_CONFIG_METHOD_HW_FRAMES_CTX) != 0) {
133 // skip zero-copy decoders, we don't currently support them 138 // skip zero-copy decoders, we don't currently support them
134 LOG_DEBUG(Service_NVDRV, "Skipping decoder {} with unsupported capability {}.", 139 LOG_DEBUG(Service_NVDRV, "Skipping decoder {} with unsupported capability {}.",
135 av_hwdevice_get_type_name(type), config->methods); 140 av_hwdevice_get_type_name(type), config->methods);
136 continue; 141 continue;
137 } 142 }
143#endif
138 LOG_INFO(Service_NVDRV, "Using {} GPU decoder", av_hwdevice_get_type_name(type)); 144 LOG_INFO(Service_NVDRV, "Using {} GPU decoder", av_hwdevice_get_type_name(type));
145 av_codec_ctx->pix_fmt = config->pix_fmt;
139 return true; 146 return true;
140 } 147 }
141 } 148 }
diff --git a/src/video_core/engines/maxwell_3d.cpp b/src/video_core/engines/maxwell_3d.cpp
index 54a902f56..7399e760f 100644
--- a/src/video_core/engines/maxwell_3d.cpp
+++ b/src/video_core/engines/maxwell_3d.cpp
@@ -214,6 +214,11 @@ void Maxwell3D::ProcessMethodCall(u32 method, u32 argument, u32 nonshadow_argume
214 regs.index_array.first = regs.small_index.first; 214 regs.index_array.first = regs.small_index.first;
215 dirty.flags[VideoCommon::Dirty::IndexBuffer] = true; 215 dirty.flags[VideoCommon::Dirty::IndexBuffer] = true;
216 return DrawArrays(); 216 return DrawArrays();
217 case MAXWELL3D_REG_INDEX(small_index_2):
218 regs.index_array.count = regs.small_index_2.count;
219 regs.index_array.first = regs.small_index_2.first;
220 dirty.flags[VideoCommon::Dirty::IndexBuffer] = true;
221 return DrawArrays();
217 case MAXWELL3D_REG_INDEX(topology_override): 222 case MAXWELL3D_REG_INDEX(topology_override):
218 use_topology_override = true; 223 use_topology_override = true;
219 return; 224 return;
diff --git a/src/video_core/engines/maxwell_3d.h b/src/video_core/engines/maxwell_3d.h
index 3f5b38e55..d36dc3daa 100644
--- a/src/video_core/engines/maxwell_3d.h
+++ b/src/video_core/engines/maxwell_3d.h
@@ -1220,7 +1220,12 @@ public:
1220 BitField<16, 16, u32> count; 1220 BitField<16, 16, u32> count;
1221 } small_index; 1221 } small_index;
1222 1222
1223 INSERT_PADDING_WORDS_NOINIT(0x6); 1223 union {
1224 BitField<0, 16, u32> first;
1225 BitField<16, 16, u32> count;
1226 } small_index_2;
1227
1228 INSERT_PADDING_WORDS_NOINIT(0x5);
1224 1229
1225 INSERT_PADDING_WORDS_NOINIT(0x1F); 1230 INSERT_PADDING_WORDS_NOINIT(0x1F);
1226 1231
diff --git a/src/video_core/framebuffer_config.h b/src/video_core/framebuffer_config.h
index b1d455e30..93349bb78 100644
--- a/src/video_core/framebuffer_config.h
+++ b/src/video_core/framebuffer_config.h
@@ -6,41 +6,22 @@
6 6
7#include "common/common_types.h" 7#include "common/common_types.h"
8#include "common/math_util.h" 8#include "common/math_util.h"
9#include "core/hle/service/nvflinger/buffer_transform_flags.h"
10#include "core/hle/service/nvflinger/pixel_format.h"
9 11
10namespace Tegra { 12namespace Tegra {
13
11/** 14/**
12 * Struct describing framebuffer configuration 15 * Struct describing framebuffer configuration
13 */ 16 */
14struct FramebufferConfig { 17struct FramebufferConfig {
15 enum class PixelFormat : u32 {
16 A8B8G8R8_UNORM = 1,
17 RGB565_UNORM = 4,
18 B8G8R8A8_UNORM = 5,
19 };
20
21 enum class TransformFlags : u32 {
22 /// No transform flags are set
23 Unset = 0x00,
24 /// Flip source image horizontally (around the vertical axis)
25 FlipH = 0x01,
26 /// Flip source image vertically (around the horizontal axis)
27 FlipV = 0x02,
28 /// Rotate source image 90 degrees clockwise
29 Rotate90 = 0x04,
30 /// Rotate source image 180 degrees
31 Rotate180 = 0x03,
32 /// Rotate source image 270 degrees clockwise
33 Rotate270 = 0x07,
34 };
35
36 VAddr address{}; 18 VAddr address{};
37 u32 offset{}; 19 u32 offset{};
38 u32 width{}; 20 u32 width{};
39 u32 height{}; 21 u32 height{};
40 u32 stride{}; 22 u32 stride{};
41 PixelFormat pixel_format{}; 23 Service::android::PixelFormat pixel_format{};
42 24 Service::android::BufferTransformFlags transform_flags{};
43 TransformFlags transform_flags{};
44 Common::Rectangle<int> crop_rect; 25 Common::Rectangle<int> crop_rect;
45}; 26};
46 27
diff --git a/src/video_core/gpu.cpp b/src/video_core/gpu.cpp
index ba9ba082f..789af452d 100644
--- a/src/video_core/gpu.cpp
+++ b/src/video_core/gpu.cpp
@@ -230,7 +230,7 @@ struct GPU::Impl {
230 void IncrementSyncPoint(u32 syncpoint_id) { 230 void IncrementSyncPoint(u32 syncpoint_id) {
231 auto& syncpoint = syncpoints.at(syncpoint_id); 231 auto& syncpoint = syncpoints.at(syncpoint_id);
232 syncpoint++; 232 syncpoint++;
233 std::lock_guard lock{sync_mutex}; 233 std::scoped_lock lock{sync_mutex};
234 sync_cv.notify_all(); 234 sync_cv.notify_all();
235 auto& interrupt = syncpt_interrupts.at(syncpoint_id); 235 auto& interrupt = syncpt_interrupts.at(syncpoint_id);
236 if (!interrupt.empty()) { 236 if (!interrupt.empty()) {
@@ -252,7 +252,7 @@ struct GPU::Impl {
252 } 252 }
253 253
254 void RegisterSyncptInterrupt(u32 syncpoint_id, u32 value) { 254 void RegisterSyncptInterrupt(u32 syncpoint_id, u32 value) {
255 std::lock_guard lock{sync_mutex}; 255 std::scoped_lock lock{sync_mutex};
256 auto& interrupt = syncpt_interrupts.at(syncpoint_id); 256 auto& interrupt = syncpt_interrupts.at(syncpoint_id);
257 bool contains = std::any_of(interrupt.begin(), interrupt.end(), 257 bool contains = std::any_of(interrupt.begin(), interrupt.end(),
258 [value](u32 in_value) { return in_value == value; }); 258 [value](u32 in_value) { return in_value == value; });
@@ -263,7 +263,7 @@ struct GPU::Impl {
263 } 263 }
264 264
265 [[nodiscard]] bool CancelSyncptInterrupt(u32 syncpoint_id, u32 value) { 265 [[nodiscard]] bool CancelSyncptInterrupt(u32 syncpoint_id, u32 value) {
266 std::lock_guard lock{sync_mutex}; 266 std::scoped_lock lock{sync_mutex};
267 auto& interrupt = syncpt_interrupts.at(syncpoint_id); 267 auto& interrupt = syncpt_interrupts.at(syncpoint_id);
268 const auto iter = 268 const auto iter =
269 std::find_if(interrupt.begin(), interrupt.end(), 269 std::find_if(interrupt.begin(), interrupt.end(),
diff --git a/src/video_core/gpu.h b/src/video_core/gpu.h
index 26b8ea233..97c029140 100644
--- a/src/video_core/gpu.h
+++ b/src/video_core/gpu.h
@@ -8,6 +8,7 @@
8 8
9#include "common/bit_field.h" 9#include "common/bit_field.h"
10#include "common/common_types.h" 10#include "common/common_types.h"
11#include "core/hle/service/nvdrv/nvdata.h"
11#include "video_core/cdma_pusher.h" 12#include "video_core/cdma_pusher.h"
12#include "video_core/framebuffer_config.h" 13#include "video_core/framebuffer_config.h"
13 14
diff --git a/src/video_core/gpu_thread.cpp b/src/video_core/gpu_thread.cpp
index 9547f277a..4e8999915 100644
--- a/src/video_core/gpu_thread.cpp
+++ b/src/video_core/gpu_thread.cpp
@@ -56,7 +56,7 @@ static void RunThread(std::stop_token stop_token, Core::System& system,
56 if (next.block) { 56 if (next.block) {
57 // We have to lock the write_lock to ensure that the condition_variable wait not get a 57 // We have to lock the write_lock to ensure that the condition_variable wait not get a
58 // race between the check and the lock itself. 58 // race between the check and the lock itself.
59 std::lock_guard lk(state.write_lock); 59 std::scoped_lock lk{state.write_lock};
60 state.cv.notify_all(); 60 state.cv.notify_all();
61 } 61 }
62 } 62 }
diff --git a/src/video_core/host_shaders/CMakeLists.txt b/src/video_core/host_shaders/CMakeLists.txt
index af05d47d1..190fc6aea 100644
--- a/src/video_core/host_shaders/CMakeLists.txt
+++ b/src/video_core/host_shaders/CMakeLists.txt
@@ -18,6 +18,7 @@ set(SHADER_FILES
18 full_screen_triangle.vert 18 full_screen_triangle.vert
19 fxaa.frag 19 fxaa.frag
20 fxaa.vert 20 fxaa.vert
21 opengl_convert_s8d24.comp
21 opengl_copy_bc4.comp 22 opengl_copy_bc4.comp
22 opengl_present.frag 23 opengl_present.frag
23 opengl_present.vert 24 opengl_present.vert
diff --git a/src/video_core/host_shaders/opengl_convert_s8d24.comp b/src/video_core/host_shaders/opengl_convert_s8d24.comp
new file mode 100644
index 000000000..83e1ab176
--- /dev/null
+++ b/src/video_core/host_shaders/opengl_convert_s8d24.comp
@@ -0,0 +1,18 @@
1// Copyright 2022 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#version 430 core
6
7layout(local_size_x = 16, local_size_y = 8) in;
8
9layout(binding = 0, rgba8ui) restrict uniform uimage2D destination;
10layout(location = 0) uniform uvec3 size;
11
12void main() {
13 if (any(greaterThanEqual(gl_GlobalInvocationID, size))) {
14 return;
15 }
16 uvec4 components = imageLoad(destination, ivec2(gl_GlobalInvocationID.xy));
17 imageStore(destination, ivec2(gl_GlobalInvocationID.xy), components.wxyz);
18}
diff --git a/src/video_core/renderer_base.cpp b/src/video_core/renderer_base.cpp
index c9627cffd..9756a81d6 100644
--- a/src/video_core/renderer_base.cpp
+++ b/src/video_core/renderer_base.cpp
@@ -26,6 +26,10 @@ void RendererBase::UpdateCurrentFramebufferLayout() {
26 render_window.UpdateCurrentFramebufferLayout(layout.width, layout.height); 26 render_window.UpdateCurrentFramebufferLayout(layout.width, layout.height);
27} 27}
28 28
29bool RendererBase::IsScreenshotPending() const {
30 return renderer_settings.screenshot_requested;
31}
32
29void RendererBase::RequestScreenshot(void* data, std::function<void(bool)> callback, 33void RendererBase::RequestScreenshot(void* data, std::function<void(bool)> callback,
30 const Layout::FramebufferLayout& layout) { 34 const Layout::FramebufferLayout& layout) {
31 if (renderer_settings.screenshot_requested) { 35 if (renderer_settings.screenshot_requested) {
diff --git a/src/video_core/renderer_base.h b/src/video_core/renderer_base.h
index c5f974080..30d19b178 100644
--- a/src/video_core/renderer_base.h
+++ b/src/video_core/renderer_base.h
@@ -83,6 +83,9 @@ public:
83 /// Refreshes the settings common to all renderers 83 /// Refreshes the settings common to all renderers
84 void RefreshBaseSettings(); 84 void RefreshBaseSettings();
85 85
86 /// Returns true if a screenshot is being processed
87 bool IsScreenshotPending() const;
88
86 /// Request a screenshot of the next frame 89 /// Request a screenshot of the next frame
87 void RequestScreenshot(void* data, std::function<void(bool)> callback, 90 void RequestScreenshot(void* data, std::function<void(bool)> callback,
88 const Layout::FramebufferLayout& layout); 91 const Layout::FramebufferLayout& layout);
diff --git a/src/video_core/renderer_opengl/gl_buffer_cache.cpp b/src/video_core/renderer_opengl/gl_buffer_cache.cpp
index d4dd10bb6..f1f7b384b 100644
--- a/src/video_core/renderer_opengl/gl_buffer_cache.cpp
+++ b/src/video_core/renderer_opengl/gl_buffer_cache.cpp
@@ -135,6 +135,20 @@ BufferCacheRuntime::BufferCacheRuntime(const Device& device_)
135 buffer.Create(); 135 buffer.Create();
136 glNamedBufferData(buffer.handle, 0x10'000, nullptr, GL_STREAM_COPY); 136 glNamedBufferData(buffer.handle, 0x10'000, nullptr, GL_STREAM_COPY);
137 } 137 }
138
139 device_access_memory = [this]() -> u64 {
140 if (device.CanReportMemoryUsage()) {
141 return device.GetCurrentDedicatedVideoMemory() + 512_MiB;
142 }
143 return 2_GiB; // Return minimum requirements
144 }();
145}
146
147u64 BufferCacheRuntime::GetDeviceMemoryUsage() const {
148 if (device.CanReportMemoryUsage()) {
149 return device_access_memory - device.GetCurrentDedicatedVideoMemory();
150 }
151 return 2_GiB;
138} 152}
139 153
140void BufferCacheRuntime::CopyBuffer(Buffer& dst_buffer, Buffer& src_buffer, 154void BufferCacheRuntime::CopyBuffer(Buffer& dst_buffer, Buffer& src_buffer,
diff --git a/src/video_core/renderer_opengl/gl_buffer_cache.h b/src/video_core/renderer_opengl/gl_buffer_cache.h
index 7287731b6..a8699f28c 100644
--- a/src/video_core/renderer_opengl/gl_buffer_cache.h
+++ b/src/video_core/renderer_opengl/gl_buffer_cache.h
@@ -89,6 +89,8 @@ public:
89 void BindImageBuffer(Buffer& buffer, u32 offset, u32 size, 89 void BindImageBuffer(Buffer& buffer, u32 offset, u32 size,
90 VideoCore::Surface::PixelFormat format); 90 VideoCore::Surface::PixelFormat format);
91 91
92 u64 GetDeviceMemoryUsage() const;
93
92 void BindFastUniformBuffer(size_t stage, u32 binding_index, u32 size) { 94 void BindFastUniformBuffer(size_t stage, u32 binding_index, u32 size) {
93 const GLuint handle = fast_uniforms[stage][binding_index].handle; 95 const GLuint handle = fast_uniforms[stage][binding_index].handle;
94 const GLsizeiptr gl_size = static_cast<GLsizeiptr>(size); 96 const GLsizeiptr gl_size = static_cast<GLsizeiptr>(size);
@@ -151,6 +153,14 @@ public:
151 use_storage_buffers = use_storage_buffers_; 153 use_storage_buffers = use_storage_buffers_;
152 } 154 }
153 155
156 u64 GetDeviceLocalMemory() const {
157 return device_access_memory;
158 }
159
160 bool CanReportMemoryUsage() const {
161 return device.CanReportMemoryUsage();
162 }
163
154private: 164private:
155 static constexpr std::array PABO_LUT{ 165 static constexpr std::array PABO_LUT{
156 GL_VERTEX_PROGRAM_PARAMETER_BUFFER_NV, GL_TESS_CONTROL_PROGRAM_PARAMETER_BUFFER_NV, 166 GL_VERTEX_PROGRAM_PARAMETER_BUFFER_NV, GL_TESS_CONTROL_PROGRAM_PARAMETER_BUFFER_NV,
@@ -184,6 +194,8 @@ private:
184 std::array<OGLBuffer, VideoCommon::NUM_COMPUTE_UNIFORM_BUFFERS> copy_compute_uniforms; 194 std::array<OGLBuffer, VideoCommon::NUM_COMPUTE_UNIFORM_BUFFERS> copy_compute_uniforms;
185 195
186 u32 index_buffer_offset = 0; 196 u32 index_buffer_offset = 0;
197
198 u64 device_access_memory;
187}; 199};
188 200
189struct BufferCacheParams { 201struct BufferCacheParams {
diff --git a/src/video_core/renderer_opengl/gl_device.cpp b/src/video_core/renderer_opengl/gl_device.cpp
index 715cd3a48..597301eeb 100644
--- a/src/video_core/renderer_opengl/gl_device.cpp
+++ b/src/video_core/renderer_opengl/gl_device.cpp
@@ -13,12 +13,15 @@
13 13
14#include <glad/glad.h> 14#include <glad/glad.h>
15 15
16#include "common/literals.h"
16#include "common/logging/log.h" 17#include "common/logging/log.h"
17#include "common/settings.h" 18#include "common/settings.h"
18#include "shader_recompiler/stage.h" 19#include "shader_recompiler/stage.h"
19#include "video_core/renderer_opengl/gl_device.h" 20#include "video_core/renderer_opengl/gl_device.h"
20#include "video_core/renderer_opengl/gl_resource_manager.h" 21#include "video_core/renderer_opengl/gl_resource_manager.h"
21 22
23using namespace Common::Literals;
24
22namespace OpenGL { 25namespace OpenGL {
23namespace { 26namespace {
24constexpr std::array LIMIT_UBOS = { 27constexpr std::array LIMIT_UBOS = {
@@ -165,6 +168,7 @@ Device::Device() {
165 has_sparse_texture_2 = GLAD_GL_ARB_sparse_texture2; 168 has_sparse_texture_2 = GLAD_GL_ARB_sparse_texture2;
166 warp_size_potentially_larger_than_guest = !is_nvidia && !is_intel; 169 warp_size_potentially_larger_than_guest = !is_nvidia && !is_intel;
167 need_fastmath_off = is_nvidia; 170 need_fastmath_off = is_nvidia;
171 can_report_memory = GLAD_GL_NVX_gpu_memory_info;
168 172
169 // At the moment of writing this, only Nvidia's driver optimizes BufferSubData on exclusive 173 // At the moment of writing this, only Nvidia's driver optimizes BufferSubData on exclusive
170 // uniform buffers as "push constants" 174 // uniform buffers as "push constants"
@@ -276,4 +280,10 @@ void main() {
276})"); 280})");
277} 281}
278 282
283u64 Device::GetCurrentDedicatedVideoMemory() const {
284 GLint cur_avail_mem_kb = 0;
285 glGetIntegerv(GL_GPU_MEMORY_INFO_TOTAL_AVAILABLE_MEMORY_NVX, &cur_avail_mem_kb);
286 return static_cast<u64>(cur_avail_mem_kb) * 1_KiB;
287}
288
279} // namespace OpenGL 289} // namespace OpenGL
diff --git a/src/video_core/renderer_opengl/gl_device.h b/src/video_core/renderer_opengl/gl_device.h
index 95c2e8d38..9bb0b9148 100644
--- a/src/video_core/renderer_opengl/gl_device.h
+++ b/src/video_core/renderer_opengl/gl_device.h
@@ -20,6 +20,8 @@ public:
20 20
21 [[nodiscard]] std::string GetVendorName() const; 21 [[nodiscard]] std::string GetVendorName() const;
22 22
23 u64 GetCurrentDedicatedVideoMemory() const;
24
23 u32 GetMaxUniformBuffers(Shader::Stage stage) const noexcept { 25 u32 GetMaxUniformBuffers(Shader::Stage stage) const noexcept {
24 return max_uniform_buffers[static_cast<size_t>(stage)]; 26 return max_uniform_buffers[static_cast<size_t>(stage)];
25 } 27 }
@@ -168,6 +170,10 @@ public:
168 return vendor_name == "ATI Technologies Inc."; 170 return vendor_name == "ATI Technologies Inc.";
169 } 171 }
170 172
173 bool CanReportMemoryUsage() const {
174 return can_report_memory;
175 }
176
171private: 177private:
172 static bool TestVariableAoffi(); 178 static bool TestVariableAoffi();
173 static bool TestPreciseBug(); 179 static bool TestPreciseBug();
@@ -210,6 +216,7 @@ private:
210 bool need_fastmath_off{}; 216 bool need_fastmath_off{};
211 bool has_cbuf_ftou_bug{}; 217 bool has_cbuf_ftou_bug{};
212 bool has_bool_ref_bug{}; 218 bool has_bool_ref_bug{};
219 bool can_report_memory{};
213 220
214 std::string vendor_name; 221 std::string vendor_name;
215}; 222};
diff --git a/src/video_core/renderer_opengl/gl_graphics_pipeline.cpp b/src/video_core/renderer_opengl/gl_graphics_pipeline.cpp
index 9e6732abd..fd40966d5 100644
--- a/src/video_core/renderer_opengl/gl_graphics_pipeline.cpp
+++ b/src/video_core/renderer_opengl/gl_graphics_pipeline.cpp
@@ -253,7 +253,7 @@ GraphicsPipeline::GraphicsPipeline(
253 } 253 }
254 } 254 }
255 if (in_parallel) { 255 if (in_parallel) {
256 std::lock_guard lock{built_mutex}; 256 std::scoped_lock lock{built_mutex};
257 built_fence.Create(); 257 built_fence.Create();
258 // Flush this context to ensure compilation commands and fence are in the GPU pipe. 258 // Flush this context to ensure compilation commands and fence are in the GPU pipe.
259 glFlush(); 259 glFlush();
diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp
index 4d632d211..8ef79753f 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer.cpp
+++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp
@@ -15,8 +15,9 @@
15#include "common/logging/log.h" 15#include "common/logging/log.h"
16#include "common/math_util.h" 16#include "common/math_util.h"
17#include "common/microprofile.h" 17#include "common/microprofile.h"
18#include "common/scope_exit.h"
18#include "common/settings.h" 19#include "common/settings.h"
19#include "core/memory.h" 20
20#include "video_core/engines/kepler_compute.h" 21#include "video_core/engines/kepler_compute.h"
21#include "video_core/engines/maxwell_3d.h" 22#include "video_core/engines/maxwell_3d.h"
22#include "video_core/memory_manager.h" 23#include "video_core/memory_manager.h"
@@ -210,6 +211,7 @@ void RasterizerOpenGL::Clear() {
210void RasterizerOpenGL::Draw(bool is_indexed, bool is_instanced) { 211void RasterizerOpenGL::Draw(bool is_indexed, bool is_instanced) {
211 MICROPROFILE_SCOPE(OpenGL_Drawing); 212 MICROPROFILE_SCOPE(OpenGL_Drawing);
212 213
214 SCOPE_EXIT({ gpu.TickWork(); });
213 query_cache.UpdateCounters(); 215 query_cache.UpdateCounters();
214 216
215 GraphicsPipeline* const pipeline{shader_cache.CurrentGraphicsPipeline()}; 217 GraphicsPipeline* const pipeline{shader_cache.CurrentGraphicsPipeline()};
@@ -265,8 +267,6 @@ void RasterizerOpenGL::Draw(bool is_indexed, bool is_instanced) {
265 267
266 ++num_queued_commands; 268 ++num_queued_commands;
267 has_written_global_memory |= pipeline->WritesGlobalMemory(); 269 has_written_global_memory |= pipeline->WritesGlobalMemory();
268
269 gpu.TickWork();
270} 270}
271 271
272void RasterizerOpenGL::DispatchCompute() { 272void RasterizerOpenGL::DispatchCompute() {
@@ -520,6 +520,8 @@ bool RasterizerOpenGL::AccelerateDisplay(const Tegra::FramebufferConfig& config,
520 // ASSERT_MSG(image_view->size.width == config.width, "Framebuffer width is different"); 520 // ASSERT_MSG(image_view->size.width == config.width, "Framebuffer width is different");
521 // ASSERT_MSG(image_view->size.height == config.height, "Framebuffer height is different"); 521 // ASSERT_MSG(image_view->size.height == config.height, "Framebuffer height is different");
522 522
523 screen_info.texture.width = image_view->size.width;
524 screen_info.texture.height = image_view->size.height;
523 screen_info.display_texture = image_view->Handle(Shader::TextureType::Color2D); 525 screen_info.display_texture = image_view->Handle(Shader::TextureType::Color2D);
524 screen_info.display_srgb = VideoCore::Surface::IsPixelFormatSRGB(image_view->format); 526 screen_info.display_srgb = VideoCore::Surface::IsPixelFormatSRGB(image_view->format);
525 return true; 527 return true;
@@ -557,12 +559,19 @@ void RasterizerOpenGL::SyncViewport() {
557 const bool dirty_viewport = flags[Dirty::Viewports] || rescale_viewports; 559 const bool dirty_viewport = flags[Dirty::Viewports] || rescale_viewports;
558 const bool dirty_clip_control = flags[Dirty::ClipControl]; 560 const bool dirty_clip_control = flags[Dirty::ClipControl];
559 561
560 if (dirty_clip_control || flags[Dirty::FrontFace]) { 562 if (dirty_viewport || dirty_clip_control || flags[Dirty::FrontFace]) {
561 flags[Dirty::FrontFace] = false; 563 flags[Dirty::FrontFace] = false;
562 564
563 GLenum mode = MaxwellToGL::FrontFace(regs.front_face); 565 GLenum mode = MaxwellToGL::FrontFace(regs.front_face);
566 bool flip_faces = false;
564 if (regs.screen_y_control.triangle_rast_flip != 0 && 567 if (regs.screen_y_control.triangle_rast_flip != 0 &&
565 regs.viewport_transform[0].scale_y < 0.0f) { 568 regs.viewport_transform[0].scale_y < 0.0f) {
569 flip_faces = !flip_faces;
570 }
571 if (regs.viewport_transform[0].scale_z < 0.0f) {
572 flip_faces = !flip_faces;
573 }
574 if (flip_faces) {
566 switch (mode) { 575 switch (mode) {
567 case GL_CW: 576 case GL_CW:
568 mode = GL_CCW; 577 mode = GL_CCW;
diff --git a/src/video_core/renderer_opengl/gl_shader_cache.cpp b/src/video_core/renderer_opengl/gl_shader_cache.cpp
index 6423992c3..05c5e702c 100644
--- a/src/video_core/renderer_opengl/gl_shader_cache.cpp
+++ b/src/video_core/renderer_opengl/gl_shader_cache.cpp
@@ -258,7 +258,7 @@ void ShaderCache::LoadDiskResources(u64 title_id, std::stop_token stop_loading,
258 [this, key, env = std::move(env), &state, &callback](Context* ctx) mutable { 258 [this, key, env = std::move(env), &state, &callback](Context* ctx) mutable {
259 ctx->pools.ReleaseContents(); 259 ctx->pools.ReleaseContents();
260 auto pipeline{CreateComputePipeline(ctx->pools, key, env)}; 260 auto pipeline{CreateComputePipeline(ctx->pools, key, env)};
261 std::lock_guard lock{state.mutex}; 261 std::scoped_lock lock{state.mutex};
262 if (pipeline) { 262 if (pipeline) {
263 compute_cache.emplace(key, std::move(pipeline)); 263 compute_cache.emplace(key, std::move(pipeline));
264 } 264 }
@@ -280,7 +280,7 @@ void ShaderCache::LoadDiskResources(u64 title_id, std::stop_token stop_loading,
280 } 280 }
281 ctx->pools.ReleaseContents(); 281 ctx->pools.ReleaseContents();
282 auto pipeline{CreateGraphicsPipeline(ctx->pools, key, MakeSpan(env_ptrs), false)}; 282 auto pipeline{CreateGraphicsPipeline(ctx->pools, key, MakeSpan(env_ptrs), false)};
283 std::lock_guard lock{state.mutex}; 283 std::scoped_lock lock{state.mutex};
284 if (pipeline) { 284 if (pipeline) {
285 graphics_cache.emplace(key, std::move(pipeline)); 285 graphics_cache.emplace(key, std::move(pipeline));
286 } 286 }
diff --git a/src/video_core/renderer_opengl/gl_texture_cache.cpp b/src/video_core/renderer_opengl/gl_texture_cache.cpp
index 3c1f79a27..f8c6e5c7e 100644
--- a/src/video_core/renderer_opengl/gl_texture_cache.cpp
+++ b/src/video_core/renderer_opengl/gl_texture_cache.cpp
@@ -182,6 +182,26 @@ GLenum AttachmentType(PixelFormat format) {
182 } 182 }
183} 183}
184 184
185GLint ConvertA5B5G5R1_UNORM(SwizzleSource source) {
186 switch (source) {
187 case SwizzleSource::Zero:
188 return GL_ZERO;
189 case SwizzleSource::R:
190 return GL_ALPHA;
191 case SwizzleSource::G:
192 return GL_BLUE;
193 case SwizzleSource::B:
194 return GL_GREEN;
195 case SwizzleSource::A:
196 return GL_RED;
197 case SwizzleSource::OneInt:
198 case SwizzleSource::OneFloat:
199 return GL_ONE;
200 }
201 UNREACHABLE_MSG("Invalid swizzle source={}", source);
202 return GL_NONE;
203}
204
185void ApplySwizzle(GLuint handle, PixelFormat format, std::array<SwizzleSource, 4> swizzle) { 205void ApplySwizzle(GLuint handle, PixelFormat format, std::array<SwizzleSource, 4> swizzle) {
186 switch (format) { 206 switch (format) {
187 case PixelFormat::D24_UNORM_S8_UINT: 207 case PixelFormat::D24_UNORM_S8_UINT:
@@ -192,6 +212,12 @@ void ApplySwizzle(GLuint handle, PixelFormat format, std::array<SwizzleSource, 4
192 TextureMode(format, swizzle[0] == SwizzleSource::R)); 212 TextureMode(format, swizzle[0] == SwizzleSource::R));
193 std::ranges::transform(swizzle, swizzle.begin(), ConvertGreenRed); 213 std::ranges::transform(swizzle, swizzle.begin(), ConvertGreenRed);
194 break; 214 break;
215 case PixelFormat::A5B5G5R1_UNORM: {
216 std::array<GLint, 4> gl_swizzle;
217 std::ranges::transform(swizzle, gl_swizzle.begin(), ConvertA5B5G5R1_UNORM);
218 glTextureParameteriv(handle, GL_TEXTURE_SWIZZLE_RGBA, gl_swizzle.data());
219 return;
220 }
195 default: 221 default:
196 break; 222 break;
197 } 223 }
@@ -409,8 +435,8 @@ ImageBufferMap::~ImageBufferMap() {
409 435
410TextureCacheRuntime::TextureCacheRuntime(const Device& device_, ProgramManager& program_manager, 436TextureCacheRuntime::TextureCacheRuntime(const Device& device_, ProgramManager& program_manager,
411 StateTracker& state_tracker_) 437 StateTracker& state_tracker_)
412 : device{device_}, state_tracker{state_tracker_}, 438 : device{device_}, state_tracker{state_tracker_}, util_shaders(program_manager),
413 util_shaders(program_manager), resolution{Settings::values.resolution_info} { 439 format_conversion_pass{util_shaders}, resolution{Settings::values.resolution_info} {
414 static constexpr std::array TARGETS{GL_TEXTURE_1D_ARRAY, GL_TEXTURE_2D_ARRAY, GL_TEXTURE_3D}; 440 static constexpr std::array TARGETS{GL_TEXTURE_1D_ARRAY, GL_TEXTURE_2D_ARRAY, GL_TEXTURE_3D};
415 for (size_t i = 0; i < TARGETS.size(); ++i) { 441 for (size_t i = 0; i < TARGETS.size(); ++i) {
416 const GLenum target = TARGETS[i]; 442 const GLenum target = TARGETS[i];
@@ -484,6 +510,13 @@ TextureCacheRuntime::TextureCacheRuntime(const Device& device_, ProgramManager&
484 rescale_read_fbos[i].Create(); 510 rescale_read_fbos[i].Create();
485 } 511 }
486 } 512 }
513
514 device_access_memory = [this]() -> u64 {
515 if (device.CanReportMemoryUsage()) {
516 return device.GetCurrentDedicatedVideoMemory() + 512_MiB;
517 }
518 return 2_GiB; // Return minimum requirements
519 }();
487} 520}
488 521
489TextureCacheRuntime::~TextureCacheRuntime() = default; 522TextureCacheRuntime::~TextureCacheRuntime() = default;
@@ -500,13 +533,11 @@ ImageBufferMap TextureCacheRuntime::DownloadStagingBuffer(size_t size) {
500 return download_buffers.RequestMap(size, false); 533 return download_buffers.RequestMap(size, false);
501} 534}
502 535
503u64 TextureCacheRuntime::GetDeviceLocalMemory() const { 536u64 TextureCacheRuntime::GetDeviceMemoryUsage() const {
504 if (GLAD_GL_NVX_gpu_memory_info) { 537 if (device.CanReportMemoryUsage()) {
505 GLint cur_avail_mem_kb = 0; 538 return device_access_memory - device.GetCurrentDedicatedVideoMemory();
506 glGetIntegerv(GL_GPU_MEMORY_INFO_CURRENT_AVAILABLE_VIDMEM_NVX, &cur_avail_mem_kb);
507 return static_cast<u64>(cur_avail_mem_kb) * 1_KiB;
508 } 539 }
509 return 2_GiB; // Return minimum requirements 540 return 2_GiB;
510} 541}
511 542
512void TextureCacheRuntime::CopyImage(Image& dst_image, Image& src_image, 543void TextureCacheRuntime::CopyImage(Image& dst_image, Image& src_image,
@@ -686,6 +717,7 @@ Image::Image(TextureCacheRuntime& runtime_, const VideoCommon::ImageInfo& info_,
686 } 717 }
687 if (IsConverted(runtime->device, info.format, info.type)) { 718 if (IsConverted(runtime->device, info.format, info.type)) {
688 flags |= ImageFlagBits::Converted; 719 flags |= ImageFlagBits::Converted;
720 flags |= ImageFlagBits::CostlyLoad;
689 gl_internal_format = IsPixelFormatSRGB(info.format) ? GL_SRGB8_ALPHA8 : GL_RGBA8; 721 gl_internal_format = IsPixelFormatSRGB(info.format) ? GL_SRGB8_ALPHA8 : GL_RGBA8;
690 gl_format = GL_RGBA; 722 gl_format = GL_RGBA;
691 gl_type = GL_UNSIGNED_INT_8_8_8_8_REV; 723 gl_type = GL_UNSIGNED_INT_8_8_8_8_REV;
@@ -1319,6 +1351,9 @@ Framebuffer::Framebuffer(TextureCacheRuntime& runtime, std::span<ImageView*, NUM
1319 1351
1320Framebuffer::~Framebuffer() = default; 1352Framebuffer::~Framebuffer() = default;
1321 1353
1354FormatConversionPass::FormatConversionPass(UtilShaders& util_shaders_)
1355 : util_shaders{util_shaders_} {}
1356
1322void FormatConversionPass::ConvertImage(Image& dst_image, Image& src_image, 1357void FormatConversionPass::ConvertImage(Image& dst_image, Image& src_image,
1323 std::span<const VideoCommon::ImageCopy> copies) { 1358 std::span<const VideoCommon::ImageCopy> copies) {
1324 const GLenum dst_target = ImageTarget(dst_image.info); 1359 const GLenum dst_target = ImageTarget(dst_image.info);
@@ -1351,6 +1386,12 @@ void FormatConversionPass::ConvertImage(Image& dst_image, Image& src_image,
1351 dst_origin.z, region.width, region.height, region.depth, 1386 dst_origin.z, region.width, region.height, region.depth,
1352 dst_image.GlFormat(), dst_image.GlType(), nullptr); 1387 dst_image.GlFormat(), dst_image.GlType(), nullptr);
1353 } 1388 }
1389
1390 // Swap component order of S8D24 to ABGR8 reinterprets
1391 if (src_image.info.format == PixelFormat::D24_UNORM_S8_UINT &&
1392 dst_image.info.format == PixelFormat::A8B8G8R8_UNORM) {
1393 util_shaders.ConvertS8D24(dst_image, copies);
1394 }
1354} 1395}
1355 1396
1356} // namespace OpenGL 1397} // namespace OpenGL
diff --git a/src/video_core/renderer_opengl/gl_texture_cache.h b/src/video_core/renderer_opengl/gl_texture_cache.h
index 7f425631f..672fa8dde 100644
--- a/src/video_core/renderer_opengl/gl_texture_cache.h
+++ b/src/video_core/renderer_opengl/gl_texture_cache.h
@@ -10,6 +10,7 @@
10#include <glad/glad.h> 10#include <glad/glad.h>
11 11
12#include "shader_recompiler/shader_info.h" 12#include "shader_recompiler/shader_info.h"
13#include "video_core/renderer_opengl/gl_device.h"
13#include "video_core/renderer_opengl/gl_resource_manager.h" 14#include "video_core/renderer_opengl/gl_resource_manager.h"
14#include "video_core/renderer_opengl/util_shaders.h" 15#include "video_core/renderer_opengl/util_shaders.h"
15#include "video_core/texture_cache/image_view_base.h" 16#include "video_core/texture_cache/image_view_base.h"
@@ -21,7 +22,6 @@ struct ResolutionScalingInfo;
21 22
22namespace OpenGL { 23namespace OpenGL {
23 24
24class Device;
25class ProgramManager; 25class ProgramManager;
26class StateTracker; 26class StateTracker;
27 27
@@ -55,13 +55,14 @@ struct FormatProperties {
55 55
56class FormatConversionPass { 56class FormatConversionPass {
57public: 57public:
58 FormatConversionPass() = default; 58 explicit FormatConversionPass(UtilShaders& util_shaders);
59 ~FormatConversionPass() = default; 59 ~FormatConversionPass() = default;
60 60
61 void ConvertImage(Image& dst_image, Image& src_image, 61 void ConvertImage(Image& dst_image, Image& src_image,
62 std::span<const VideoCommon::ImageCopy> copies); 62 std::span<const VideoCommon::ImageCopy> copies);
63 63
64private: 64private:
65 UtilShaders& util_shaders;
65 OGLBuffer intermediate_pbo; 66 OGLBuffer intermediate_pbo;
66 size_t pbo_size{}; 67 size_t pbo_size{};
67}; 68};
@@ -83,7 +84,15 @@ public:
83 84
84 ImageBufferMap DownloadStagingBuffer(size_t size); 85 ImageBufferMap DownloadStagingBuffer(size_t size);
85 86
86 u64 GetDeviceLocalMemory() const; 87 u64 GetDeviceLocalMemory() const {
88 return device_access_memory;
89 }
90
91 u64 GetDeviceMemoryUsage() const;
92
93 bool CanReportMemoryUsage() const {
94 return device.CanReportMemoryUsage();
95 }
87 96
88 bool ShouldReinterpret([[maybe_unused]] Image& dst, [[maybe_unused]] Image& src) { 97 bool ShouldReinterpret([[maybe_unused]] Image& dst, [[maybe_unused]] Image& src) {
89 return true; 98 return true;
@@ -172,6 +181,7 @@ private:
172 std::array<OGLFramebuffer, 4> rescale_draw_fbos; 181 std::array<OGLFramebuffer, 4> rescale_draw_fbos;
173 std::array<OGLFramebuffer, 4> rescale_read_fbos; 182 std::array<OGLFramebuffer, 4> rescale_read_fbos;
174 const Settings::ResolutionScalingInfo& resolution; 183 const Settings::ResolutionScalingInfo& resolution;
184 u64 device_access_memory;
175}; 185};
176 186
177class Image : public VideoCommon::ImageBase { 187class Image : public VideoCommon::ImageBase {
diff --git a/src/video_core/renderer_opengl/maxwell_to_gl.h b/src/video_core/renderer_opengl/maxwell_to_gl.h
index db5bf1d30..03adf3d4c 100644
--- a/src/video_core/renderer_opengl/maxwell_to_gl.h
+++ b/src/video_core/renderer_opengl/maxwell_to_gl.h
@@ -30,6 +30,7 @@ constexpr std::array<FormatTuple, VideoCore::Surface::MaxPixelFormat> FORMAT_TAB
30 {GL_RGB10_A2, GL_RGBA, GL_UNSIGNED_INT_2_10_10_10_REV}, // A2B10G10R10_UNORM 30 {GL_RGB10_A2, GL_RGBA, GL_UNSIGNED_INT_2_10_10_10_REV}, // A2B10G10R10_UNORM
31 {GL_RGB10_A2UI, GL_RGBA_INTEGER, GL_UNSIGNED_INT_2_10_10_10_REV}, // A2B10G10R10_UINT 31 {GL_RGB10_A2UI, GL_RGBA_INTEGER, GL_UNSIGNED_INT_2_10_10_10_REV}, // A2B10G10R10_UINT
32 {GL_RGB5_A1, GL_RGBA, GL_UNSIGNED_SHORT_1_5_5_5_REV}, // A1B5G5R5_UNORM 32 {GL_RGB5_A1, GL_RGBA, GL_UNSIGNED_SHORT_1_5_5_5_REV}, // A1B5G5R5_UNORM
33 {GL_RGB5_A1, GL_RGBA, GL_UNSIGNED_SHORT_5_5_5_1}, // A5B5G5R1_UNORM
33 {GL_R8, GL_RED, GL_UNSIGNED_BYTE}, // R8_UNORM 34 {GL_R8, GL_RED, GL_UNSIGNED_BYTE}, // R8_UNORM
34 {GL_R8_SNORM, GL_RED, GL_BYTE}, // R8_SNORM 35 {GL_R8_SNORM, GL_RED, GL_BYTE}, // R8_SNORM
35 {GL_R8I, GL_RED_INTEGER, GL_BYTE}, // R8_SINT 36 {GL_R8I, GL_RED_INTEGER, GL_BYTE}, // R8_SINT
@@ -87,6 +88,7 @@ constexpr std::array<FormatTuple, VideoCore::Surface::MaxPixelFormat> FORMAT_TAB
87 {GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT}, // BC3_SRGB 88 {GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT}, // BC3_SRGB
88 {GL_COMPRESSED_SRGB_ALPHA_BPTC_UNORM}, // BC7_SRGB 89 {GL_COMPRESSED_SRGB_ALPHA_BPTC_UNORM}, // BC7_SRGB
89 {GL_RGBA4, GL_RGBA, GL_UNSIGNED_SHORT_4_4_4_4_REV}, // A4B4G4R4_UNORM 90 {GL_RGBA4, GL_RGBA, GL_UNSIGNED_SHORT_4_4_4_4_REV}, // A4B4G4R4_UNORM
91 {GL_R8, GL_RED, GL_UNSIGNED_BYTE}, // R4G4_UNORM
90 {GL_COMPRESSED_SRGB8_ALPHA8_ASTC_4x4_KHR}, // ASTC_2D_4X4_SRGB 92 {GL_COMPRESSED_SRGB8_ALPHA8_ASTC_4x4_KHR}, // ASTC_2D_4X4_SRGB
91 {GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x8_KHR}, // ASTC_2D_8X8_SRGB 93 {GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x8_KHR}, // ASTC_2D_8X8_SRGB
92 {GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x5_KHR}, // ASTC_2D_8X5_SRGB 94 {GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x5_KHR}, // ASTC_2D_8X5_SRGB
diff --git a/src/video_core/renderer_opengl/renderer_opengl.cpp b/src/video_core/renderer_opengl/renderer_opengl.cpp
index 795c97831..3a3c213bb 100644
--- a/src/video_core/renderer_opengl/renderer_opengl.cpp
+++ b/src/video_core/renderer_opengl/renderer_opengl.cpp
@@ -208,6 +208,8 @@ void RendererOpenGL::LoadFBToScreenInfo(const Tegra::FramebufferConfig& framebuf
208 // Framebuffer orientation handling 208 // Framebuffer orientation handling
209 framebuffer_transform_flags = framebuffer.transform_flags; 209 framebuffer_transform_flags = framebuffer.transform_flags;
210 framebuffer_crop_rect = framebuffer.crop_rect; 210 framebuffer_crop_rect = framebuffer.crop_rect;
211 framebuffer_width = framebuffer.width;
212 framebuffer_height = framebuffer.height;
211 213
212 const VAddr framebuffer_addr{framebuffer.address + framebuffer.offset}; 214 const VAddr framebuffer_addr{framebuffer.address + framebuffer.offset};
213 screen_info.was_accelerated = 215 screen_info.was_accelerated =
@@ -323,12 +325,12 @@ void RendererOpenGL::ConfigureFramebufferTexture(TextureInfo& texture,
323 325
324 GLint internal_format; 326 GLint internal_format;
325 switch (framebuffer.pixel_format) { 327 switch (framebuffer.pixel_format) {
326 case Tegra::FramebufferConfig::PixelFormat::A8B8G8R8_UNORM: 328 case Service::android::PixelFormat::Rgba8888:
327 internal_format = GL_RGBA8; 329 internal_format = GL_RGBA8;
328 texture.gl_format = GL_RGBA; 330 texture.gl_format = GL_RGBA;
329 texture.gl_type = GL_UNSIGNED_INT_8_8_8_8_REV; 331 texture.gl_type = GL_UNSIGNED_INT_8_8_8_8_REV;
330 break; 332 break;
331 case Tegra::FramebufferConfig::PixelFormat::RGB565_UNORM: 333 case Service::android::PixelFormat::Rgb565:
332 internal_format = GL_RGB565; 334 internal_format = GL_RGB565;
333 texture.gl_format = GL_RGB; 335 texture.gl_format = GL_RGB;
334 texture.gl_type = GL_UNSIGNED_SHORT_5_6_5; 336 texture.gl_type = GL_UNSIGNED_SHORT_5_6_5;
@@ -464,8 +466,8 @@ void RendererOpenGL::DrawScreen(const Layout::FramebufferLayout& layout) {
464 const auto& texcoords = screen_info.display_texcoords; 466 const auto& texcoords = screen_info.display_texcoords;
465 auto left = texcoords.left; 467 auto left = texcoords.left;
466 auto right = texcoords.right; 468 auto right = texcoords.right;
467 if (framebuffer_transform_flags != Tegra::FramebufferConfig::TransformFlags::Unset) { 469 if (framebuffer_transform_flags != Service::android::BufferTransformFlags::Unset) {
468 if (framebuffer_transform_flags == Tegra::FramebufferConfig::TransformFlags::FlipV) { 470 if (framebuffer_transform_flags == Service::android::BufferTransformFlags::FlipV) {
469 // Flip the framebuffer vertically 471 // Flip the framebuffer vertically
470 left = texcoords.right; 472 left = texcoords.right;
471 right = texcoords.left; 473 right = texcoords.left;
@@ -480,9 +482,12 @@ void RendererOpenGL::DrawScreen(const Layout::FramebufferLayout& layout) {
480 ASSERT_MSG(framebuffer_crop_rect.top == 0, "Unimplemented"); 482 ASSERT_MSG(framebuffer_crop_rect.top == 0, "Unimplemented");
481 ASSERT_MSG(framebuffer_crop_rect.left == 0, "Unimplemented"); 483 ASSERT_MSG(framebuffer_crop_rect.left == 0, "Unimplemented");
482 484
485 f32 scale_u = static_cast<f32>(framebuffer_width) / static_cast<f32>(screen_info.texture.width);
486 f32 scale_v =
487 static_cast<f32>(framebuffer_height) / static_cast<f32>(screen_info.texture.height);
488
483 // Scale the output by the crop width/height. This is commonly used with 1280x720 rendering 489 // Scale the output by the crop width/height. This is commonly used with 1280x720 rendering
484 // (e.g. handheld mode) on a 1920x1080 framebuffer. 490 // (e.g. handheld mode) on a 1920x1080 framebuffer.
485 f32 scale_u = 1.f, scale_v = 1.f;
486 if (framebuffer_crop_rect.GetWidth() > 0) { 491 if (framebuffer_crop_rect.GetWidth() > 0) {
487 scale_u = static_cast<f32>(framebuffer_crop_rect.GetWidth()) / 492 scale_u = static_cast<f32>(framebuffer_crop_rect.GetWidth()) /
488 static_cast<f32>(screen_info.texture.width); 493 static_cast<f32>(screen_info.texture.width);
diff --git a/src/video_core/renderer_opengl/renderer_opengl.h b/src/video_core/renderer_opengl/renderer_opengl.h
index 35706cf05..ae9558a33 100644
--- a/src/video_core/renderer_opengl/renderer_opengl.h
+++ b/src/video_core/renderer_opengl/renderer_opengl.h
@@ -46,7 +46,7 @@ struct TextureInfo {
46 GLsizei height; 46 GLsizei height;
47 GLenum gl_format; 47 GLenum gl_format;
48 GLenum gl_type; 48 GLenum gl_type;
49 Tegra::FramebufferConfig::PixelFormat pixel_format; 49 Service::android::PixelFormat pixel_format;
50}; 50};
51 51
52/// Structure used for storing information about the display target for the Switch screen 52/// Structure used for storing information about the display target for the Switch screen
@@ -135,8 +135,10 @@ private:
135 std::vector<u8> gl_framebuffer_data; 135 std::vector<u8> gl_framebuffer_data;
136 136
137 /// Used for transforming the framebuffer orientation 137 /// Used for transforming the framebuffer orientation
138 Tegra::FramebufferConfig::TransformFlags framebuffer_transform_flags{}; 138 Service::android::BufferTransformFlags framebuffer_transform_flags{};
139 Common::Rectangle<int> framebuffer_crop_rect; 139 Common::Rectangle<int> framebuffer_crop_rect;
140 u32 framebuffer_width;
141 u32 framebuffer_height;
140}; 142};
141 143
142} // namespace OpenGL 144} // namespace OpenGL
diff --git a/src/video_core/renderer_opengl/util_shaders.cpp b/src/video_core/renderer_opengl/util_shaders.cpp
index 897c380b3..04c482a09 100644
--- a/src/video_core/renderer_opengl/util_shaders.cpp
+++ b/src/video_core/renderer_opengl/util_shaders.cpp
@@ -13,6 +13,7 @@
13#include "video_core/host_shaders/astc_decoder_comp.h" 13#include "video_core/host_shaders/astc_decoder_comp.h"
14#include "video_core/host_shaders/block_linear_unswizzle_2d_comp.h" 14#include "video_core/host_shaders/block_linear_unswizzle_2d_comp.h"
15#include "video_core/host_shaders/block_linear_unswizzle_3d_comp.h" 15#include "video_core/host_shaders/block_linear_unswizzle_3d_comp.h"
16#include "video_core/host_shaders/opengl_convert_s8d24_comp.h"
16#include "video_core/host_shaders/opengl_copy_bc4_comp.h" 17#include "video_core/host_shaders/opengl_copy_bc4_comp.h"
17#include "video_core/host_shaders/pitch_unswizzle_comp.h" 18#include "video_core/host_shaders/pitch_unswizzle_comp.h"
18#include "video_core/renderer_opengl/gl_shader_manager.h" 19#include "video_core/renderer_opengl/gl_shader_manager.h"
@@ -50,7 +51,8 @@ UtilShaders::UtilShaders(ProgramManager& program_manager_)
50 block_linear_unswizzle_2d_program(MakeProgram(BLOCK_LINEAR_UNSWIZZLE_2D_COMP)), 51 block_linear_unswizzle_2d_program(MakeProgram(BLOCK_LINEAR_UNSWIZZLE_2D_COMP)),
51 block_linear_unswizzle_3d_program(MakeProgram(BLOCK_LINEAR_UNSWIZZLE_3D_COMP)), 52 block_linear_unswizzle_3d_program(MakeProgram(BLOCK_LINEAR_UNSWIZZLE_3D_COMP)),
52 pitch_unswizzle_program(MakeProgram(PITCH_UNSWIZZLE_COMP)), 53 pitch_unswizzle_program(MakeProgram(PITCH_UNSWIZZLE_COMP)),
53 copy_bc4_program(MakeProgram(OPENGL_COPY_BC4_COMP)) { 54 copy_bc4_program(MakeProgram(OPENGL_COPY_BC4_COMP)),
55 convert_s8d24_program(MakeProgram(OPENGL_CONVERT_S8D24_COMP)) {
54 const auto swizzle_table = Tegra::Texture::MakeSwizzleTable(); 56 const auto swizzle_table = Tegra::Texture::MakeSwizzleTable();
55 swizzle_table_buffer.Create(); 57 swizzle_table_buffer.Create();
56 glNamedBufferStorage(swizzle_table_buffer.handle, sizeof(swizzle_table), &swizzle_table, 0); 58 glNamedBufferStorage(swizzle_table_buffer.handle, sizeof(swizzle_table), &swizzle_table, 0);
@@ -248,6 +250,26 @@ void UtilShaders::CopyBC4(Image& dst_image, Image& src_image, std::span<const Im
248 program_manager.RestoreGuestCompute(); 250 program_manager.RestoreGuestCompute();
249} 251}
250 252
253void UtilShaders::ConvertS8D24(Image& dst_image, std::span<const ImageCopy> copies) {
254 static constexpr GLuint BINDING_DESTINATION = 0;
255 static constexpr GLuint LOC_SIZE = 0;
256
257 program_manager.BindComputeProgram(convert_s8d24_program.handle);
258 for (const ImageCopy& copy : copies) {
259 ASSERT(copy.src_subresource.base_layer == 0);
260 ASSERT(copy.src_subresource.num_layers == 1);
261 ASSERT(copy.dst_subresource.base_layer == 0);
262 ASSERT(copy.dst_subresource.num_layers == 1);
263
264 glUniform3ui(LOC_SIZE, copy.extent.width, copy.extent.height, copy.extent.depth);
265 glBindImageTexture(BINDING_DESTINATION, dst_image.StorageHandle(),
266 copy.dst_subresource.base_level, GL_TRUE, 0, GL_READ_WRITE, GL_RGBA8UI);
267 glDispatchCompute(Common::DivCeil(copy.extent.width, 16u),
268 Common::DivCeil(copy.extent.height, 8u), copy.extent.depth);
269 }
270 program_manager.RestoreGuestCompute();
271}
272
251GLenum StoreFormat(u32 bytes_per_block) { 273GLenum StoreFormat(u32 bytes_per_block) {
252 switch (bytes_per_block) { 274 switch (bytes_per_block) {
253 case 1: 275 case 1:
diff --git a/src/video_core/renderer_opengl/util_shaders.h b/src/video_core/renderer_opengl/util_shaders.h
index 5de95ea7a..5c132e67f 100644
--- a/src/video_core/renderer_opengl/util_shaders.h
+++ b/src/video_core/renderer_opengl/util_shaders.h
@@ -39,6 +39,8 @@ public:
39 void CopyBC4(Image& dst_image, Image& src_image, 39 void CopyBC4(Image& dst_image, Image& src_image,
40 std::span<const VideoCommon::ImageCopy> copies); 40 std::span<const VideoCommon::ImageCopy> copies);
41 41
42 void ConvertS8D24(Image& dst_image, std::span<const VideoCommon::ImageCopy> copies);
43
42private: 44private:
43 ProgramManager& program_manager; 45 ProgramManager& program_manager;
44 46
@@ -49,6 +51,7 @@ private:
49 OGLProgram block_linear_unswizzle_3d_program; 51 OGLProgram block_linear_unswizzle_3d_program;
50 OGLProgram pitch_unswizzle_program; 52 OGLProgram pitch_unswizzle_program;
51 OGLProgram copy_bc4_program; 53 OGLProgram copy_bc4_program;
54 OGLProgram convert_s8d24_program;
52}; 55};
53 56
54GLenum StoreFormat(u32 bytes_per_block); 57GLenum StoreFormat(u32 bytes_per_block);
diff --git a/src/video_core/renderer_vulkan/blit_image.cpp b/src/video_core/renderer_vulkan/blit_image.cpp
index ec03cca38..abda1c490 100644
--- a/src/video_core/renderer_vulkan/blit_image.cpp
+++ b/src/video_core/renderer_vulkan/blit_image.cpp
@@ -367,17 +367,14 @@ BlitImageHelper::BlitImageHelper(const Device& device_, VKScheduler& scheduler_,
367 PipelineLayoutCreateInfo(two_textures_set_layout.address()))), 367 PipelineLayoutCreateInfo(two_textures_set_layout.address()))),
368 full_screen_vert(BuildShader(device, FULL_SCREEN_TRIANGLE_VERT_SPV)), 368 full_screen_vert(BuildShader(device, FULL_SCREEN_TRIANGLE_VERT_SPV)),
369 blit_color_to_color_frag(BuildShader(device, VULKAN_BLIT_COLOR_FLOAT_FRAG_SPV)), 369 blit_color_to_color_frag(BuildShader(device, VULKAN_BLIT_COLOR_FLOAT_FRAG_SPV)),
370 blit_depth_stencil_frag(BuildShader(device, VULKAN_BLIT_DEPTH_STENCIL_FRAG_SPV)),
370 convert_depth_to_float_frag(BuildShader(device, CONVERT_DEPTH_TO_FLOAT_FRAG_SPV)), 371 convert_depth_to_float_frag(BuildShader(device, CONVERT_DEPTH_TO_FLOAT_FRAG_SPV)),
371 convert_float_to_depth_frag(BuildShader(device, CONVERT_FLOAT_TO_DEPTH_FRAG_SPV)), 372 convert_float_to_depth_frag(BuildShader(device, CONVERT_FLOAT_TO_DEPTH_FRAG_SPV)),
372 convert_abgr8_to_d24s8_frag(BuildShader(device, CONVERT_ABGR8_TO_D24S8_FRAG_SPV)), 373 convert_abgr8_to_d24s8_frag(BuildShader(device, CONVERT_ABGR8_TO_D24S8_FRAG_SPV)),
373 convert_d24s8_to_abgr8_frag(BuildShader(device, CONVERT_D24S8_TO_ABGR8_FRAG_SPV)), 374 convert_d24s8_to_abgr8_frag(BuildShader(device, CONVERT_D24S8_TO_ABGR8_FRAG_SPV)),
374 convert_s8d24_to_abgr8_frag(BuildShader(device, CONVERT_S8D24_TO_ABGR8_FRAG_SPV)), 375 convert_s8d24_to_abgr8_frag(BuildShader(device, CONVERT_S8D24_TO_ABGR8_FRAG_SPV)),
375 linear_sampler(device.GetLogical().CreateSampler(SAMPLER_CREATE_INFO<VK_FILTER_LINEAR>)), 376 linear_sampler(device.GetLogical().CreateSampler(SAMPLER_CREATE_INFO<VK_FILTER_LINEAR>)),
376 nearest_sampler(device.GetLogical().CreateSampler(SAMPLER_CREATE_INFO<VK_FILTER_NEAREST>)) { 377 nearest_sampler(device.GetLogical().CreateSampler(SAMPLER_CREATE_INFO<VK_FILTER_NEAREST>)) {}
377 if (device.IsExtShaderStencilExportSupported()) {
378 blit_depth_stencil_frag = BuildShader(device, VULKAN_BLIT_DEPTH_STENCIL_FRAG_SPV);
379 }
380}
381 378
382BlitImageHelper::~BlitImageHelper() = default; 379BlitImageHelper::~BlitImageHelper() = default;
383 380
diff --git a/src/video_core/renderer_vulkan/maxwell_to_vk.cpp b/src/video_core/renderer_vulkan/maxwell_to_vk.cpp
index 1c136c410..a2c6d0e6c 100644
--- a/src/video_core/renderer_vulkan/maxwell_to_vk.cpp
+++ b/src/video_core/renderer_vulkan/maxwell_to_vk.cpp
@@ -127,6 +127,7 @@ struct FormatTuple {
127 {VK_FORMAT_A2B10G10R10_UNORM_PACK32, Attachable | Storage}, // A2B10G10R10_UNORM 127 {VK_FORMAT_A2B10G10R10_UNORM_PACK32, Attachable | Storage}, // A2B10G10R10_UNORM
128 {VK_FORMAT_A2B10G10R10_UINT_PACK32, Attachable | Storage}, // A2B10G10R10_UINT 128 {VK_FORMAT_A2B10G10R10_UINT_PACK32, Attachable | Storage}, // A2B10G10R10_UINT
129 {VK_FORMAT_A1R5G5B5_UNORM_PACK16, Attachable}, // A1B5G5R5_UNORM (flipped with swizzle) 129 {VK_FORMAT_A1R5G5B5_UNORM_PACK16, Attachable}, // A1B5G5R5_UNORM (flipped with swizzle)
130 {VK_FORMAT_R5G5B5A1_UNORM_PACK16}, // A5B5G5R1_UNORM (specially swizzled)
130 {VK_FORMAT_R8_UNORM, Attachable | Storage}, // R8_UNORM 131 {VK_FORMAT_R8_UNORM, Attachable | Storage}, // R8_UNORM
131 {VK_FORMAT_R8_SNORM, Attachable | Storage}, // R8_SNORM 132 {VK_FORMAT_R8_SNORM, Attachable | Storage}, // R8_SNORM
132 {VK_FORMAT_R8_SINT, Attachable | Storage}, // R8_SINT 133 {VK_FORMAT_R8_SINT, Attachable | Storage}, // R8_SINT
@@ -184,6 +185,7 @@ struct FormatTuple {
184 {VK_FORMAT_BC3_SRGB_BLOCK}, // BC3_SRGB 185 {VK_FORMAT_BC3_SRGB_BLOCK}, // BC3_SRGB
185 {VK_FORMAT_BC7_SRGB_BLOCK}, // BC7_SRGB 186 {VK_FORMAT_BC7_SRGB_BLOCK}, // BC7_SRGB
186 {VK_FORMAT_R4G4B4A4_UNORM_PACK16, Attachable}, // A4B4G4R4_UNORM 187 {VK_FORMAT_R4G4B4A4_UNORM_PACK16, Attachable}, // A4B4G4R4_UNORM
188 {VK_FORMAT_R4G4_UNORM_PACK8}, // R4G4_UNORM
187 {VK_FORMAT_ASTC_4x4_SRGB_BLOCK}, // ASTC_2D_4X4_SRGB 189 {VK_FORMAT_ASTC_4x4_SRGB_BLOCK}, // ASTC_2D_4X4_SRGB
188 {VK_FORMAT_ASTC_8x8_SRGB_BLOCK}, // ASTC_2D_8X8_SRGB 190 {VK_FORMAT_ASTC_8x8_SRGB_BLOCK}, // ASTC_2D_8X8_SRGB
189 {VK_FORMAT_ASTC_8x5_SRGB_BLOCK}, // ASTC_2D_8X5_SRGB 191 {VK_FORMAT_ASTC_8x5_SRGB_BLOCK}, // ASTC_2D_8X5_SRGB
diff --git a/src/video_core/renderer_vulkan/pipeline_statistics.cpp b/src/video_core/renderer_vulkan/pipeline_statistics.cpp
index bfec931a6..7ccadf084 100644
--- a/src/video_core/renderer_vulkan/pipeline_statistics.cpp
+++ b/src/video_core/renderer_vulkan/pipeline_statistics.cpp
@@ -57,7 +57,7 @@ void PipelineStatistics::Collect(VkPipeline pipeline) {
57 stage_stats.basic_block_count = GetUint64(statistic); 57 stage_stats.basic_block_count = GetUint64(statistic);
58 } 58 }
59 } 59 }
60 std::lock_guard lock{mutex}; 60 std::scoped_lock lock{mutex};
61 collected_stats.push_back(stage_stats); 61 collected_stats.push_back(stage_stats);
62 } 62 }
63} 63}
@@ -66,7 +66,7 @@ void PipelineStatistics::Report() const {
66 double num{}; 66 double num{};
67 Stats total; 67 Stats total;
68 { 68 {
69 std::lock_guard lock{mutex}; 69 std::scoped_lock lock{mutex};
70 for (const Stats& stats : collected_stats) { 70 for (const Stats& stats : collected_stats) {
71 total.code_size += stats.code_size; 71 total.code_size += stats.code_size;
72 total.register_count += stats.register_count; 72 total.register_count += stats.register_count;
diff --git a/src/video_core/renderer_vulkan/vk_blit_screen.cpp b/src/video_core/renderer_vulkan/vk_blit_screen.cpp
index 0ec85682b..b866e9103 100644
--- a/src/video_core/renderer_vulkan/vk_blit_screen.cpp
+++ b/src/video_core/renderer_vulkan/vk_blit_screen.cpp
@@ -94,11 +94,11 @@ std::size_t GetSizeInBytes(const Tegra::FramebufferConfig& framebuffer) {
94 94
95VkFormat GetFormat(const Tegra::FramebufferConfig& framebuffer) { 95VkFormat GetFormat(const Tegra::FramebufferConfig& framebuffer) {
96 switch (framebuffer.pixel_format) { 96 switch (framebuffer.pixel_format) {
97 case Tegra::FramebufferConfig::PixelFormat::A8B8G8R8_UNORM: 97 case Service::android::PixelFormat::Rgba8888:
98 return VK_FORMAT_A8B8G8R8_UNORM_PACK32; 98 return VK_FORMAT_A8B8G8R8_UNORM_PACK32;
99 case Tegra::FramebufferConfig::PixelFormat::RGB565_UNORM: 99 case Service::android::PixelFormat::Rgb565:
100 return VK_FORMAT_R5G6B5_UNORM_PACK16; 100 return VK_FORMAT_R5G6B5_UNORM_PACK16;
101 case Tegra::FramebufferConfig::PixelFormat::B8G8R8A8_UNORM: 101 case Service::android::PixelFormat::Bgra8888:
102 return VK_FORMAT_B8G8R8A8_UNORM; 102 return VK_FORMAT_B8G8R8A8_UNORM;
103 default: 103 default:
104 UNIMPLEMENTED_MSG("Unknown framebuffer pixel format: {}", 104 UNIMPLEMENTED_MSG("Unknown framebuffer pixel format: {}",
@@ -1390,9 +1390,9 @@ void VKBlitScreen::SetVertexData(BufferData& data, const Tegra::FramebufferConfi
1390 auto right = texcoords.right; 1390 auto right = texcoords.right;
1391 1391
1392 switch (framebuffer_transform_flags) { 1392 switch (framebuffer_transform_flags) {
1393 case Tegra::FramebufferConfig::TransformFlags::Unset: 1393 case Service::android::BufferTransformFlags::Unset:
1394 break; 1394 break;
1395 case Tegra::FramebufferConfig::TransformFlags::FlipV: 1395 case Service::android::BufferTransformFlags::FlipV:
1396 // Flip the framebuffer vertically 1396 // Flip the framebuffer vertically
1397 left = texcoords.right; 1397 left = texcoords.right;
1398 right = texcoords.left; 1398 right = texcoords.left;
@@ -1406,8 +1406,9 @@ void VKBlitScreen::SetVertexData(BufferData& data, const Tegra::FramebufferConfi
1406 UNIMPLEMENTED_IF(framebuffer_crop_rect.top != 0); 1406 UNIMPLEMENTED_IF(framebuffer_crop_rect.top != 0);
1407 UNIMPLEMENTED_IF(framebuffer_crop_rect.left != 0); 1407 UNIMPLEMENTED_IF(framebuffer_crop_rect.left != 0);
1408 1408
1409 f32 scale_u = 1.0f; 1409 f32 scale_u = static_cast<f32>(framebuffer.width) / static_cast<f32>(screen_info.width);
1410 f32 scale_v = 1.0f; 1410 f32 scale_v = static_cast<f32>(framebuffer.height) / static_cast<f32>(screen_info.height);
1411
1411 // Scale the output by the crop width/height. This is commonly used with 1280x720 rendering 1412 // Scale the output by the crop width/height. This is commonly used with 1280x720 rendering
1412 // (e.g. handheld mode) on a 1920x1080 framebuffer. 1413 // (e.g. handheld mode) on a 1920x1080 framebuffer.
1413 if (!fsr) { 1414 if (!fsr) {
diff --git a/src/video_core/renderer_vulkan/vk_buffer_cache.cpp b/src/video_core/renderer_vulkan/vk_buffer_cache.cpp
index 5ffd93499..def838c34 100644
--- a/src/video_core/renderer_vulkan/vk_buffer_cache.cpp
+++ b/src/video_core/renderer_vulkan/vk_buffer_cache.cpp
@@ -141,6 +141,18 @@ StagingBufferRef BufferCacheRuntime::DownloadStagingBuffer(size_t size) {
141 return staging_pool.Request(size, MemoryUsage::Download); 141 return staging_pool.Request(size, MemoryUsage::Download);
142} 142}
143 143
144u64 BufferCacheRuntime::GetDeviceLocalMemory() const {
145 return device.GetDeviceLocalMemory();
146}
147
148u64 BufferCacheRuntime::GetDeviceMemoryUsage() const {
149 return device.GetDeviceMemoryUsage();
150}
151
152bool BufferCacheRuntime::CanReportMemoryUsage() const {
153 return device.CanReportMemoryUsage();
154}
155
144void BufferCacheRuntime::Finish() { 156void BufferCacheRuntime::Finish() {
145 scheduler.Finish(); 157 scheduler.Finish();
146} 158}
diff --git a/src/video_core/renderer_vulkan/vk_buffer_cache.h b/src/video_core/renderer_vulkan/vk_buffer_cache.h
index 1ee0d8420..d7fdd18ff 100644
--- a/src/video_core/renderer_vulkan/vk_buffer_cache.h
+++ b/src/video_core/renderer_vulkan/vk_buffer_cache.h
@@ -65,6 +65,12 @@ public:
65 65
66 void Finish(); 66 void Finish();
67 67
68 u64 GetDeviceLocalMemory() const;
69
70 u64 GetDeviceMemoryUsage() const;
71
72 bool CanReportMemoryUsage() const;
73
68 [[nodiscard]] StagingBufferRef UploadStagingBuffer(size_t size); 74 [[nodiscard]] StagingBufferRef UploadStagingBuffer(size_t size);
69 75
70 [[nodiscard]] StagingBufferRef DownloadStagingBuffer(size_t size); 76 [[nodiscard]] StagingBufferRef DownloadStagingBuffer(size_t size);
diff --git a/src/video_core/renderer_vulkan/vk_compute_pipeline.cpp b/src/video_core/renderer_vulkan/vk_compute_pipeline.cpp
index de36bcdb7..97b3594c2 100644
--- a/src/video_core/renderer_vulkan/vk_compute_pipeline.cpp
+++ b/src/video_core/renderer_vulkan/vk_compute_pipeline.cpp
@@ -77,7 +77,7 @@ ComputePipeline::ComputePipeline(const Device& device_, DescriptorPool& descript
77 if (pipeline_statistics) { 77 if (pipeline_statistics) {
78 pipeline_statistics->Collect(*pipeline); 78 pipeline_statistics->Collect(*pipeline);
79 } 79 }
80 std::lock_guard lock{build_mutex}; 80 std::scoped_lock lock{build_mutex};
81 is_built = true; 81 is_built = true;
82 build_condvar.notify_one(); 82 build_condvar.notify_one();
83 if (shader_notify) { 83 if (shader_notify) {
diff --git a/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp b/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp
index d514b71d0..8959d6059 100644
--- a/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp
+++ b/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp
@@ -258,7 +258,7 @@ GraphicsPipeline::GraphicsPipeline(
258 pipeline_statistics->Collect(*pipeline); 258 pipeline_statistics->Collect(*pipeline);
259 } 259 }
260 260
261 std::lock_guard lock{build_mutex}; 261 std::scoped_lock lock{build_mutex};
262 is_built = true; 262 is_built = true;
263 build_condvar.notify_one(); 263 build_condvar.notify_one();
264 if (shader_notify) { 264 if (shader_notify) {
diff --git a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp
index 27e59df73..336d1e9dc 100644
--- a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp
+++ b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp
@@ -404,7 +404,7 @@ void PipelineCache::LoadDiskResources(u64 title_id, std::stop_token stop_loading
404 workers.QueueWork([this, key, env = std::move(env), &state, &callback]() mutable { 404 workers.QueueWork([this, key, env = std::move(env), &state, &callback]() mutable {
405 ShaderPools pools; 405 ShaderPools pools;
406 auto pipeline{CreateComputePipeline(pools, key, env, state.statistics.get(), false)}; 406 auto pipeline{CreateComputePipeline(pools, key, env, state.statistics.get(), false)};
407 std::lock_guard lock{state.mutex}; 407 std::scoped_lock lock{state.mutex};
408 if (pipeline) { 408 if (pipeline) {
409 compute_cache.emplace(key, std::move(pipeline)); 409 compute_cache.emplace(key, std::move(pipeline));
410 } 410 }
@@ -434,7 +434,7 @@ void PipelineCache::LoadDiskResources(u64 title_id, std::stop_token stop_loading
434 auto pipeline{CreateGraphicsPipeline(pools, key, MakeSpan(env_ptrs), 434 auto pipeline{CreateGraphicsPipeline(pools, key, MakeSpan(env_ptrs),
435 state.statistics.get(), false)}; 435 state.statistics.get(), false)};
436 436
437 std::lock_guard lock{state.mutex}; 437 std::scoped_lock lock{state.mutex};
438 graphics_cache.emplace(key, std::move(pipeline)); 438 graphics_cache.emplace(key, std::move(pipeline));
439 ++state.built; 439 ++state.built;
440 if (state.has_loaded) { 440 if (state.has_loaded) {
diff --git a/src/video_core/renderer_vulkan/vk_render_pass_cache.cpp b/src/video_core/renderer_vulkan/vk_render_pass_cache.cpp
index 451ffe019..d22bb6694 100644
--- a/src/video_core/renderer_vulkan/vk_render_pass_cache.cpp
+++ b/src/video_core/renderer_vulkan/vk_render_pass_cache.cpp
@@ -36,7 +36,7 @@ VkAttachmentDescription AttachmentDescription(const Device& device, PixelFormat
36RenderPassCache::RenderPassCache(const Device& device_) : device{&device_} {} 36RenderPassCache::RenderPassCache(const Device& device_) : device{&device_} {}
37 37
38VkRenderPass RenderPassCache::Get(const RenderPassKey& key) { 38VkRenderPass RenderPassCache::Get(const RenderPassKey& key) {
39 std::lock_guard lock{mutex}; 39 std::scoped_lock lock{mutex};
40 const auto [pair, is_new] = cache.try_emplace(key); 40 const auto [pair, is_new] = cache.try_emplace(key);
41 if (!is_new) { 41 if (!is_new) {
42 return *pair->second; 42 return *pair->second;
diff --git a/src/video_core/renderer_vulkan/vk_scheduler.cpp b/src/video_core/renderer_vulkan/vk_scheduler.cpp
index ad320991b..6a9416457 100644
--- a/src/video_core/renderer_vulkan/vk_scheduler.cpp
+++ b/src/video_core/renderer_vulkan/vk_scheduler.cpp
@@ -73,7 +73,7 @@ void VKScheduler::DispatchWork() {
73 return; 73 return;
74 } 74 }
75 { 75 {
76 std::lock_guard lock{work_mutex}; 76 std::scoped_lock lock{work_mutex};
77 work_queue.push(std::move(chunk)); 77 work_queue.push(std::move(chunk));
78 } 78 }
79 work_cv.notify_one(); 79 work_cv.notify_one();
@@ -157,7 +157,7 @@ void VKScheduler::WorkerThread(std::stop_token stop_token) {
157 if (has_submit) { 157 if (has_submit) {
158 AllocateWorkerCommandBuffer(); 158 AllocateWorkerCommandBuffer();
159 } 159 }
160 std::lock_guard reserve_lock{reserve_mutex}; 160 std::scoped_lock reserve_lock{reserve_mutex};
161 chunk_reserve.push_back(std::move(work)); 161 chunk_reserve.push_back(std::move(work));
162 } while (!stop_token.stop_requested()); 162 } while (!stop_token.stop_requested());
163} 163}
@@ -282,7 +282,7 @@ void VKScheduler::EndRenderPass() {
282} 282}
283 283
284void VKScheduler::AcquireNewChunk() { 284void VKScheduler::AcquireNewChunk() {
285 std::lock_guard lock{reserve_mutex}; 285 std::scoped_lock lock{reserve_mutex};
286 if (chunk_reserve.empty()) { 286 if (chunk_reserve.empty()) {
287 chunk = std::make_unique<CommandChunk>(); 287 chunk = std::make_unique<CommandChunk>();
288 return; 288 return;
diff --git a/src/video_core/renderer_vulkan/vk_staging_buffer_pool.cpp b/src/video_core/renderer_vulkan/vk_staging_buffer_pool.cpp
index 5d5329abf..64a58304b 100644
--- a/src/video_core/renderer_vulkan/vk_staging_buffer_pool.cpp
+++ b/src/video_core/renderer_vulkan/vk_staging_buffer_pool.cpp
@@ -118,7 +118,7 @@ StagingBufferPool::StagingBufferPool(const Device& device_, MemoryAllocator& mem
118 .image = nullptr, 118 .image = nullptr,
119 .buffer = *stream_buffer, 119 .buffer = *stream_buffer,
120 }; 120 };
121 const auto memory_properties = device.GetPhysical().GetMemoryProperties(); 121 const auto memory_properties = device.GetPhysical().GetMemoryProperties().memoryProperties;
122 VkMemoryAllocateInfo stream_memory_info{ 122 VkMemoryAllocateInfo stream_memory_info{
123 .sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO, 123 .sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO,
124 .pNext = make_dedicated ? &dedicated_info : nullptr, 124 .pNext = make_dedicated ? &dedicated_info : nullptr,
diff --git a/src/video_core/renderer_vulkan/vk_texture_cache.cpp b/src/video_core/renderer_vulkan/vk_texture_cache.cpp
index 83a23b66a..49691ce0c 100644
--- a/src/video_core/renderer_vulkan/vk_texture_cache.cpp
+++ b/src/video_core/renderer_vulkan/vk_texture_cache.cpp
@@ -438,6 +438,32 @@ constexpr VkBorderColor ConvertBorderColor(const std::array<float, 4>& color) {
438 } 438 }
439} 439}
440 440
441[[nodiscard]] SwizzleSource SwapGreenRed(SwizzleSource value) {
442 switch (value) {
443 case SwizzleSource::R:
444 return SwizzleSource::G;
445 case SwizzleSource::G:
446 return SwizzleSource::R;
447 default:
448 return value;
449 }
450}
451
452[[nodiscard]] SwizzleSource SwapSpecial(SwizzleSource value) {
453 switch (value) {
454 case SwizzleSource::A:
455 return SwizzleSource::R;
456 case SwizzleSource::R:
457 return SwizzleSource::A;
458 case SwizzleSource::G:
459 return SwizzleSource::B;
460 case SwizzleSource::B:
461 return SwizzleSource::G;
462 default:
463 return value;
464 }
465}
466
441void CopyBufferToImage(vk::CommandBuffer cmdbuf, VkBuffer src_buffer, VkImage image, 467void CopyBufferToImage(vk::CommandBuffer cmdbuf, VkBuffer src_buffer, VkImage image,
442 VkImageAspectFlags aspect_mask, bool is_initialized, 468 VkImageAspectFlags aspect_mask, bool is_initialized,
443 std::span<const VkBufferImageCopy> copies) { 469 std::span<const VkBufferImageCopy> copies) {
@@ -554,14 +580,25 @@ void CopyBufferToImage(vk::CommandBuffer cmdbuf, VkBuffer src_buffer, VkImage im
554 }; 580 };
555} 581}
556 582
557[[nodiscard]] bool IsFormatFlipped(PixelFormat format, bool emulate_bgr565) { 583void TryTransformSwizzleIfNeeded(PixelFormat format, std::array<SwizzleSource, 4>& swizzle,
584 bool emulate_bgr565) {
558 switch (format) { 585 switch (format) {
559 case PixelFormat::A1B5G5R5_UNORM: 586 case PixelFormat::A1B5G5R5_UNORM:
560 return true; 587 std::ranges::transform(swizzle, swizzle.begin(), SwapBlueRed);
588 break;
561 case PixelFormat::B5G6R5_UNORM: 589 case PixelFormat::B5G6R5_UNORM:
562 return emulate_bgr565; 590 if (emulate_bgr565) {
591 std::ranges::transform(swizzle, swizzle.begin(), SwapBlueRed);
592 }
593 break;
594 case PixelFormat::A5B5G5R1_UNORM:
595 std::ranges::transform(swizzle, swizzle.begin(), SwapSpecial);
596 break;
597 case PixelFormat::R4G4_UNORM:
598 std::ranges::transform(swizzle, swizzle.begin(), SwapGreenRed);
599 break;
563 default: 600 default:
564 return false; 601 break;
565 } 602 }
566} 603}
567 604
@@ -1189,6 +1226,14 @@ u64 TextureCacheRuntime::GetDeviceLocalMemory() const {
1189 return device.GetDeviceLocalMemory(); 1226 return device.GetDeviceLocalMemory();
1190} 1227}
1191 1228
1229u64 TextureCacheRuntime::GetDeviceMemoryUsage() const {
1230 return device.GetDeviceMemoryUsage();
1231}
1232
1233bool TextureCacheRuntime::CanReportMemoryUsage() const {
1234 return device.CanReportMemoryUsage();
1235}
1236
1192void TextureCacheRuntime::TickFrame() {} 1237void TextureCacheRuntime::TickFrame() {}
1193 1238
1194Image::Image(TextureCacheRuntime& runtime_, const ImageInfo& info_, GPUVAddr gpu_addr_, 1239Image::Image(TextureCacheRuntime& runtime_, const ImageInfo& info_, GPUVAddr gpu_addr_,
@@ -1203,6 +1248,7 @@ Image::Image(TextureCacheRuntime& runtime_, const ImageInfo& info_, GPUVAddr gpu
1203 } else { 1248 } else {
1204 flags |= VideoCommon::ImageFlagBits::Converted; 1249 flags |= VideoCommon::ImageFlagBits::Converted;
1205 } 1250 }
1251 flags |= VideoCommon::ImageFlagBits::CostlyLoad;
1206 } 1252 }
1207 if (runtime->device.HasDebuggingToolAttached()) { 1253 if (runtime->device.HasDebuggingToolAttached()) {
1208 original_image.SetObjectNameEXT(VideoCommon::Name(*this).c_str()); 1254 original_image.SetObjectNameEXT(VideoCommon::Name(*this).c_str());
@@ -1442,8 +1488,7 @@ bool Image::BlitScaleHelper(bool scale_up) {
1442 1488
1443 runtime->blit_image_helper.BlitColor(blit_framebuffer.get(), color_view, dst_region, 1489 runtime->blit_image_helper.BlitColor(blit_framebuffer.get(), color_view, dst_region,
1444 src_region, operation, BLIT_OPERATION); 1490 src_region, operation, BLIT_OPERATION);
1445 } else if (!runtime->device.IsBlitDepthStencilSupported() && 1491 } else if (aspect_mask == (VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT)) {
1446 aspect_mask == (VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT)) {
1447 if (!blit_framebuffer) { 1492 if (!blit_framebuffer) {
1448 blit_framebuffer = std::make_unique<Framebuffer>(*runtime, nullptr, view_ptr, extent); 1493 blit_framebuffer = std::make_unique<Framebuffer>(*runtime, nullptr, view_ptr, extent);
1449 } 1494 }
@@ -1488,9 +1533,7 @@ ImageView::ImageView(TextureCacheRuntime& runtime, const VideoCommon::ImageViewI
1488 }; 1533 };
1489 if (!info.IsRenderTarget()) { 1534 if (!info.IsRenderTarget()) {
1490 swizzle = info.Swizzle(); 1535 swizzle = info.Swizzle();
1491 if (IsFormatFlipped(format, device->MustEmulateBGR565())) { 1536 TryTransformSwizzleIfNeeded(format, swizzle, device->MustEmulateBGR565());
1492 std::ranges::transform(swizzle, swizzle.begin(), SwapBlueRed);
1493 }
1494 if ((aspect_mask & (VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT)) != 0) { 1537 if ((aspect_mask & (VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT)) != 0) {
1495 std::ranges::transform(swizzle, swizzle.begin(), ConvertGreenRed); 1538 std::ranges::transform(swizzle, swizzle.begin(), ConvertGreenRed);
1496 } 1539 }
diff --git a/src/video_core/renderer_vulkan/vk_texture_cache.h b/src/video_core/renderer_vulkan/vk_texture_cache.h
index c81130dd2..cb15b4a1c 100644
--- a/src/video_core/renderer_vulkan/vk_texture_cache.h
+++ b/src/video_core/renderer_vulkan/vk_texture_cache.h
@@ -55,6 +55,10 @@ public:
55 55
56 u64 GetDeviceLocalMemory() const; 56 u64 GetDeviceLocalMemory() const;
57 57
58 u64 GetDeviceMemoryUsage() const;
59
60 bool CanReportMemoryUsage() const;
61
58 void BlitImage(Framebuffer* dst_framebuffer, ImageView& dst, ImageView& src, 62 void BlitImage(Framebuffer* dst_framebuffer, ImageView& dst, ImageView& src,
59 const Region2D& dst_region, const Region2D& src_region, 63 const Region2D& dst_region, const Region2D& src_region,
60 Tegra::Engines::Fermi2D::Filter filter, 64 Tegra::Engines::Fermi2D::Filter filter,
diff --git a/src/video_core/shader_cache.cpp b/src/video_core/shader_cache.cpp
index 87636857d..75031767a 100644
--- a/src/video_core/shader_cache.cpp
+++ b/src/video_core/shader_cache.cpp
@@ -25,7 +25,7 @@ void ShaderCache::InvalidateRegion(VAddr addr, size_t size) {
25} 25}
26 26
27void ShaderCache::OnCPUWrite(VAddr addr, size_t size) { 27void ShaderCache::OnCPUWrite(VAddr addr, size_t size) {
28 std::lock_guard lock{invalidation_mutex}; 28 std::scoped_lock lock{invalidation_mutex};
29 InvalidatePagesInRegion(addr, size); 29 InvalidatePagesInRegion(addr, size);
30} 30}
31 31
diff --git a/src/video_core/surface.cpp b/src/video_core/surface.cpp
index a36015c8c..5f428d35d 100644
--- a/src/video_core/surface.cpp
+++ b/src/video_core/surface.cpp
@@ -190,13 +190,13 @@ PixelFormat PixelFormatFromRenderTargetFormat(Tegra::RenderTargetFormat format)
190 } 190 }
191} 191}
192 192
193PixelFormat PixelFormatFromGPUPixelFormat(Tegra::FramebufferConfig::PixelFormat format) { 193PixelFormat PixelFormatFromGPUPixelFormat(Service::android::PixelFormat format) {
194 switch (format) { 194 switch (format) {
195 case Tegra::FramebufferConfig::PixelFormat::A8B8G8R8_UNORM: 195 case Service::android::PixelFormat::Rgba8888:
196 return PixelFormat::A8B8G8R8_UNORM; 196 return PixelFormat::A8B8G8R8_UNORM;
197 case Tegra::FramebufferConfig::PixelFormat::RGB565_UNORM: 197 case Service::android::PixelFormat::Rgb565:
198 return PixelFormat::R5G6B5_UNORM; 198 return PixelFormat::R5G6B5_UNORM;
199 case Tegra::FramebufferConfig::PixelFormat::B8G8R8A8_UNORM: 199 case Service::android::PixelFormat::Bgra8888:
200 return PixelFormat::B8G8R8A8_UNORM; 200 return PixelFormat::B8G8R8A8_UNORM;
201 default: 201 default:
202 UNIMPLEMENTED_MSG("Unimplemented format={}", format); 202 UNIMPLEMENTED_MSG("Unimplemented format={}", format);
diff --git a/src/video_core/surface.h b/src/video_core/surface.h
index 33e8d24ab..86fea61ae 100644
--- a/src/video_core/surface.h
+++ b/src/video_core/surface.h
@@ -25,6 +25,7 @@ enum class PixelFormat {
25 A2B10G10R10_UNORM, 25 A2B10G10R10_UNORM,
26 A2B10G10R10_UINT, 26 A2B10G10R10_UINT,
27 A1B5G5R5_UNORM, 27 A1B5G5R5_UNORM,
28 A5B5G5R1_UNORM,
28 R8_UNORM, 29 R8_UNORM,
29 R8_SNORM, 30 R8_SNORM,
30 R8_SINT, 31 R8_SINT,
@@ -82,6 +83,7 @@ enum class PixelFormat {
82 BC3_SRGB, 83 BC3_SRGB,
83 BC7_SRGB, 84 BC7_SRGB,
84 A4B4G4R4_UNORM, 85 A4B4G4R4_UNORM,
86 R4G4_UNORM,
85 ASTC_2D_4X4_SRGB, 87 ASTC_2D_4X4_SRGB,
86 ASTC_2D_8X8_SRGB, 88 ASTC_2D_8X8_SRGB,
87 ASTC_2D_8X5_SRGB, 89 ASTC_2D_8X5_SRGB,
@@ -156,6 +158,7 @@ constexpr std::array<u32, MaxPixelFormat> BLOCK_WIDTH_TABLE = {{
156 1, // A2B10G10R10_UNORM 158 1, // A2B10G10R10_UNORM
157 1, // A2B10G10R10_UINT 159 1, // A2B10G10R10_UINT
158 1, // A1B5G5R5_UNORM 160 1, // A1B5G5R5_UNORM
161 1, // A5B5G5R1_UNORM
159 1, // R8_UNORM 162 1, // R8_UNORM
160 1, // R8_SNORM 163 1, // R8_SNORM
161 1, // R8_SINT 164 1, // R8_SINT
@@ -213,6 +216,7 @@ constexpr std::array<u32, MaxPixelFormat> BLOCK_WIDTH_TABLE = {{
213 4, // BC3_SRGB 216 4, // BC3_SRGB
214 4, // BC7_SRGB 217 4, // BC7_SRGB
215 1, // A4B4G4R4_UNORM 218 1, // A4B4G4R4_UNORM
219 1, // R4G4_UNORM
216 4, // ASTC_2D_4X4_SRGB 220 4, // ASTC_2D_4X4_SRGB
217 8, // ASTC_2D_8X8_SRGB 221 8, // ASTC_2D_8X8_SRGB
218 8, // ASTC_2D_8X5_SRGB 222 8, // ASTC_2D_8X5_SRGB
@@ -256,6 +260,7 @@ constexpr std::array<u32, MaxPixelFormat> BLOCK_HEIGHT_TABLE = {{
256 1, // A2B10G10R10_UNORM 260 1, // A2B10G10R10_UNORM
257 1, // A2B10G10R10_UINT 261 1, // A2B10G10R10_UINT
258 1, // A1B5G5R5_UNORM 262 1, // A1B5G5R5_UNORM
263 1, // A5B5G5R1_UNORM
259 1, // R8_UNORM 264 1, // R8_UNORM
260 1, // R8_SNORM 265 1, // R8_SNORM
261 1, // R8_SINT 266 1, // R8_SINT
@@ -313,6 +318,7 @@ constexpr std::array<u32, MaxPixelFormat> BLOCK_HEIGHT_TABLE = {{
313 4, // BC3_SRGB 318 4, // BC3_SRGB
314 4, // BC7_SRGB 319 4, // BC7_SRGB
315 1, // A4B4G4R4_UNORM 320 1, // A4B4G4R4_UNORM
321 1, // R4G4_UNORM
316 4, // ASTC_2D_4X4_SRGB 322 4, // ASTC_2D_4X4_SRGB
317 8, // ASTC_2D_8X8_SRGB 323 8, // ASTC_2D_8X8_SRGB
318 5, // ASTC_2D_8X5_SRGB 324 5, // ASTC_2D_8X5_SRGB
@@ -356,6 +362,7 @@ constexpr std::array<u32, MaxPixelFormat> BITS_PER_BLOCK_TABLE = {{
356 32, // A2B10G10R10_UNORM 362 32, // A2B10G10R10_UNORM
357 32, // A2B10G10R10_UINT 363 32, // A2B10G10R10_UINT
358 16, // A1B5G5R5_UNORM 364 16, // A1B5G5R5_UNORM
365 16, // A5B5G5R1_UNORM
359 8, // R8_UNORM 366 8, // R8_UNORM
360 8, // R8_SNORM 367 8, // R8_SNORM
361 8, // R8_SINT 368 8, // R8_SINT
@@ -413,6 +420,7 @@ constexpr std::array<u32, MaxPixelFormat> BITS_PER_BLOCK_TABLE = {{
413 128, // BC3_SRGB 420 128, // BC3_SRGB
414 128, // BC7_UNORM 421 128, // BC7_UNORM
415 16, // A4B4G4R4_UNORM 422 16, // A4B4G4R4_UNORM
423 8, // R4G4_UNORM
416 128, // ASTC_2D_4X4_SRGB 424 128, // ASTC_2D_4X4_SRGB
417 128, // ASTC_2D_8X8_SRGB 425 128, // ASTC_2D_8X8_SRGB
418 128, // ASTC_2D_8X5_SRGB 426 128, // ASTC_2D_8X5_SRGB
@@ -460,7 +468,7 @@ PixelFormat PixelFormatFromDepthFormat(Tegra::DepthFormat format);
460 468
461PixelFormat PixelFormatFromRenderTargetFormat(Tegra::RenderTargetFormat format); 469PixelFormat PixelFormatFromRenderTargetFormat(Tegra::RenderTargetFormat format);
462 470
463PixelFormat PixelFormatFromGPUPixelFormat(Tegra::FramebufferConfig::PixelFormat format); 471PixelFormat PixelFormatFromGPUPixelFormat(Service::android::PixelFormat format);
464 472
465SurfaceType GetFormatType(PixelFormat pixel_format); 473SurfaceType GetFormatType(PixelFormat pixel_format);
466 474
diff --git a/src/video_core/texture_cache/format_lookup_table.cpp b/src/video_core/texture_cache/format_lookup_table.cpp
index afa807d5d..20e64a7c2 100644
--- a/src/video_core/texture_cache/format_lookup_table.cpp
+++ b/src/video_core/texture_cache/format_lookup_table.cpp
@@ -63,6 +63,10 @@ PixelFormat PixelFormatFromTextureInfo(TextureFormat format, ComponentType red,
63 return PixelFormat::A1B5G5R5_UNORM; 63 return PixelFormat::A1B5G5R5_UNORM;
64 case Hash(TextureFormat::A4B4G4R4, UNORM): 64 case Hash(TextureFormat::A4B4G4R4, UNORM):
65 return PixelFormat::A4B4G4R4_UNORM; 65 return PixelFormat::A4B4G4R4_UNORM;
66 case Hash(TextureFormat::G4R4, UNORM):
67 return PixelFormat::R4G4_UNORM;
68 case Hash(TextureFormat::A5B5G5R1, UNORM):
69 return PixelFormat::A5B5G5R1_UNORM;
66 case Hash(TextureFormat::R8, UNORM): 70 case Hash(TextureFormat::R8, UNORM):
67 return PixelFormat::R8_UNORM; 71 return PixelFormat::R8_UNORM;
68 case Hash(TextureFormat::R8, SNORM): 72 case Hash(TextureFormat::R8, SNORM):
@@ -143,6 +147,8 @@ PixelFormat PixelFormatFromTextureInfo(TextureFormat format, ComponentType red,
143 return PixelFormat::S8_UINT_D24_UNORM; 147 return PixelFormat::S8_UINT_D24_UNORM;
144 case Hash(TextureFormat::R8G24, UINT, UNORM, UNORM, UNORM, LINEAR): 148 case Hash(TextureFormat::R8G24, UINT, UNORM, UNORM, UNORM, LINEAR):
145 return PixelFormat::S8_UINT_D24_UNORM; 149 return PixelFormat::S8_UINT_D24_UNORM;
150 case Hash(TextureFormat::D24S8, UNORM, UINT, UINT, UINT, LINEAR):
151 return PixelFormat::D24_UNORM_S8_UINT;
146 case Hash(TextureFormat::D32S8, FLOAT, UINT, UNORM, UNORM, LINEAR): 152 case Hash(TextureFormat::D32S8, FLOAT, UINT, UNORM, UNORM, LINEAR):
147 return PixelFormat::D32_FLOAT_S8_UINT; 153 return PixelFormat::D32_FLOAT_S8_UINT;
148 case Hash(TextureFormat::BC1_RGBA, UNORM, LINEAR): 154 case Hash(TextureFormat::BC1_RGBA, UNORM, LINEAR):
diff --git a/src/video_core/texture_cache/formatter.h b/src/video_core/texture_cache/formatter.h
index b2c81057b..6f5afc5a9 100644
--- a/src/video_core/texture_cache/formatter.h
+++ b/src/video_core/texture_cache/formatter.h
@@ -38,6 +38,8 @@ struct fmt::formatter<VideoCore::Surface::PixelFormat> : fmt::formatter<fmt::str
38 return "A2B10G10R10_UINT"; 38 return "A2B10G10R10_UINT";
39 case PixelFormat::A1B5G5R5_UNORM: 39 case PixelFormat::A1B5G5R5_UNORM:
40 return "A1B5G5R5_UNORM"; 40 return "A1B5G5R5_UNORM";
41 case PixelFormat::A5B5G5R1_UNORM:
42 return "A5B5G5R1_UNORM";
41 case PixelFormat::R8_UNORM: 43 case PixelFormat::R8_UNORM:
42 return "R8_UNORM"; 44 return "R8_UNORM";
43 case PixelFormat::R8_SNORM: 45 case PixelFormat::R8_SNORM:
@@ -152,6 +154,8 @@ struct fmt::formatter<VideoCore::Surface::PixelFormat> : fmt::formatter<fmt::str
152 return "BC7_SRGB"; 154 return "BC7_SRGB";
153 case PixelFormat::A4B4G4R4_UNORM: 155 case PixelFormat::A4B4G4R4_UNORM:
154 return "A4B4G4R4_UNORM"; 156 return "A4B4G4R4_UNORM";
157 case PixelFormat::R4G4_UNORM:
158 return "R4G4_UNORM";
155 case PixelFormat::ASTC_2D_4X4_SRGB: 159 case PixelFormat::ASTC_2D_4X4_SRGB:
156 return "ASTC_2D_4X4_SRGB"; 160 return "ASTC_2D_4X4_SRGB";
157 case PixelFormat::ASTC_2D_8X8_SRGB: 161 case PixelFormat::ASTC_2D_8X8_SRGB:
diff --git a/src/video_core/texture_cache/image_base.h b/src/video_core/texture_cache/image_base.h
index 89c111c00..dd0106432 100644
--- a/src/video_core/texture_cache/image_base.h
+++ b/src/video_core/texture_cache/image_base.h
@@ -33,11 +33,12 @@ enum class ImageFlagBits : u32 {
33 ///< garbage collection priority 33 ///< garbage collection priority
34 Alias = 1 << 11, ///< This image has aliases and has priority on garbage 34 Alias = 1 << 11, ///< This image has aliases and has priority on garbage
35 ///< collection 35 ///< collection
36 CostlyLoad = 1 << 12, ///< Protected from low-tier GC as it is costly to load back.
36 37
37 // Rescaler 38 // Rescaler
38 Rescaled = 1 << 12, 39 Rescaled = 1 << 13,
39 CheckingRescalable = 1 << 13, 40 CheckingRescalable = 1 << 14,
40 IsRescalable = 1 << 14, 41 IsRescalable = 1 << 15,
41}; 42};
42DECLARE_ENUM_FLAG_OPERATORS(ImageFlagBits) 43DECLARE_ENUM_FLAG_OPERATORS(ImageFlagBits)
43 44
diff --git a/src/video_core/texture_cache/texture_cache.h b/src/video_core/texture_cache/texture_cache.h
index 72eeb8bbd..8fef74117 100644
--- a/src/video_core/texture_cache/texture_cache.h
+++ b/src/video_core/texture_cache/texture_cache.h
@@ -50,14 +50,20 @@ TextureCache<P>::TextureCache(Runtime& runtime_, VideoCore::RasterizerInterface&
50 void(slot_samplers.insert(runtime, sampler_descriptor)); 50 void(slot_samplers.insert(runtime, sampler_descriptor));
51 51
52 if constexpr (HAS_DEVICE_MEMORY_INFO) { 52 if constexpr (HAS_DEVICE_MEMORY_INFO) {
53 const auto device_memory = runtime.GetDeviceLocalMemory(); 53 const s64 device_memory = static_cast<s64>(runtime.GetDeviceLocalMemory());
54 const u64 possible_expected_memory = (device_memory * 4) / 10; 54 const s64 min_spacing_expected = device_memory - 1_GiB - 512_MiB;
55 const u64 possible_critical_memory = (device_memory * 7) / 10; 55 const s64 min_spacing_critical = device_memory - 1_GiB;
56 expected_memory = std::max(possible_expected_memory, DEFAULT_EXPECTED_MEMORY - 256_MiB); 56 const s64 mem_threshold = std::min(device_memory, TARGET_THRESHOLD);
57 critical_memory = std::max(possible_critical_memory, DEFAULT_CRITICAL_MEMORY - 512_MiB); 57 const s64 min_vacancy_expected = (6 * mem_threshold) / 10;
58 minimum_memory = 0; 58 const s64 min_vacancy_critical = (3 * mem_threshold) / 10;
59 expected_memory = static_cast<u64>(
60 std::max(std::min(device_memory - min_vacancy_expected, min_spacing_expected),
61 DEFAULT_EXPECTED_MEMORY));
62 critical_memory = static_cast<u64>(
63 std::max(std::min(device_memory - min_vacancy_critical, min_spacing_critical),
64 DEFAULT_CRITICAL_MEMORY));
65 minimum_memory = static_cast<u64>((device_memory - mem_threshold) / 2);
59 } else { 66 } else {
60 // On OpenGL we can be more conservatives as the driver takes care.
61 expected_memory = DEFAULT_EXPECTED_MEMORY + 512_MiB; 67 expected_memory = DEFAULT_EXPECTED_MEMORY + 512_MiB;
62 critical_memory = DEFAULT_CRITICAL_MEMORY + 1_GiB; 68 critical_memory = DEFAULT_CRITICAL_MEMORY + 1_GiB;
63 minimum_memory = 0; 69 minimum_memory = 0;
@@ -66,18 +72,21 @@ TextureCache<P>::TextureCache(Runtime& runtime_, VideoCore::RasterizerInterface&
66 72
67template <class P> 73template <class P>
68void TextureCache<P>::RunGarbageCollector() { 74void TextureCache<P>::RunGarbageCollector() {
69 const bool high_priority_mode = total_used_memory >= expected_memory; 75 bool high_priority_mode = total_used_memory >= expected_memory;
70 const bool aggressive_mode = total_used_memory >= critical_memory; 76 bool aggressive_mode = total_used_memory >= critical_memory;
71 const u64 ticks_to_destroy = aggressive_mode ? 10ULL : high_priority_mode ? 25ULL : 100ULL; 77 const u64 ticks_to_destroy = aggressive_mode ? 10ULL : high_priority_mode ? 25ULL : 50ULL;
72 size_t num_iterations = aggressive_mode ? 300 : (high_priority_mode ? 50 : 10); 78 size_t num_iterations = aggressive_mode ? 40 : (high_priority_mode ? 20 : 10);
73 const auto clean_up = [this, &num_iterations, high_priority_mode](ImageId image_id) { 79 const auto clean_up = [this, &num_iterations, &high_priority_mode,
80 &aggressive_mode](ImageId image_id) {
74 if (num_iterations == 0) { 81 if (num_iterations == 0) {
75 return true; 82 return true;
76 } 83 }
77 --num_iterations; 84 --num_iterations;
78 auto& image = slot_images[image_id]; 85 auto& image = slot_images[image_id];
79 const bool must_download = image.IsSafeDownload(); 86 const bool must_download =
80 if (!high_priority_mode && must_download) { 87 image.IsSafeDownload() && False(image.flags & ImageFlagBits::BadOverlap);
88 if (!high_priority_mode &&
89 (must_download || True(image.flags & ImageFlagBits::CostlyLoad))) {
81 return false; 90 return false;
82 } 91 }
83 if (must_download) { 92 if (must_download) {
@@ -92,6 +101,18 @@ void TextureCache<P>::RunGarbageCollector() {
92 } 101 }
93 UnregisterImage(image_id); 102 UnregisterImage(image_id);
94 DeleteImage(image_id, image.scale_tick > frame_tick + 5); 103 DeleteImage(image_id, image.scale_tick > frame_tick + 5);
104 if (total_used_memory < critical_memory) {
105 if (aggressive_mode) {
106 // Sink the aggresiveness.
107 num_iterations >>= 2;
108 aggressive_mode = false;
109 return false;
110 }
111 if (high_priority_mode && total_used_memory < expected_memory) {
112 num_iterations >>= 1;
113 high_priority_mode = false;
114 }
115 }
95 return false; 116 return false;
96 }; 117 };
97 lru_cache.ForEachItemBelow(frame_tick - ticks_to_destroy, clean_up); 118 lru_cache.ForEachItemBelow(frame_tick - ticks_to_destroy, clean_up);
@@ -99,6 +120,10 @@ void TextureCache<P>::RunGarbageCollector() {
99 120
100template <class P> 121template <class P>
101void TextureCache<P>::TickFrame() { 122void TextureCache<P>::TickFrame() {
123 // If we can obtain the memory info, use it instead of the estimate.
124 if (runtime.CanReportMemoryUsage()) {
125 total_used_memory = runtime.GetDeviceMemoryUsage();
126 }
102 if (total_used_memory > minimum_memory) { 127 if (total_used_memory > minimum_memory) {
103 RunGarbageCollector(); 128 RunGarbageCollector();
104 } 129 }
@@ -106,6 +131,7 @@ void TextureCache<P>::TickFrame() {
106 sentenced_framebuffers.Tick(); 131 sentenced_framebuffers.Tick();
107 sentenced_image_view.Tick(); 132 sentenced_image_view.Tick();
108 runtime.TickFrame(); 133 runtime.TickFrame();
134 critical_gc = 0;
109 ++frame_tick; 135 ++frame_tick;
110} 136}
111 137
@@ -1052,6 +1078,9 @@ ImageId TextureCache<P>::JoinImages(const ImageInfo& info, GPUVAddr gpu_addr, VA
1052 1078
1053 for (const ImageId overlap_id : overlap_ids) { 1079 for (const ImageId overlap_id : overlap_ids) {
1054 Image& overlap = slot_images[overlap_id]; 1080 Image& overlap = slot_images[overlap_id];
1081 if (True(overlap.flags & ImageFlagBits::GpuModified)) {
1082 new_image.flags |= ImageFlagBits::GpuModified;
1083 }
1055 if (overlap.info.num_samples != new_image.info.num_samples) { 1084 if (overlap.info.num_samples != new_image.info.num_samples) {
1056 LOG_WARNING(HW_GPU, "Copying between images with different samples is not implemented"); 1085 LOG_WARNING(HW_GPU, "Copying between images with different samples is not implemented");
1057 } else { 1086 } else {
@@ -1414,6 +1443,10 @@ void TextureCache<P>::RegisterImage(ImageId image_id) {
1414 tentative_size = EstimatedDecompressedSize(tentative_size, image.info.format); 1443 tentative_size = EstimatedDecompressedSize(tentative_size, image.info.format);
1415 } 1444 }
1416 total_used_memory += Common::AlignUp(tentative_size, 1024); 1445 total_used_memory += Common::AlignUp(tentative_size, 1024);
1446 if (total_used_memory > critical_memory && critical_gc < GC_EMERGENCY_COUNTS) {
1447 RunGarbageCollector();
1448 critical_gc++;
1449 }
1417 image.lru_index = lru_cache.Insert(image_id, frame_tick); 1450 image.lru_index = lru_cache.Insert(image_id, frame_tick);
1418 1451
1419 ForEachGPUPage(image.gpu_addr, image.guest_size_bytes, 1452 ForEachGPUPage(image.gpu_addr, image.guest_size_bytes,
@@ -1704,6 +1737,9 @@ void TextureCache<P>::SynchronizeAliases(ImageId image_id) {
1704 most_recent_tick = std::max(most_recent_tick, aliased_image.modification_tick); 1737 most_recent_tick = std::max(most_recent_tick, aliased_image.modification_tick);
1705 aliased_images.push_back(&aliased); 1738 aliased_images.push_back(&aliased);
1706 any_rescaled |= True(aliased_image.flags & ImageFlagBits::Rescaled); 1739 any_rescaled |= True(aliased_image.flags & ImageFlagBits::Rescaled);
1740 if (True(aliased_image.flags & ImageFlagBits::GpuModified)) {
1741 image.flags |= ImageFlagBits::GpuModified;
1742 }
1707 } 1743 }
1708 } 1744 }
1709 if (aliased_images.empty()) { 1745 if (aliased_images.empty()) {
diff --git a/src/video_core/texture_cache/texture_cache_base.h b/src/video_core/texture_cache/texture_cache_base.h
index 647ca0730..b1324edf3 100644
--- a/src/video_core/texture_cache/texture_cache_base.h
+++ b/src/video_core/texture_cache/texture_cache_base.h
@@ -59,8 +59,10 @@ class TextureCache {
59 /// True when the API can provide info about the memory of the device. 59 /// True when the API can provide info about the memory of the device.
60 static constexpr bool HAS_DEVICE_MEMORY_INFO = P::HAS_DEVICE_MEMORY_INFO; 60 static constexpr bool HAS_DEVICE_MEMORY_INFO = P::HAS_DEVICE_MEMORY_INFO;
61 61
62 static constexpr u64 DEFAULT_EXPECTED_MEMORY = 1_GiB; 62 static constexpr s64 TARGET_THRESHOLD = 4_GiB;
63 static constexpr u64 DEFAULT_CRITICAL_MEMORY = 2_GiB; 63 static constexpr s64 DEFAULT_EXPECTED_MEMORY = 1_GiB + 125_MiB;
64 static constexpr s64 DEFAULT_CRITICAL_MEMORY = 1_GiB + 625_MiB;
65 static constexpr size_t GC_EMERGENCY_COUNTS = 2;
64 66
65 using Runtime = typename P::Runtime; 67 using Runtime = typename P::Runtime;
66 using Image = typename P::Image; 68 using Image = typename P::Image;
@@ -372,6 +374,7 @@ private:
372 u64 minimum_memory; 374 u64 minimum_memory;
373 u64 expected_memory; 375 u64 expected_memory;
374 u64 critical_memory; 376 u64 critical_memory;
377 size_t critical_gc;
375 378
376 SlotVector<Image> slot_images; 379 SlotVector<Image> slot_images;
377 SlotVector<ImageMapView> slot_map_views; 380 SlotVector<ImageMapView> slot_map_views;
diff --git a/src/video_core/vulkan_common/vulkan_device.cpp b/src/video_core/vulkan_common/vulkan_device.cpp
index 32c10d675..bd05a1f84 100644
--- a/src/video_core/vulkan_common/vulkan_device.cpp
+++ b/src/video_core/vulkan_common/vulkan_device.cpp
@@ -12,12 +12,14 @@
12#include <vector> 12#include <vector>
13 13
14#include "common/assert.h" 14#include "common/assert.h"
15#include "common/literals.h"
15#include "common/settings.h" 16#include "common/settings.h"
16#include "video_core/vulkan_common/nsight_aftermath_tracker.h" 17#include "video_core/vulkan_common/nsight_aftermath_tracker.h"
17#include "video_core/vulkan_common/vulkan_device.h" 18#include "video_core/vulkan_common/vulkan_device.h"
18#include "video_core/vulkan_common/vulkan_wrapper.h" 19#include "video_core/vulkan_common/vulkan_wrapper.h"
19 20
20namespace Vulkan { 21namespace Vulkan {
22using namespace Common::Literals;
21namespace { 23namespace {
22namespace Alternatives { 24namespace Alternatives {
23constexpr std::array STENCIL8_UINT{ 25constexpr std::array STENCIL8_UINT{
@@ -43,6 +45,12 @@ constexpr std::array B5G6R5_UNORM_PACK16{
43 VK_FORMAT_R5G6B5_UNORM_PACK16, 45 VK_FORMAT_R5G6B5_UNORM_PACK16,
44 VK_FORMAT_UNDEFINED, 46 VK_FORMAT_UNDEFINED,
45}; 47};
48
49constexpr std::array R4G4_UNORM_PACK8{
50 VK_FORMAT_R8_UNORM,
51 VK_FORMAT_UNDEFINED,
52};
53
46} // namespace Alternatives 54} // namespace Alternatives
47 55
48enum class NvidiaArchitecture { 56enum class NvidiaArchitecture {
@@ -93,6 +101,8 @@ constexpr const VkFormat* GetFormatAlternatives(VkFormat format) {
93 return Alternatives::DEPTH16_UNORM_STENCIL8_UINT.data(); 101 return Alternatives::DEPTH16_UNORM_STENCIL8_UINT.data();
94 case VK_FORMAT_B5G6R5_UNORM_PACK16: 102 case VK_FORMAT_B5G6R5_UNORM_PACK16:
95 return Alternatives::B5G6R5_UNORM_PACK16.data(); 103 return Alternatives::B5G6R5_UNORM_PACK16.data();
104 case VK_FORMAT_R4G4_UNORM_PACK8:
105 return Alternatives::R4G4_UNORM_PACK8.data();
96 default: 106 default:
97 return nullptr; 107 return nullptr;
98 } 108 }
@@ -120,6 +130,8 @@ std::unordered_map<VkFormat, VkFormatProperties> GetFormatProperties(vk::Physica
120 VK_FORMAT_A8B8G8R8_SRGB_PACK32, 130 VK_FORMAT_A8B8G8R8_SRGB_PACK32,
121 VK_FORMAT_R5G6B5_UNORM_PACK16, 131 VK_FORMAT_R5G6B5_UNORM_PACK16,
122 VK_FORMAT_B5G6R5_UNORM_PACK16, 132 VK_FORMAT_B5G6R5_UNORM_PACK16,
133 VK_FORMAT_R5G5B5A1_UNORM_PACK16,
134 VK_FORMAT_B5G5R5A1_UNORM_PACK16,
123 VK_FORMAT_A2B10G10R10_UNORM_PACK32, 135 VK_FORMAT_A2B10G10R10_UNORM_PACK32,
124 VK_FORMAT_A2B10G10R10_UINT_PACK32, 136 VK_FORMAT_A2B10G10R10_UINT_PACK32,
125 VK_FORMAT_A1R5G5B5_UNORM_PACK16, 137 VK_FORMAT_A1R5G5B5_UNORM_PACK16,
@@ -158,7 +170,9 @@ std::unordered_map<VkFormat, VkFormatProperties> GetFormatProperties(vk::Physica
158 VK_FORMAT_R16G16B16A16_SFLOAT, 170 VK_FORMAT_R16G16B16A16_SFLOAT,
159 VK_FORMAT_B8G8R8A8_UNORM, 171 VK_FORMAT_B8G8R8A8_UNORM,
160 VK_FORMAT_B8G8R8A8_SRGB, 172 VK_FORMAT_B8G8R8A8_SRGB,
173 VK_FORMAT_R4G4_UNORM_PACK8,
161 VK_FORMAT_R4G4B4A4_UNORM_PACK16, 174 VK_FORMAT_R4G4B4A4_UNORM_PACK16,
175 VK_FORMAT_B4G4R4A4_UNORM_PACK16,
162 VK_FORMAT_D32_SFLOAT, 176 VK_FORMAT_D32_SFLOAT,
163 VK_FORMAT_D16_UNORM, 177 VK_FORMAT_D16_UNORM,
164 VK_FORMAT_S8_UINT, 178 VK_FORMAT_S8_UINT,
@@ -596,6 +610,11 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR
596 } 610 }
597 logical = vk::Device::Create(physical, queue_cis, extensions, first_next, dld); 611 logical = vk::Device::Create(physical, queue_cis, extensions, first_next, dld);
598 612
613 is_integrated = properties.deviceType == VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU;
614 is_virtual = properties.deviceType == VK_PHYSICAL_DEVICE_TYPE_VIRTUAL_GPU;
615 is_non_gpu = properties.deviceType == VK_PHYSICAL_DEVICE_TYPE_OTHER ||
616 properties.deviceType == VK_PHYSICAL_DEVICE_TYPE_CPU;
617
599 CollectPhysicalMemoryInfo(); 618 CollectPhysicalMemoryInfo();
600 CollectTelemetryParameters(); 619 CollectTelemetryParameters();
601 CollectToolingInfo(); 620 CollectToolingInfo();
@@ -614,6 +633,11 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR
614 khr_push_descriptor = false; 633 khr_push_descriptor = false;
615 break; 634 break;
616 } 635 }
636 const u32 nv_major_version = (properties.driverVersion >> 22) & 0x3ff;
637 if (nv_major_version >= 510) {
638 LOG_WARNING(Render_Vulkan, "NVIDIA Drivers >= 510 do not support MSAA image blits");
639 cant_blit_msaa = true;
640 }
617 } 641 }
618 const bool is_radv = driver_id == VK_DRIVER_ID_MESA_RADV; 642 const bool is_radv = driver_id == VK_DRIVER_ID_MESA_RADV;
619 if (ext_extended_dynamic_state && is_radv) { 643 if (ext_extended_dynamic_state && is_radv) {
@@ -724,7 +748,7 @@ VkFormat Device::GetSupportedFormat(VkFormat wanted_format, VkFormatFeatureFlags
724} 748}
725 749
726void Device::ReportLoss() const { 750void Device::ReportLoss() const {
727 LOG_CRITICAL(Render_Vulkan, "Device loss occured!"); 751 LOG_CRITICAL(Render_Vulkan, "Device loss occurred!");
728 752
729 // Wait for the log to flush and for Nsight Aftermath to dump the results 753 // Wait for the log to flush and for Nsight Aftermath to dump the results
730 std::this_thread::sleep_for(std::chrono::seconds{15}); 754 std::this_thread::sleep_for(std::chrono::seconds{15});
@@ -985,6 +1009,7 @@ std::vector<const char*> Device::LoadExtensions(bool requires_surface) {
985 test(has_khr_swapchain_mutable_format, VK_KHR_SWAPCHAIN_MUTABLE_FORMAT_EXTENSION_NAME, 1009 test(has_khr_swapchain_mutable_format, VK_KHR_SWAPCHAIN_MUTABLE_FORMAT_EXTENSION_NAME,
986 false); 1010 false);
987 test(has_ext_line_rasterization, VK_EXT_LINE_RASTERIZATION_EXTENSION_NAME, false); 1011 test(has_ext_line_rasterization, VK_EXT_LINE_RASTERIZATION_EXTENSION_NAME, false);
1012 test(ext_memory_budget, VK_EXT_MEMORY_BUDGET_EXTENSION_NAME, true);
988 if (Settings::values.enable_nsight_aftermath) { 1013 if (Settings::values.enable_nsight_aftermath) {
989 test(nv_device_diagnostics_config, VK_NV_DEVICE_DIAGNOSTICS_CONFIG_EXTENSION_NAME, 1014 test(nv_device_diagnostics_config, VK_NV_DEVICE_DIAGNOSTICS_CONFIG_EXTENSION_NAME,
990 true); 1015 true);
@@ -997,7 +1022,7 @@ std::vector<const char*> Device::LoadExtensions(bool requires_surface) {
997 VkPhysicalDeviceFeatures2KHR features{}; 1022 VkPhysicalDeviceFeatures2KHR features{};
998 features.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2_KHR; 1023 features.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2_KHR;
999 1024
1000 VkPhysicalDeviceProperties2KHR physical_properties; 1025 VkPhysicalDeviceProperties2KHR physical_properties{};
1001 physical_properties.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2_KHR; 1026 physical_properties.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2_KHR;
1002 1027
1003 if (has_khr_shader_float16_int8) { 1028 if (has_khr_shader_float16_int8) {
@@ -1267,15 +1292,50 @@ void Device::CollectTelemetryParameters() {
1267 vendor_name = driver.driverName; 1292 vendor_name = driver.driverName;
1268} 1293}
1269 1294
1295u64 Device::GetDeviceMemoryUsage() const {
1296 VkPhysicalDeviceMemoryBudgetPropertiesEXT budget;
1297 budget.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MEMORY_BUDGET_PROPERTIES_EXT;
1298 budget.pNext = nullptr;
1299 physical.GetMemoryProperties(&budget);
1300 u64 result{};
1301 for (const size_t heap : valid_heap_memory) {
1302 result += budget.heapUsage[heap];
1303 }
1304 return result;
1305}
1306
1270void Device::CollectPhysicalMemoryInfo() { 1307void Device::CollectPhysicalMemoryInfo() {
1271 const auto mem_properties = physical.GetMemoryProperties(); 1308 VkPhysicalDeviceMemoryBudgetPropertiesEXT budget{};
1309 budget.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MEMORY_BUDGET_PROPERTIES_EXT;
1310 const auto mem_info = physical.GetMemoryProperties(ext_memory_budget ? &budget : nullptr);
1311 const auto& mem_properties = mem_info.memoryProperties;
1272 const size_t num_properties = mem_properties.memoryHeapCount; 1312 const size_t num_properties = mem_properties.memoryHeapCount;
1273 device_access_memory = 0; 1313 device_access_memory = 0;
1314 u64 device_initial_usage = 0;
1315 u64 local_memory = 0;
1274 for (size_t element = 0; element < num_properties; ++element) { 1316 for (size_t element = 0; element < num_properties; ++element) {
1275 if ((mem_properties.memoryHeaps[element].flags & VK_MEMORY_HEAP_DEVICE_LOCAL_BIT) != 0) { 1317 const bool is_heap_local =
1276 device_access_memory += mem_properties.memoryHeaps[element].size; 1318 (mem_properties.memoryHeaps[element].flags & VK_MEMORY_HEAP_DEVICE_LOCAL_BIT) != 0;
1319 if (!is_integrated && !is_heap_local) {
1320 continue;
1277 } 1321 }
1322 valid_heap_memory.push_back(element);
1323 if (is_heap_local) {
1324 local_memory += mem_properties.memoryHeaps[element].size;
1325 }
1326 if (ext_memory_budget) {
1327 device_initial_usage += budget.heapUsage[element];
1328 device_access_memory += budget.heapBudget[element];
1329 continue;
1330 }
1331 device_access_memory += mem_properties.memoryHeaps[element].size;
1332 }
1333 if (!is_integrated) {
1334 return;
1278 } 1335 }
1336 const s64 available_memory = static_cast<s64>(device_access_memory - device_initial_usage);
1337 device_access_memory = static_cast<u64>(std::max<s64>(
1338 std::min<s64>(available_memory - 8_GiB, 4_GiB), static_cast<s64>(local_memory)));
1279} 1339}
1280 1340
1281void Device::CollectToolingInfo() { 1341void Device::CollectToolingInfo() {
diff --git a/src/video_core/vulkan_common/vulkan_device.h b/src/video_core/vulkan_common/vulkan_device.h
index 1c7c18bcf..2d709d069 100644
--- a/src/video_core/vulkan_common/vulkan_device.h
+++ b/src/video_core/vulkan_common/vulkan_device.h
@@ -341,6 +341,12 @@ public:
341 return device_access_memory; 341 return device_access_memory;
342 } 342 }
343 343
344 bool CanReportMemoryUsage() const {
345 return ext_memory_budget;
346 }
347
348 u64 GetDeviceMemoryUsage() const;
349
344 u32 GetSetsPerPool() const { 350 u32 GetSetsPerPool() const {
345 return sets_per_pool; 351 return sets_per_pool;
346 } 352 }
@@ -421,6 +427,9 @@ private:
421 bool is_topology_list_restart_supported{}; ///< Support for primitive restart with list 427 bool is_topology_list_restart_supported{}; ///< Support for primitive restart with list
422 ///< topologies. 428 ///< topologies.
423 bool is_patch_list_restart_supported{}; ///< Support for primitive restart with list patch. 429 bool is_patch_list_restart_supported{}; ///< Support for primitive restart with list patch.
430 bool is_integrated{}; ///< Is GPU an iGPU.
431 bool is_virtual{}; ///< Is GPU a virtual GPU.
432 bool is_non_gpu{}; ///< Is SoftwareRasterizer, FPGA, non-GPU device.
424 bool nv_viewport_swizzle{}; ///< Support for VK_NV_viewport_swizzle. 433 bool nv_viewport_swizzle{}; ///< Support for VK_NV_viewport_swizzle.
425 bool nv_viewport_array2{}; ///< Support for VK_NV_viewport_array2. 434 bool nv_viewport_array2{}; ///< Support for VK_NV_viewport_array2.
426 bool nv_geometry_shader_passthrough{}; ///< Support for VK_NV_geometry_shader_passthrough. 435 bool nv_geometry_shader_passthrough{}; ///< Support for VK_NV_geometry_shader_passthrough.
@@ -445,6 +454,7 @@ private:
445 bool ext_shader_atomic_int64{}; ///< Support for VK_KHR_shader_atomic_int64. 454 bool ext_shader_atomic_int64{}; ///< Support for VK_KHR_shader_atomic_int64.
446 bool ext_conservative_rasterization{}; ///< Support for VK_EXT_conservative_rasterization. 455 bool ext_conservative_rasterization{}; ///< Support for VK_EXT_conservative_rasterization.
447 bool ext_provoking_vertex{}; ///< Support for VK_EXT_provoking_vertex. 456 bool ext_provoking_vertex{}; ///< Support for VK_EXT_provoking_vertex.
457 bool ext_memory_budget{}; ///< Support for VK_EXT_memory_budget.
448 bool nv_device_diagnostics_config{}; ///< Support for VK_NV_device_diagnostics_config. 458 bool nv_device_diagnostics_config{}; ///< Support for VK_NV_device_diagnostics_config.
449 bool has_broken_cube_compatibility{}; ///< Has broken cube compatiblity bit 459 bool has_broken_cube_compatibility{}; ///< Has broken cube compatiblity bit
450 bool has_renderdoc{}; ///< Has RenderDoc attached 460 bool has_renderdoc{}; ///< Has RenderDoc attached
@@ -456,6 +466,7 @@ private:
456 // Telemetry parameters 466 // Telemetry parameters
457 std::string vendor_name; ///< Device's driver name. 467 std::string vendor_name; ///< Device's driver name.
458 std::vector<std::string> supported_extensions; ///< Reported Vulkan extensions. 468 std::vector<std::string> supported_extensions; ///< Reported Vulkan extensions.
469 std::vector<size_t> valid_heap_memory; ///< Heaps used.
459 470
460 /// Format properties dictionary. 471 /// Format properties dictionary.
461 std::unordered_map<VkFormat, VkFormatProperties> format_properties; 472 std::unordered_map<VkFormat, VkFormatProperties> format_properties;
diff --git a/src/video_core/vulkan_common/vulkan_memory_allocator.cpp b/src/video_core/vulkan_common/vulkan_memory_allocator.cpp
index 300a61205..e6e97b332 100644
--- a/src/video_core/vulkan_common/vulkan_memory_allocator.cpp
+++ b/src/video_core/vulkan_common/vulkan_memory_allocator.cpp
@@ -227,7 +227,7 @@ void MemoryCommit::Release() {
227} 227}
228 228
229MemoryAllocator::MemoryAllocator(const Device& device_, bool export_allocations_) 229MemoryAllocator::MemoryAllocator(const Device& device_, bool export_allocations_)
230 : device{device_}, properties{device_.GetPhysical().GetMemoryProperties()}, 230 : device{device_}, properties{device_.GetPhysical().GetMemoryProperties().memoryProperties},
231 export_allocations{export_allocations_}, 231 export_allocations{export_allocations_},
232 buffer_image_granularity{ 232 buffer_image_granularity{
233 device_.GetPhysical().GetProperties().limits.bufferImageGranularity} {} 233 device_.GetPhysical().GetProperties().limits.bufferImageGranularity} {}
diff --git a/src/video_core/vulkan_common/vulkan_wrapper.cpp b/src/video_core/vulkan_common/vulkan_wrapper.cpp
index a794f16dd..742cc39da 100644
--- a/src/video_core/vulkan_common/vulkan_wrapper.cpp
+++ b/src/video_core/vulkan_common/vulkan_wrapper.cpp
@@ -237,8 +237,8 @@ bool Load(VkInstance instance, InstanceDispatch& dld) noexcept {
237 return X(vkCreateDevice) && X(vkDestroyDevice) && X(vkDestroyDevice) && 237 return X(vkCreateDevice) && X(vkDestroyDevice) && X(vkDestroyDevice) &&
238 X(vkEnumerateDeviceExtensionProperties) && X(vkEnumeratePhysicalDevices) && 238 X(vkEnumerateDeviceExtensionProperties) && X(vkEnumeratePhysicalDevices) &&
239 X(vkGetDeviceProcAddr) && X(vkGetPhysicalDeviceFormatProperties) && 239 X(vkGetDeviceProcAddr) && X(vkGetPhysicalDeviceFormatProperties) &&
240 X(vkGetPhysicalDeviceMemoryProperties) && X(vkGetPhysicalDeviceProperties) && 240 X(vkGetPhysicalDeviceMemoryProperties) && X(vkGetPhysicalDeviceMemoryProperties2) &&
241 X(vkGetPhysicalDeviceQueueFamilyProperties); 241 X(vkGetPhysicalDeviceProperties) && X(vkGetPhysicalDeviceQueueFamilyProperties);
242#undef X 242#undef X
243} 243}
244 244
@@ -926,9 +926,12 @@ std::vector<VkPresentModeKHR> PhysicalDevice::GetSurfacePresentModesKHR(
926 return modes; 926 return modes;
927} 927}
928 928
929VkPhysicalDeviceMemoryProperties PhysicalDevice::GetMemoryProperties() const noexcept { 929VkPhysicalDeviceMemoryProperties2 PhysicalDevice::GetMemoryProperties(
930 VkPhysicalDeviceMemoryProperties properties; 930 void* next_structures) const noexcept {
931 dld->vkGetPhysicalDeviceMemoryProperties(physical_device, &properties); 931 VkPhysicalDeviceMemoryProperties2 properties{};
932 properties.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MEMORY_PROPERTIES_2;
933 properties.pNext = next_structures;
934 dld->vkGetPhysicalDeviceMemoryProperties2(physical_device, &properties);
932 return properties; 935 return properties;
933} 936}
934 937
diff --git a/src/video_core/vulkan_common/vulkan_wrapper.h b/src/video_core/vulkan_common/vulkan_wrapper.h
index 53bac627f..0a5f9931c 100644
--- a/src/video_core/vulkan_common/vulkan_wrapper.h
+++ b/src/video_core/vulkan_common/vulkan_wrapper.h
@@ -172,6 +172,7 @@ struct InstanceDispatch {
172 PFN_vkGetPhysicalDeviceFeatures2KHR vkGetPhysicalDeviceFeatures2KHR{}; 172 PFN_vkGetPhysicalDeviceFeatures2KHR vkGetPhysicalDeviceFeatures2KHR{};
173 PFN_vkGetPhysicalDeviceFormatProperties vkGetPhysicalDeviceFormatProperties{}; 173 PFN_vkGetPhysicalDeviceFormatProperties vkGetPhysicalDeviceFormatProperties{};
174 PFN_vkGetPhysicalDeviceMemoryProperties vkGetPhysicalDeviceMemoryProperties{}; 174 PFN_vkGetPhysicalDeviceMemoryProperties vkGetPhysicalDeviceMemoryProperties{};
175 PFN_vkGetPhysicalDeviceMemoryProperties2 vkGetPhysicalDeviceMemoryProperties2{};
175 PFN_vkGetPhysicalDeviceProperties vkGetPhysicalDeviceProperties{}; 176 PFN_vkGetPhysicalDeviceProperties vkGetPhysicalDeviceProperties{};
176 PFN_vkGetPhysicalDeviceProperties2KHR vkGetPhysicalDeviceProperties2KHR{}; 177 PFN_vkGetPhysicalDeviceProperties2KHR vkGetPhysicalDeviceProperties2KHR{};
177 PFN_vkGetPhysicalDeviceQueueFamilyProperties vkGetPhysicalDeviceQueueFamilyProperties{}; 178 PFN_vkGetPhysicalDeviceQueueFamilyProperties vkGetPhysicalDeviceQueueFamilyProperties{};
@@ -950,7 +951,8 @@ public:
950 951
951 std::vector<VkPresentModeKHR> GetSurfacePresentModesKHR(VkSurfaceKHR) const; 952 std::vector<VkPresentModeKHR> GetSurfacePresentModesKHR(VkSurfaceKHR) const;
952 953
953 VkPhysicalDeviceMemoryProperties GetMemoryProperties() const noexcept; 954 VkPhysicalDeviceMemoryProperties2 GetMemoryProperties(
955 void* next_structures = nullptr) const noexcept;
954 956
955private: 957private:
956 VkPhysicalDevice physical_device = nullptr; 958 VkPhysicalDevice physical_device = nullptr;
diff --git a/src/web_service/web_backend.cpp b/src/web_service/web_backend.cpp
index 4208bd044..58b0c2f10 100644
--- a/src/web_service/web_backend.cpp
+++ b/src/web_service/web_backend.cpp
@@ -32,7 +32,7 @@ constexpr std::size_t TIMEOUT_SECONDS = 30;
32struct Client::Impl { 32struct Client::Impl {
33 Impl(std::string host, std::string username, std::string token) 33 Impl(std::string host, std::string username, std::string token)
34 : host{std::move(host)}, username{std::move(username)}, token{std::move(token)} { 34 : host{std::move(host)}, username{std::move(username)}, token{std::move(token)} {
35 std::lock_guard lock{jwt_cache.mutex}; 35 std::scoped_lock lock{jwt_cache.mutex};
36 if (this->username == jwt_cache.username && this->token == jwt_cache.token) { 36 if (this->username == jwt_cache.username && this->token == jwt_cache.token) {
37 jwt = jwt_cache.jwt; 37 jwt = jwt_cache.jwt;
38 } 38 }
@@ -147,7 +147,7 @@ struct Client::Impl {
147 if (result.result_code != WebResult::Code::Success) { 147 if (result.result_code != WebResult::Code::Success) {
148 LOG_ERROR(WebService, "UpdateJWT failed"); 148 LOG_ERROR(WebService, "UpdateJWT failed");
149 } else { 149 } else {
150 std::lock_guard lock{jwt_cache.mutex}; 150 std::scoped_lock lock{jwt_cache.mutex};
151 jwt_cache.username = username; 151 jwt_cache.username = username;
152 jwt_cache.token = token; 152 jwt_cache.token = token;
153 jwt_cache.jwt = jwt = result.returned_data; 153 jwt_cache.jwt = jwt = result.returned_data;
diff --git a/src/yuzu/CMakeLists.txt b/src/yuzu/CMakeLists.txt
index 30902101d..2ee21f751 100644
--- a/src/yuzu/CMakeLists.txt
+++ b/src/yuzu/CMakeLists.txt
@@ -99,6 +99,9 @@ add_executable(yuzu
99 configuration/configure_profile_manager.cpp 99 configuration/configure_profile_manager.cpp
100 configuration/configure_profile_manager.h 100 configuration/configure_profile_manager.h
101 configuration/configure_profile_manager.ui 101 configuration/configure_profile_manager.ui
102 configuration/configure_ringcon.cpp
103 configuration/configure_ringcon.h
104 configuration/configure_ringcon.ui
102 configuration/configure_network.cpp 105 configuration/configure_network.cpp
103 configuration/configure_network.h 106 configuration/configure_network.h
104 configuration/configure_network.ui 107 configuration/configure_network.ui
@@ -293,7 +296,7 @@ if (YUZU_USE_QT_WEB_ENGINE)
293endif () 296endif ()
294 297
295if(UNIX AND NOT APPLE) 298if(UNIX AND NOT APPLE)
296 install(TARGETS yuzu RUNTIME DESTINATION "${CMAKE_INSTALL_PREFIX}/bin") 299 install(TARGETS yuzu)
297endif() 300endif()
298 301
299if (YUZU_USE_BUNDLED_QT) 302if (YUZU_USE_BUNDLED_QT)
diff --git a/src/yuzu/aboutdialog.ui b/src/yuzu/aboutdialog.ui
index 27d81cd13..2f7ddc7f3 100644
--- a/src/yuzu/aboutdialog.ui
+++ b/src/yuzu/aboutdialog.ui
@@ -87,7 +87,7 @@
87&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; 87&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;
88p, li { white-space: pre-wrap; } 88p, li { white-space: pre-wrap; }
89&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:'Ubuntu'; font-size:11pt; font-weight:400; font-style:normal;&quot;&gt; 89&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:'Ubuntu'; font-size:11pt; font-weight:400; font-style:normal;&quot;&gt;
90&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 GPLv2.0.&lt;/span&gt;&lt;/p&gt; 90&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;
91&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; 91&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;
92&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;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;</string> 92&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;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;</string>
93 </property> 93 </property>
diff --git a/src/yuzu/applets/qt_web_browser.cpp b/src/yuzu/applets/qt_web_browser.cpp
index 398d8f0b6..116ef54db 100644
--- a/src/yuzu/applets/qt_web_browser.cpp
+++ b/src/yuzu/applets/qt_web_browser.cpp
@@ -11,6 +11,9 @@
11#include <QWebEngineScriptCollection> 11#include <QWebEngineScriptCollection>
12#include <QWebEngineSettings> 12#include <QWebEngineSettings>
13#include <QWebEngineUrlScheme> 13#include <QWebEngineUrlScheme>
14
15#include "core/hid/input_interpreter.h"
16#include "yuzu/applets/qt_web_browser_scripts.h"
14#endif 17#endif
15 18
16#include "common/fs/path_util.h" 19#include "common/fs/path_util.h"
diff --git a/src/yuzu/applets/qt_web_browser.h b/src/yuzu/applets/qt_web_browser.h
index 79a08f509..a47059412 100644
--- a/src/yuzu/applets/qt_web_browser.h
+++ b/src/yuzu/applets/qt_web_browser.h
@@ -5,6 +5,7 @@
5#pragma once 5#pragma once
6 6
7#include <atomic> 7#include <atomic>
8#include <thread>
8 9
9#include <QObject> 10#include <QObject>
10 11
diff --git a/src/yuzu/bootmanager.cpp b/src/yuzu/bootmanager.cpp
index 27b0300d2..a1b819ae0 100644
--- a/src/yuzu/bootmanager.cpp
+++ b/src/yuzu/bootmanager.cpp
@@ -933,6 +933,12 @@ void GRenderWindow::CaptureScreenshot(const QString& screenshot_path) {
933 auto& renderer = system.Renderer(); 933 auto& renderer = system.Renderer();
934 const f32 res_scale = Settings::values.resolution_info.up_factor; 934 const f32 res_scale = Settings::values.resolution_info.up_factor;
935 935
936 if (renderer.IsScreenshotPending()) {
937 LOG_WARNING(Render,
938 "A screenshot is already requested or in progress, ignoring the request");
939 return;
940 }
941
936 const Layout::FramebufferLayout layout{Layout::FrameLayoutFromResolutionScale(res_scale)}; 942 const Layout::FramebufferLayout layout{Layout::FrameLayoutFromResolutionScale(res_scale)};
937 screenshot_image = QImage(QSize(layout.width, layout.height), QImage::Format_RGB32); 943 screenshot_image = QImage(QSize(layout.width, layout.height), QImage::Format_RGB32);
938 renderer.RequestScreenshot( 944 renderer.RequestScreenshot(
diff --git a/src/yuzu/compatdb.ui b/src/yuzu/compatdb.ui
index fed402176..3ca55eda6 100644
--- a/src/yuzu/compatdb.ui
+++ b/src/yuzu/compatdb.ui
@@ -86,7 +86,7 @@
86 <item row="4" column="0"> 86 <item row="4" column="0">
87 <widget class="QRadioButton" name="radioButton_Great"> 87 <widget class="QRadioButton" name="radioButton_Great">
88 <property name="text"> 88 <property name="text">
89 <string>Great </string> 89 <string>Great</string>
90 </property> 90 </property>
91 </widget> 91 </widget>
92 </item> 92 </item>
diff --git a/src/yuzu/configuration/config.cpp b/src/yuzu/configuration/config.cpp
index 4b943c6ba..ac26b885b 100644
--- a/src/yuzu/configuration/config.cpp
+++ b/src/yuzu/configuration/config.cpp
@@ -60,6 +60,11 @@ const std::array<int, 2> Config::default_stick_mod = {
60 0, 60 0,
61}; 61};
62 62
63const std::array<int, 2> Config::default_ringcon_analogs{{
64 Qt::Key_A,
65 Qt::Key_D,
66}};
67
63// This shouldn't have anything except static initializers (no functions). So 68// This shouldn't have anything except static initializers (no functions). So
64// QKeySequence(...).toString() is NOT ALLOWED HERE. 69// QKeySequence(...).toString() is NOT ALLOWED HERE.
65// This must be in alphabetical order according to action name as it must have the same order as 70// This must be in alphabetical order according to action name as it must have the same order as
@@ -346,6 +351,23 @@ void Config::ReadTouchscreenValues() {
346 ReadSetting(QStringLiteral("touchscreen_diameter_y"), 15).toUInt(); 351 ReadSetting(QStringLiteral("touchscreen_diameter_y"), 15).toUInt();
347} 352}
348 353
354void Config::ReadHidbusValues() {
355 Settings::values.enable_ring_controller =
356 ReadSetting(QStringLiteral("enable_ring_controller"), true).toBool();
357
358 const std::string default_param = InputCommon::GenerateAnalogParamFromKeys(
359 0, 0, default_ringcon_analogs[0], default_ringcon_analogs[1], 0, 0.05f);
360 auto& ringcon_analogs = Settings::values.ringcon_analogs;
361
362 ringcon_analogs =
363 qt_config->value(QStringLiteral("ring_controller"), QString::fromStdString(default_param))
364 .toString()
365 .toStdString();
366 if (ringcon_analogs.empty()) {
367 ringcon_analogs = default_param;
368 }
369}
370
349void Config::ReadAudioValues() { 371void Config::ReadAudioValues() {
350 qt_config->beginGroup(QStringLiteral("Audio")); 372 qt_config->beginGroup(QStringLiteral("Audio"));
351 373
@@ -369,6 +391,7 @@ void Config::ReadControlValues() {
369 ReadMouseValues(); 391 ReadMouseValues();
370 ReadTouchscreenValues(); 392 ReadTouchscreenValues();
371 ReadMotionTouchValues(); 393 ReadMotionTouchValues();
394 ReadHidbusValues();
372 395
373#ifdef _WIN32 396#ifdef _WIN32
374 ReadBasicSetting(Settings::values.enable_raw_input); 397 ReadBasicSetting(Settings::values.enable_raw_input);
@@ -775,6 +798,7 @@ void Config::ReadUIValues() {
775 ReadBasicSetting(UISettings::values.pause_when_in_background); 798 ReadBasicSetting(UISettings::values.pause_when_in_background);
776 ReadBasicSetting(UISettings::values.mute_when_in_background); 799 ReadBasicSetting(UISettings::values.mute_when_in_background);
777 ReadBasicSetting(UISettings::values.hide_mouse); 800 ReadBasicSetting(UISettings::values.hide_mouse);
801 ReadBasicSetting(UISettings::values.disable_web_applet);
778 802
779 qt_config->endGroup(); 803 qt_config->endGroup();
780} 804}
@@ -961,6 +985,16 @@ void Config::SaveMotionTouchValues() {
961 qt_config->endArray(); 985 qt_config->endArray();
962} 986}
963 987
988void Config::SaveHidbusValues() {
989 WriteBasicSetting(Settings::values.enable_ring_controller);
990
991 const std::string default_param = InputCommon::GenerateAnalogParamFromKeys(
992 0, 0, default_ringcon_analogs[0], default_ringcon_analogs[1], 0, 0.05f);
993 WriteSetting(QStringLiteral("ring_controller"),
994 QString::fromStdString(Settings::values.ringcon_analogs),
995 QString::fromStdString(default_param));
996}
997
964void Config::SaveValues() { 998void Config::SaveValues() {
965 if (global) { 999 if (global) {
966 SaveControlValues(); 1000 SaveControlValues();
@@ -1001,6 +1035,7 @@ void Config::SaveControlValues() {
1001 SaveMouseValues(); 1035 SaveMouseValues();
1002 SaveTouchscreenValues(); 1036 SaveTouchscreenValues();
1003 SaveMotionTouchValues(); 1037 SaveMotionTouchValues();
1038 SaveHidbusValues();
1004 1039
1005 WriteGlobalSetting(Settings::values.use_docked_mode); 1040 WriteGlobalSetting(Settings::values.use_docked_mode);
1006 WriteGlobalSetting(Settings::values.vibration_enabled); 1041 WriteGlobalSetting(Settings::values.vibration_enabled);
@@ -1308,6 +1343,7 @@ void Config::SaveUIValues() {
1308 WriteBasicSetting(UISettings::values.pause_when_in_background); 1343 WriteBasicSetting(UISettings::values.pause_when_in_background);
1309 WriteBasicSetting(UISettings::values.mute_when_in_background); 1344 WriteBasicSetting(UISettings::values.mute_when_in_background);
1310 WriteBasicSetting(UISettings::values.hide_mouse); 1345 WriteBasicSetting(UISettings::values.hide_mouse);
1346 WriteBasicSetting(UISettings::values.disable_web_applet);
1311 1347
1312 qt_config->endGroup(); 1348 qt_config->endGroup();
1313} 1349}
diff --git a/src/yuzu/configuration/config.h b/src/yuzu/configuration/config.h
index ae3e36a11..f0ab6bdaa 100644
--- a/src/yuzu/configuration/config.h
+++ b/src/yuzu/configuration/config.h
@@ -42,6 +42,7 @@ public:
42 static const std::array<int, Settings::NativeMotion::NumMotions> default_motions; 42 static const std::array<int, Settings::NativeMotion::NumMotions> default_motions;
43 static const std::array<std::array<int, 4>, Settings::NativeAnalog::NumAnalogs> default_analogs; 43 static const std::array<std::array<int, 4>, Settings::NativeAnalog::NumAnalogs> default_analogs;
44 static const std::array<int, 2> default_stick_mod; 44 static const std::array<int, 2> default_stick_mod;
45 static const std::array<int, 2> default_ringcon_analogs;
45 static const std::array<int, Settings::NativeMouseButton::NumMouseButtons> 46 static const std::array<int, Settings::NativeMouseButton::NumMouseButtons>
46 default_mouse_buttons; 47 default_mouse_buttons;
47 static const std::array<int, Settings::NativeKeyboard::NumKeyboardKeys> default_keyboard_keys; 48 static const std::array<int, Settings::NativeKeyboard::NumKeyboardKeys> default_keyboard_keys;
@@ -66,6 +67,7 @@ private:
66 void ReadMouseValues(); 67 void ReadMouseValues();
67 void ReadTouchscreenValues(); 68 void ReadTouchscreenValues();
68 void ReadMotionTouchValues(); 69 void ReadMotionTouchValues();
70 void ReadHidbusValues();
69 71
70 // Read functions bases off the respective config section names. 72 // Read functions bases off the respective config section names.
71 void ReadAudioValues(); 73 void ReadAudioValues();
@@ -93,6 +95,7 @@ private:
93 void SaveMouseValues(); 95 void SaveMouseValues();
94 void SaveTouchscreenValues(); 96 void SaveTouchscreenValues();
95 void SaveMotionTouchValues(); 97 void SaveMotionTouchValues();
98 void SaveHidbusValues();
96 99
97 // Save functions based off the respective config section names. 100 // Save functions based off the respective config section names.
98 void SaveAudioValues(); 101 void SaveAudioValues();
diff --git a/src/yuzu/configuration/configure_cpu.ui b/src/yuzu/configuration/configure_cpu.ui
index 5d80a8c91..8ae569ee6 100644
--- a/src/yuzu/configuration/configure_cpu.ui
+++ b/src/yuzu/configuration/configure_cpu.ui
@@ -52,6 +52,11 @@
52 <string>Unsafe</string> 52 <string>Unsafe</string>
53 </property> 53 </property>
54 </item> 54 </item>
55 <item>
56 <property name="text">
57 <string>Paranoid (disables most optimizations)</string>
58 </property>
59 </item>
55 </widget> 60 </widget>
56 </item> 61 </item>
57 </layout> 62 </layout>
diff --git a/src/yuzu/configuration/configure_debug.cpp b/src/yuzu/configuration/configure_debug.cpp
index c1cf4050c..bd50f7a68 100644
--- a/src/yuzu/configuration/configure_debug.cpp
+++ b/src/yuzu/configuration/configure_debug.cpp
@@ -59,6 +59,13 @@ void ConfigureDebug::SetConfiguration() {
59 ui->disable_loop_safety_checks->setChecked( 59 ui->disable_loop_safety_checks->setChecked(
60 Settings::values.disable_shader_loop_safety_checks.GetValue()); 60 Settings::values.disable_shader_loop_safety_checks.GetValue());
61 ui->extended_logging->setChecked(Settings::values.extended_logging.GetValue()); 61 ui->extended_logging->setChecked(Settings::values.extended_logging.GetValue());
62
63#ifdef YUZU_USE_QT_WEB_ENGINE
64 ui->disable_web_applet->setChecked(UISettings::values.disable_web_applet.GetValue());
65#else
66 ui->disable_web_applet->setEnabled(false);
67 ui->disable_web_applet->setText(QString::fromUtf8("Web applet not compiled"));
68#endif
62} 69}
63 70
64void ConfigureDebug::ApplyConfiguration() { 71void ConfigureDebug::ApplyConfiguration() {
@@ -80,6 +87,7 @@ void ConfigureDebug::ApplyConfiguration() {
80 ui->disable_loop_safety_checks->isChecked(); 87 ui->disable_loop_safety_checks->isChecked();
81 Settings::values.disable_macro_jit = ui->disable_macro_jit->isChecked(); 88 Settings::values.disable_macro_jit = ui->disable_macro_jit->isChecked();
82 Settings::values.extended_logging = ui->extended_logging->isChecked(); 89 Settings::values.extended_logging = ui->extended_logging->isChecked();
90 UISettings::values.disable_web_applet = ui->disable_web_applet->isChecked();
83 Debugger::ToggleConsole(); 91 Debugger::ToggleConsole();
84 Common::Log::Filter filter; 92 Common::Log::Filter filter;
85 filter.ParseFilterString(Settings::values.log_filter.GetValue()); 93 filter.ParseFilterString(Settings::values.log_filter.GetValue());
diff --git a/src/yuzu/configuration/configure_debug.ui b/src/yuzu/configuration/configure_debug.ui
index 4dd870855..c1d90d588 100644
--- a/src/yuzu/configuration/configure_debug.ui
+++ b/src/yuzu/configuration/configure_debug.ui
@@ -8,49 +8,49 @@
8 <property name="title"> 8 <property name="title">
9 <string>Logging</string> 9 <string>Logging</string>
10 </property> 10 </property>
11 <layout class="QGridLayout" name="gridLayout_1"> 11 <layout class="QGridLayout" name="gridLayout_1">
12 <item row="0" column="0" colspan="2"> 12 <item row="0" column="0" colspan="2">
13 <layout class="QHBoxLayout" name="horizontalLayout_1"> 13 <layout class="QHBoxLayout" name="horizontalLayout_1">
14 <item> 14 <item>
15 <widget class="QLabel" name="label_1"> 15 <widget class="QLabel" name="label_1">
16 <property name="text">
17 <string>Global Log Filter</string>
18 </property>
19 </widget>
20 </item>
21 <item>
22 <widget class="QLineEdit" name="log_filter_edit"/>
23 </item>
24 </layout>
25 </item>
26 <item row="1" column="0">
27 <widget class="QCheckBox" name="toggle_console">
28 <property name="text">
29 <string>Show Log in Console</string>
30 </property>
31 </widget>
32 </item>
33 <item row="1" column="1">
34 <widget class="QPushButton" name="open_log_button">
35 <property name="text">
36 <string>Open Log Location</string>
37 </property>
38 </widget>
39 </item>
40 <item row="2" column="0">
41 <widget class="QCheckBox" name="extended_logging">
42 <property name="enabled">
43 <bool>true</bool>
44 </property>
45 <property name="toolTip">
46 <string>When checked, the max size of the log increases from 100 MB to 1 GB</string>
47 </property>
48 <property name="text"> 16 <property name="text">
49 <string>Enable Extended Logging**</string> 17 <string>Global Log Filter</string>
50 </property> 18 </property>
51 </widget> 19 </widget>
52 </item> 20 </item>
53 </layout> 21 <item>
22 <widget class="QLineEdit" name="log_filter_edit"/>
23 </item>
24 </layout>
25 </item>
26 <item row="1" column="0">
27 <widget class="QCheckBox" name="toggle_console">
28 <property name="text">
29 <string>Show Log in Console</string>
30 </property>
31 </widget>
32 </item>
33 <item row="1" column="1">
34 <widget class="QPushButton" name="open_log_button">
35 <property name="text">
36 <string>Open Log Location</string>
37 </property>
38 </widget>
39 </item>
40 <item row="2" column="0">
41 <widget class="QCheckBox" name="extended_logging">
42 <property name="enabled">
43 <bool>true</bool>
44 </property>
45 <property name="toolTip">
46 <string>When checked, the max size of the log increases from 100 MB to 1 GB</string>
47 </property>
48 <property name="text">
49 <string>Enable Extended Logging**</string>
50 </property>
51 </widget>
52 </item>
53 </layout>
54 </widget> 54 </widget>
55 </item> 55 </item>
56 <item> 56 <item>
@@ -183,7 +183,7 @@
183 <string>Advanced</string> 183 <string>Advanced</string>
184 </property> 184 </property>
185 <layout class="QGridLayout" name="gridLayout_4"> 185 <layout class="QGridLayout" name="gridLayout_4">
186 <item> row="0" column="0"> 186 <item row="0" column="0">
187 <widget class="QCheckBox" name="quest_flag"> 187 <widget class="QCheckBox" name="quest_flag">
188 <property name="text"> 188 <property name="text">
189 <string>Kiosk (Quest) Mode</string> 189 <string>Kiosk (Quest) Mode</string>
@@ -214,7 +214,14 @@
214 <item row="1" column="1"> 214 <item row="1" column="1">
215 <widget class="QCheckBox" name="enable_all_controllers"> 215 <widget class="QCheckBox" name="enable_all_controllers">
216 <property name="text"> 216 <property name="text">
217 <string>Enable all Controller Types</string> 217 <string>Enable All Controller Types</string>
218 </property>
219 </widget>
220 </item>
221 <item row="2" column="1">
222 <widget class="QCheckBox" name="disable_web_applet">
223 <property name="text">
224 <string>Disable Web Applet</string>
218 </property> 225 </property>
219 </widget> 226 </widget>
220 </item> 227 </item>
diff --git a/src/yuzu/configuration/configure_dialog.cpp b/src/yuzu/configuration/configure_dialog.cpp
index 69458a3ff..b415a1cc4 100644
--- a/src/yuzu/configuration/configure_dialog.cpp
+++ b/src/yuzu/configuration/configure_dialog.cpp
@@ -25,6 +25,7 @@
25#include "yuzu/configuration/configure_ui.h" 25#include "yuzu/configuration/configure_ui.h"
26#include "yuzu/configuration/configure_web.h" 26#include "yuzu/configuration/configure_web.h"
27#include "yuzu/hotkeys.h" 27#include "yuzu/hotkeys.h"
28#include "yuzu/uisettings.h"
28 29
29ConfigureDialog::ConfigureDialog(QWidget* parent, HotkeyRegistry& registry, 30ConfigureDialog::ConfigureDialog(QWidget* parent, HotkeyRegistry& registry,
30 InputCommon::InputSubsystem* input_subsystem, 31 InputCommon::InputSubsystem* input_subsystem,
@@ -169,6 +170,8 @@ void ConfigureDialog::PopulateSelectionList() {
169 170
170void ConfigureDialog::OnLanguageChanged(const QString& locale) { 171void ConfigureDialog::OnLanguageChanged(const QString& locale) {
171 emit LanguageChanged(locale); 172 emit LanguageChanged(locale);
173 // Reloading the game list is needed to force retranslation.
174 UISettings::values.is_game_list_reload_pending = true;
172 // first apply the configuration, and then restore the display 175 // first apply the configuration, and then restore the display
173 ApplyConfiguration(); 176 ApplyConfiguration();
174 RetranslateUI(); 177 RetranslateUI();
diff --git a/src/yuzu/configuration/configure_hotkeys.cpp b/src/yuzu/configuration/configure_hotkeys.cpp
index 53e629a5e..6679e9c53 100644
--- a/src/yuzu/configuration/configure_hotkeys.cpp
+++ b/src/yuzu/configuration/configure_hotkeys.cpp
@@ -35,8 +35,9 @@ ConfigureHotkeys::ConfigureHotkeys(Core::HID::HIDCore& hid_core, QWidget* parent
35 ui->hotkey_list->setContextMenuPolicy(Qt::CustomContextMenu); 35 ui->hotkey_list->setContextMenuPolicy(Qt::CustomContextMenu);
36 ui->hotkey_list->setModel(model); 36 ui->hotkey_list->setModel(model);
37 37
38 ui->hotkey_list->setColumnWidth(name_column, 200); 38 ui->hotkey_list->header()->setStretchLastSection(false);
39 ui->hotkey_list->resizeColumnToContents(hotkey_column); 39 ui->hotkey_list->header()->setSectionResizeMode(name_column, QHeaderView::ResizeMode::Stretch);
40 ui->hotkey_list->header()->setMinimumSectionSize(150);
40 41
41 connect(ui->button_restore_defaults, &QPushButton::clicked, this, 42 connect(ui->button_restore_defaults, &QPushButton::clicked, this,
42 &ConfigureHotkeys::RestoreDefaults); 43 &ConfigureHotkeys::RestoreDefaults);
@@ -76,8 +77,8 @@ void ConfigureHotkeys::Populate(const HotkeyRegistry& registry) {
76 } 77 }
77 78
78 ui->hotkey_list->expandAll(); 79 ui->hotkey_list->expandAll();
79 ui->hotkey_list->resizeColumnToContents(name_column);
80 ui->hotkey_list->resizeColumnToContents(hotkey_column); 80 ui->hotkey_list->resizeColumnToContents(hotkey_column);
81 ui->hotkey_list->resizeColumnToContents(controller_column);
81} 82}
82 83
83void ConfigureHotkeys::changeEvent(QEvent* event) { 84void ConfigureHotkeys::changeEvent(QEvent* event) {
diff --git a/src/yuzu/configuration/configure_input.cpp b/src/yuzu/configuration/configure_input.cpp
index 4ca74a5f7..73d7ba24b 100644
--- a/src/yuzu/configuration/configure_input.cpp
+++ b/src/yuzu/configuration/configure_input.cpp
@@ -20,6 +20,7 @@
20#include "yuzu/configuration/configure_input_advanced.h" 20#include "yuzu/configuration/configure_input_advanced.h"
21#include "yuzu/configuration/configure_input_player.h" 21#include "yuzu/configuration/configure_input_player.h"
22#include "yuzu/configuration/configure_motion_touch.h" 22#include "yuzu/configuration/configure_motion_touch.h"
23#include "yuzu/configuration/configure_ringcon.h"
23#include "yuzu/configuration/configure_touchscreen_advanced.h" 24#include "yuzu/configuration/configure_touchscreen_advanced.h"
24#include "yuzu/configuration/configure_vibration.h" 25#include "yuzu/configuration/configure_vibration.h"
25#include "yuzu/configuration/input_profiles.h" 26#include "yuzu/configuration/input_profiles.h"
@@ -158,6 +159,10 @@ void ConfigureInput::Initialize(InputCommon::InputSubsystem* input_subsystem,
158 [this, input_subsystem] { 159 [this, input_subsystem] {
159 CallConfigureDialog<ConfigureMotionTouch>(*this, input_subsystem); 160 CallConfigureDialog<ConfigureMotionTouch>(*this, input_subsystem);
160 }); 161 });
162 connect(advanced, &ConfigureInputAdvanced::CallRingControllerDialog,
163 [this, input_subsystem, &hid_core] {
164 CallConfigureDialog<ConfigureRingController>(*this, input_subsystem, hid_core);
165 });
161 166
162 connect(ui->vibrationButton, &QPushButton::clicked, 167 connect(ui->vibrationButton, &QPushButton::clicked,
163 [this, &hid_core] { CallConfigureDialog<ConfigureVibration>(*this, hid_core); }); 168 [this, &hid_core] { CallConfigureDialog<ConfigureVibration>(*this, hid_core); });
diff --git a/src/yuzu/configuration/configure_input_advanced.cpp b/src/yuzu/configuration/configure_input_advanced.cpp
index 20fc2599d..8fd1f4a38 100644
--- a/src/yuzu/configuration/configure_input_advanced.cpp
+++ b/src/yuzu/configuration/configure_input_advanced.cpp
@@ -79,13 +79,17 @@ ConfigureInputAdvanced::ConfigureInputAdvanced(QWidget* parent)
79 &ConfigureInputAdvanced::UpdateUIEnabled); 79 &ConfigureInputAdvanced::UpdateUIEnabled);
80 connect(ui->touchscreen_enabled, &QCheckBox::stateChanged, this, 80 connect(ui->touchscreen_enabled, &QCheckBox::stateChanged, this,
81 &ConfigureInputAdvanced::UpdateUIEnabled); 81 &ConfigureInputAdvanced::UpdateUIEnabled);
82 connect(ui->enable_ring_controller, &QCheckBox::stateChanged, this,
83 &ConfigureInputAdvanced::UpdateUIEnabled);
82 84
83 connect(ui->debug_configure, &QPushButton::clicked, this, 85 connect(ui->debug_configure, &QPushButton::clicked, this,
84 [this] { CallDebugControllerDialog(); }); 86 [this] { CallDebugControllerDialog(); });
85 connect(ui->touchscreen_advanced, &QPushButton::clicked, this, 87 connect(ui->touchscreen_advanced, &QPushButton::clicked, this,
86 [this] { CallTouchscreenConfigDialog(); }); 88 [this] { CallTouchscreenConfigDialog(); });
87 connect(ui->buttonMotionTouch, &QPushButton::clicked, this, 89 connect(ui->buttonMotionTouch, &QPushButton::clicked, this,
88 &ConfigureInputAdvanced::CallMotionTouchConfigDialog); 90 [this] { CallMotionTouchConfigDialog(); });
91 connect(ui->ring_controller_configure, &QPushButton::clicked, this,
92 [this] { CallRingControllerDialog(); });
89 93
90#ifndef _WIN32 94#ifndef _WIN32
91 ui->enable_raw_input->setVisible(false); 95 ui->enable_raw_input->setVisible(false);
@@ -132,6 +136,7 @@ void ConfigureInputAdvanced::ApplyConfiguration() {
132 Settings::values.enable_raw_input = ui->enable_raw_input->isChecked(); 136 Settings::values.enable_raw_input = ui->enable_raw_input->isChecked();
133 Settings::values.enable_udp_controller = ui->enable_udp_controller->isChecked(); 137 Settings::values.enable_udp_controller = ui->enable_udp_controller->isChecked();
134 Settings::values.controller_navigation = ui->controller_navigation->isChecked(); 138 Settings::values.controller_navigation = ui->controller_navigation->isChecked();
139 Settings::values.enable_ring_controller = ui->enable_ring_controller->isChecked();
135} 140}
136 141
137void ConfigureInputAdvanced::LoadConfiguration() { 142void ConfigureInputAdvanced::LoadConfiguration() {
@@ -164,6 +169,7 @@ void ConfigureInputAdvanced::LoadConfiguration() {
164 ui->enable_raw_input->setChecked(Settings::values.enable_raw_input.GetValue()); 169 ui->enable_raw_input->setChecked(Settings::values.enable_raw_input.GetValue());
165 ui->enable_udp_controller->setChecked(Settings::values.enable_udp_controller.GetValue()); 170 ui->enable_udp_controller->setChecked(Settings::values.enable_udp_controller.GetValue());
166 ui->controller_navigation->setChecked(Settings::values.controller_navigation.GetValue()); 171 ui->controller_navigation->setChecked(Settings::values.controller_navigation.GetValue());
172 ui->enable_ring_controller->setChecked(Settings::values.enable_ring_controller.GetValue());
167 173
168 UpdateUIEnabled(); 174 UpdateUIEnabled();
169} 175}
@@ -185,4 +191,5 @@ void ConfigureInputAdvanced::UpdateUIEnabled() {
185 ui->touchscreen_advanced->setEnabled(ui->touchscreen_enabled->isChecked()); 191 ui->touchscreen_advanced->setEnabled(ui->touchscreen_enabled->isChecked());
186 ui->mouse_panning->setEnabled(!ui->mouse_enabled->isChecked()); 192 ui->mouse_panning->setEnabled(!ui->mouse_enabled->isChecked());
187 ui->mouse_panning_sensitivity->setEnabled(!ui->mouse_enabled->isChecked()); 193 ui->mouse_panning_sensitivity->setEnabled(!ui->mouse_enabled->isChecked());
194 ui->ring_controller_configure->setEnabled(ui->enable_ring_controller->isChecked());
188} 195}
diff --git a/src/yuzu/configuration/configure_input_advanced.h b/src/yuzu/configuration/configure_input_advanced.h
index 3083d55c1..4472cb846 100644
--- a/src/yuzu/configuration/configure_input_advanced.h
+++ b/src/yuzu/configuration/configure_input_advanced.h
@@ -29,6 +29,7 @@ signals:
29 void CallMouseConfigDialog(); 29 void CallMouseConfigDialog();
30 void CallTouchscreenConfigDialog(); 30 void CallTouchscreenConfigDialog();
31 void CallMotionTouchConfigDialog(); 31 void CallMotionTouchConfigDialog();
32 void CallRingControllerDialog();
32 33
33private: 34private:
34 void changeEvent(QEvent* event) override; 35 void changeEvent(QEvent* event) override;
diff --git a/src/yuzu/configuration/configure_input_advanced.ui b/src/yuzu/configuration/configure_input_advanced.ui
index 66f2075f2..14403cb10 100644
--- a/src/yuzu/configuration/configure_input_advanced.ui
+++ b/src/yuzu/configuration/configure_input_advanced.ui
@@ -2603,6 +2603,20 @@
2603 </property> 2603 </property>
2604 </widget> 2604 </widget>
2605 </item> 2605 </item>
2606 <item row="4" column="0">
2607 <widget class="QCheckBox" name="enable_ring_controller">
2608 <property name="text">
2609 <string>Ring Controller</string>
2610 </property>
2611 </widget>
2612 </item>
2613 <item row="4" column="2">
2614 <widget class="QPushButton" name="ring_controller_configure">
2615 <property name="text">
2616 <string>Configure</string>
2617 </property>
2618 </widget>
2619 </item>
2606 </layout> 2620 </layout>
2607 </widget> 2621 </widget>
2608 </item> 2622 </item>
diff --git a/src/yuzu/configuration/configure_input_player.cpp b/src/yuzu/configuration/configure_input_player.cpp
index ac0a9d7de..b4f5d172a 100644
--- a/src/yuzu/configuration/configure_input_player.cpp
+++ b/src/yuzu/configuration/configure_input_player.cpp
@@ -10,6 +10,7 @@
10#include <QMenu> 10#include <QMenu>
11#include <QMessageBox> 11#include <QMessageBox>
12#include <QTimer> 12#include <QTimer>
13#include "common/assert.h"
13#include "common/param_package.h" 14#include "common/param_package.h"
14#include "core/hid/emulated_controller.h" 15#include "core/hid/emulated_controller.h"
15#include "core/hid/hid_core.h" 16#include "core/hid/hid_core.h"
@@ -119,6 +120,23 @@ QString GetButtonName(Common::Input::ButtonNames button_name) {
119 } 120 }
120} 121}
121 122
123QString GetDirectionName(const std::string& direction) {
124 if (direction == "left") {
125 return QObject::tr("Left");
126 }
127 if (direction == "right") {
128 return QObject::tr("Right");
129 }
130 if (direction == "up") {
131 return QObject::tr("Up");
132 }
133 if (direction == "down") {
134 return QObject::tr("Down");
135 }
136 UNIMPLEMENTED_MSG("Unimplemented direction name={}", direction);
137 return QString::fromStdString(direction);
138}
139
122void SetAnalogParam(const Common::ParamPackage& input_param, Common::ParamPackage& analog_param, 140void SetAnalogParam(const Common::ParamPackage& input_param, Common::ParamPackage& analog_param,
123 const std::string& button_name) { 141 const std::string& button_name) {
124 // The poller returned a complete axis, so set all the buttons 142 // The poller returned a complete axis, so set all the buttons
@@ -162,7 +180,7 @@ QString ConfigureInputPlayer::ButtonToText(const Common::ParamPackage& param) {
162 180
163 if (common_button_name == Common::Input::ButtonNames::Value) { 181 if (common_button_name == Common::Input::ButtonNames::Value) {
164 if (param.Has("hat")) { 182 if (param.Has("hat")) {
165 const QString hat = QString::fromStdString(param.Get("direction", "")); 183 const QString hat = GetDirectionName(param.Get("direction", ""));
166 return QObject::tr("%1%2Hat %3").arg(toggle, inverted, hat); 184 return QObject::tr("%1%2Hat %3").arg(toggle, inverted, hat);
167 } 185 }
168 if (param.Has("axis")) { 186 if (param.Has("axis")) {
@@ -1426,10 +1444,10 @@ void ConfigureInputPlayer::mousePressEvent(QMouseEvent* event) {
1426} 1444}
1427 1445
1428void ConfigureInputPlayer::keyPressEvent(QKeyEvent* event) { 1446void ConfigureInputPlayer::keyPressEvent(QKeyEvent* event) {
1429 event->ignore();
1430 if (!input_setter || !event) { 1447 if (!input_setter || !event) {
1431 return; 1448 return;
1432 } 1449 }
1450 event->ignore();
1433 if (event->key() != Qt::Key_Escape) { 1451 if (event->key() != Qt::Key_Escape) {
1434 input_subsystem->GetKeyboard()->PressKey(event->key()); 1452 input_subsystem->GetKeyboard()->PressKey(event->key());
1435 } 1453 }
diff --git a/src/yuzu/configuration/configure_per_game_addons.cpp b/src/yuzu/configuration/configure_per_game_addons.cpp
index 21e51d749..7893a85bb 100644
--- a/src/yuzu/configuration/configure_per_game_addons.cpp
+++ b/src/yuzu/configuration/configure_per_game_addons.cpp
@@ -47,6 +47,10 @@ ConfigurePerGameAddons::ConfigurePerGameAddons(Core::System& system_, QWidget* p
47 item_model->setHeaderData(0, Qt::Horizontal, tr("Patch Name")); 47 item_model->setHeaderData(0, Qt::Horizontal, tr("Patch Name"));
48 item_model->setHeaderData(1, Qt::Horizontal, tr("Version")); 48 item_model->setHeaderData(1, Qt::Horizontal, tr("Version"));
49 49
50 tree_view->header()->setStretchLastSection(false);
51 tree_view->header()->setSectionResizeMode(0, QHeaderView::ResizeMode::Stretch);
52 tree_view->header()->setMinimumSectionSize(150);
53
50 // We must register all custom types with the Qt Automoc system so that we are able to use it 54 // We must register all custom types with the Qt Automoc system so that we are able to use it
51 // with signals/slots. In this case, QList falls under the umbrella of custom types. 55 // with signals/slots. In this case, QList falls under the umbrella of custom types.
52 qRegisterMetaType<QList<QStandardItem*>>("QList<QStandardItem*>"); 56 qRegisterMetaType<QList<QStandardItem*>>("QList<QStandardItem*>");
@@ -138,5 +142,5 @@ void ConfigurePerGameAddons::LoadConfiguration() {
138 item_model->appendRow(list_items.back()); 142 item_model->appendRow(list_items.back());
139 } 143 }
140 144
141 tree_view->setColumnWidth(0, 5 * tree_view->width() / 16); 145 tree_view->resizeColumnToContents(1);
142} 146}
diff --git a/src/yuzu/configuration/configure_ringcon.cpp b/src/yuzu/configuration/configure_ringcon.cpp
new file mode 100644
index 000000000..f1cdd4fed
--- /dev/null
+++ b/src/yuzu/configuration/configure_ringcon.cpp
@@ -0,0 +1,424 @@
1// Copyright 2022 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <memory>
6#include <QKeyEvent>
7#include <QMenu>
8#include <QTimer>
9
10#include "core/hid/emulated_devices.h"
11#include "core/hid/hid_core.h"
12#include "input_common/drivers/keyboard.h"
13#include "input_common/drivers/mouse.h"
14#include "input_common/main.h"
15#include "ui_configure_ringcon.h"
16#include "yuzu/bootmanager.h"
17#include "yuzu/configuration/config.h"
18#include "yuzu/configuration/configure_ringcon.h"
19
20const std::array<std::string, ConfigureRingController::ANALOG_SUB_BUTTONS_NUM>
21 ConfigureRingController::analog_sub_buttons{{
22 "left",
23 "right",
24 }};
25
26namespace {
27
28QString GetKeyName(int key_code) {
29 switch (key_code) {
30 case Qt::Key_Shift:
31 return QObject::tr("Shift");
32 case Qt::Key_Control:
33 return QObject::tr("Ctrl");
34 case Qt::Key_Alt:
35 return QObject::tr("Alt");
36 case Qt::Key_Meta:
37 return {};
38 default:
39 return QKeySequence(key_code).toString();
40 }
41}
42
43QString GetButtonName(Common::Input::ButtonNames button_name) {
44 switch (button_name) {
45 case Common::Input::ButtonNames::ButtonLeft:
46 return QObject::tr("Left");
47 case Common::Input::ButtonNames::ButtonRight:
48 return QObject::tr("Right");
49 case Common::Input::ButtonNames::ButtonDown:
50 return QObject::tr("Down");
51 case Common::Input::ButtonNames::ButtonUp:
52 return QObject::tr("Up");
53 case Common::Input::ButtonNames::TriggerZ:
54 return QObject::tr("Z");
55 case Common::Input::ButtonNames::TriggerR:
56 return QObject::tr("R");
57 case Common::Input::ButtonNames::TriggerL:
58 return QObject::tr("L");
59 case Common::Input::ButtonNames::ButtonA:
60 return QObject::tr("A");
61 case Common::Input::ButtonNames::ButtonB:
62 return QObject::tr("B");
63 case Common::Input::ButtonNames::ButtonX:
64 return QObject::tr("X");
65 case Common::Input::ButtonNames::ButtonY:
66 return QObject::tr("Y");
67 case Common::Input::ButtonNames::ButtonStart:
68 return QObject::tr("Start");
69 case Common::Input::ButtonNames::L1:
70 return QObject::tr("L1");
71 case Common::Input::ButtonNames::L2:
72 return QObject::tr("L2");
73 case Common::Input::ButtonNames::L3:
74 return QObject::tr("L3");
75 case Common::Input::ButtonNames::R1:
76 return QObject::tr("R1");
77 case Common::Input::ButtonNames::R2:
78 return QObject::tr("R2");
79 case Common::Input::ButtonNames::R3:
80 return QObject::tr("R3");
81 case Common::Input::ButtonNames::Circle:
82 return QObject::tr("Circle");
83 case Common::Input::ButtonNames::Cross:
84 return QObject::tr("Cross");
85 case Common::Input::ButtonNames::Square:
86 return QObject::tr("Square");
87 case Common::Input::ButtonNames::Triangle:
88 return QObject::tr("Triangle");
89 case Common::Input::ButtonNames::Share:
90 return QObject::tr("Share");
91 case Common::Input::ButtonNames::Options:
92 return QObject::tr("Options");
93 default:
94 return QObject::tr("[undefined]");
95 }
96}
97
98void SetAnalogParam(const Common::ParamPackage& input_param, Common::ParamPackage& analog_param,
99 const std::string& button_name) {
100 // The poller returned a complete axis, so set all the buttons
101 if (input_param.Has("axis_x") && input_param.Has("axis_y")) {
102 analog_param = input_param;
103 return;
104 }
105 // Check if the current configuration has either no engine or an axis binding.
106 // Clears out the old binding and adds one with analog_from_button.
107 if (!analog_param.Has("engine") || analog_param.Has("axis_x") || analog_param.Has("axis_y")) {
108 analog_param = {
109 {"engine", "analog_from_button"},
110 };
111 }
112 analog_param.Set(button_name, input_param.Serialize());
113}
114} // namespace
115
116ConfigureRingController::ConfigureRingController(QWidget* parent,
117 InputCommon::InputSubsystem* input_subsystem_,
118 Core::HID::HIDCore& hid_core_)
119 : QDialog(parent), timeout_timer(std::make_unique<QTimer>()),
120 poll_timer(std::make_unique<QTimer>()), input_subsystem{input_subsystem_},
121
122 ui(std::make_unique<Ui::ConfigureRingController>()) {
123 ui->setupUi(this);
124
125 analog_map_buttons = {
126 ui->buttonRingAnalogPull,
127 ui->buttonRingAnalogPush,
128 };
129
130 emulated_device = hid_core_.GetEmulatedDevices();
131 emulated_device->SaveCurrentConfig();
132 emulated_device->EnableConfiguration();
133
134 LoadConfiguration();
135
136 for (int sub_button_id = 0; sub_button_id < ANALOG_SUB_BUTTONS_NUM; ++sub_button_id) {
137 auto* const analog_button = analog_map_buttons[sub_button_id];
138
139 if (analog_button == nullptr) {
140 continue;
141 }
142
143 connect(analog_button, &QPushButton::clicked, [=, this] {
144 HandleClick(
145 analog_map_buttons[sub_button_id],
146 [=, this](const Common::ParamPackage& params) {
147 Common::ParamPackage param = emulated_device->GetRingParam();
148 SetAnalogParam(params, param, analog_sub_buttons[sub_button_id]);
149 emulated_device->SetRingParam(param);
150 },
151 InputCommon::Polling::InputType::Stick);
152 });
153
154 analog_button->setContextMenuPolicy(Qt::CustomContextMenu);
155
156 connect(analog_button, &QPushButton::customContextMenuRequested,
157 [=, this](const QPoint& menu_location) {
158 QMenu context_menu;
159 Common::ParamPackage param = emulated_device->GetRingParam();
160 context_menu.addAction(tr("Clear"), [&] {
161 emulated_device->SetRingParam({});
162 analog_map_buttons[sub_button_id]->setText(tr("[not set]"));
163 });
164 context_menu.addAction(tr("Invert axis"), [&] {
165 const bool invert_value = param.Get("invert_x", "+") == "-";
166 const std::string invert_str = invert_value ? "+" : "-";
167 param.Set("invert_x", invert_str);
168 emulated_device->SetRingParam(param);
169 for (int sub_button_id = 0; sub_button_id < ANALOG_SUB_BUTTONS_NUM;
170 ++sub_button_id) {
171 analog_map_buttons[sub_button_id]->setText(
172 AnalogToText(param, analog_sub_buttons[sub_button_id]));
173 }
174 });
175 context_menu.exec(
176 analog_map_buttons[sub_button_id]->mapToGlobal(menu_location));
177 });
178 }
179
180 connect(ui->sliderRingAnalogDeadzone, &QSlider::valueChanged, [=, this] {
181 Common::ParamPackage param = emulated_device->GetRingParam();
182 const auto slider_value = ui->sliderRingAnalogDeadzone->value();
183 ui->labelRingAnalogDeadzone->setText(tr("Deadzone: %1%").arg(slider_value));
184 param.Set("deadzone", slider_value / 100.0f);
185 emulated_device->SetRingParam(param);
186 });
187
188 connect(ui->restore_defaults_button, &QPushButton::clicked, this,
189 &ConfigureRingController::RestoreDefaults);
190
191 timeout_timer->setSingleShot(true);
192 connect(timeout_timer.get(), &QTimer::timeout, [this] { SetPollingResult({}, true); });
193
194 connect(poll_timer.get(), &QTimer::timeout, [this] {
195 const auto& params = input_subsystem->GetNextInput();
196 if (params.Has("engine") && IsInputAcceptable(params)) {
197 SetPollingResult(params, false);
198 return;
199 }
200 });
201
202 resize(0, 0);
203}
204
205ConfigureRingController::~ConfigureRingController() {
206 emulated_device->DisableConfiguration();
207};
208
209void ConfigureRingController::changeEvent(QEvent* event) {
210 if (event->type() == QEvent::LanguageChange) {
211 RetranslateUI();
212 }
213
214 QDialog::changeEvent(event);
215}
216
217void ConfigureRingController::RetranslateUI() {
218 ui->retranslateUi(this);
219}
220
221void ConfigureRingController::UpdateUI() {
222 RetranslateUI();
223 const Common::ParamPackage param = emulated_device->GetRingParam();
224
225 for (int sub_button_id = 0; sub_button_id < ANALOG_SUB_BUTTONS_NUM; ++sub_button_id) {
226 auto* const analog_button = analog_map_buttons[sub_button_id];
227
228 if (analog_button == nullptr) {
229 continue;
230 }
231
232 analog_button->setText(AnalogToText(param, analog_sub_buttons[sub_button_id]));
233 }
234
235 const auto deadzone_label = ui->labelRingAnalogDeadzone;
236 const auto deadzone_slider = ui->sliderRingAnalogDeadzone;
237
238 int slider_value = static_cast<int>(param.Get("deadzone", 0.15f) * 100);
239 deadzone_label->setText(tr("Deadzone: %1%").arg(slider_value));
240 deadzone_slider->setValue(slider_value);
241}
242
243void ConfigureRingController::ApplyConfiguration() {
244 emulated_device->DisableConfiguration();
245 emulated_device->SaveCurrentConfig();
246 emulated_device->EnableConfiguration();
247}
248
249void ConfigureRingController::LoadConfiguration() {
250 UpdateUI();
251}
252
253void ConfigureRingController::RestoreDefaults() {
254 const std::string default_ring_string = InputCommon::GenerateAnalogParamFromKeys(
255 0, 0, Config::default_ringcon_analogs[0], Config::default_ringcon_analogs[1], 0, 0.05f);
256 emulated_device->SetRingParam(Common::ParamPackage(default_ring_string));
257 UpdateUI();
258}
259
260void ConfigureRingController::HandleClick(
261 QPushButton* button, std::function<void(const Common::ParamPackage&)> new_input_setter,
262 InputCommon::Polling::InputType type) {
263 button->setText(tr("[waiting]"));
264 button->setFocus();
265
266 input_setter = new_input_setter;
267
268 input_subsystem->BeginMapping(type);
269
270 QWidget::grabMouse();
271 QWidget::grabKeyboard();
272
273 timeout_timer->start(2500); // Cancel after 2.5 seconds
274 poll_timer->start(25); // Check for new inputs every 25ms
275}
276
277void ConfigureRingController::SetPollingResult(const Common::ParamPackage& params, bool abort) {
278 timeout_timer->stop();
279 poll_timer->stop();
280 input_subsystem->StopMapping();
281
282 QWidget::releaseMouse();
283 QWidget::releaseKeyboard();
284
285 if (!abort) {
286 (*input_setter)(params);
287 }
288
289 UpdateUI();
290
291 input_setter = std::nullopt;
292}
293
294bool ConfigureRingController::IsInputAcceptable(const Common::ParamPackage& params) const {
295 return true;
296}
297
298void ConfigureRingController::mousePressEvent(QMouseEvent* event) {
299 if (!input_setter || !event) {
300 return;
301 }
302
303 const auto button = GRenderWindow::QtButtonToMouseButton(event->button());
304 input_subsystem->GetMouse()->PressButton(0, 0, 0, 0, button);
305}
306
307void ConfigureRingController::keyPressEvent(QKeyEvent* event) {
308 if (!input_setter || !event) {
309 return;
310 }
311 event->ignore();
312 if (event->key() != Qt::Key_Escape) {
313 input_subsystem->GetKeyboard()->PressKey(event->key());
314 }
315}
316
317QString ConfigureRingController::ButtonToText(const Common::ParamPackage& param) {
318 if (!param.Has("engine")) {
319 return QObject::tr("[not set]");
320 }
321
322 const QString toggle = QString::fromStdString(param.Get("toggle", false) ? "~" : "");
323 const QString inverted = QString::fromStdString(param.Get("inverted", false) ? "!" : "");
324 const auto common_button_name = input_subsystem->GetButtonName(param);
325
326 // Retrieve the names from Qt
327 if (param.Get("engine", "") == "keyboard") {
328 const QString button_str = GetKeyName(param.Get("code", 0));
329 return QObject::tr("%1%2").arg(toggle, button_str);
330 }
331
332 if (common_button_name == Common::Input::ButtonNames::Invalid) {
333 return QObject::tr("[invalid]");
334 }
335
336 if (common_button_name == Common::Input::ButtonNames::Engine) {
337 return QString::fromStdString(param.Get("engine", ""));
338 }
339
340 if (common_button_name == Common::Input::ButtonNames::Value) {
341 if (param.Has("hat")) {
342 const QString hat = QString::fromStdString(param.Get("direction", ""));
343 return QObject::tr("%1%2Hat %3").arg(toggle, inverted, hat);
344 }
345 if (param.Has("axis")) {
346 const QString axis = QString::fromStdString(param.Get("axis", ""));
347 return QObject::tr("%1%2Axis %3").arg(toggle, inverted, axis);
348 }
349 if (param.Has("axis_x") && param.Has("axis_y") && param.Has("axis_z")) {
350 const QString axis_x = QString::fromStdString(param.Get("axis_x", ""));
351 const QString axis_y = QString::fromStdString(param.Get("axis_y", ""));
352 const QString axis_z = QString::fromStdString(param.Get("axis_z", ""));
353 return QObject::tr("%1%2Axis %3,%4,%5").arg(toggle, inverted, axis_x, axis_y, axis_z);
354 }
355 if (param.Has("motion")) {
356 const QString motion = QString::fromStdString(param.Get("motion", ""));
357 return QObject::tr("%1%2Motion %3").arg(toggle, inverted, motion);
358 }
359 if (param.Has("button")) {
360 const QString button = QString::fromStdString(param.Get("button", ""));
361 return QObject::tr("%1%2Button %3").arg(toggle, inverted, button);
362 }
363 }
364
365 QString button_name = GetButtonName(common_button_name);
366 if (param.Has("hat")) {
367 return QObject::tr("%1%2Hat %3").arg(toggle, inverted, button_name);
368 }
369 if (param.Has("axis")) {
370 return QObject::tr("%1%2Axis %3").arg(toggle, inverted, button_name);
371 }
372 if (param.Has("motion")) {
373 return QObject::tr("%1%2Axis %3").arg(toggle, inverted, button_name);
374 }
375 if (param.Has("button")) {
376 return QObject::tr("%1%2Button %3").arg(toggle, inverted, button_name);
377 }
378
379 return QObject::tr("[unknown]");
380}
381
382QString ConfigureRingController::AnalogToText(const Common::ParamPackage& param,
383 const std::string& dir) {
384 if (!param.Has("engine")) {
385 return QObject::tr("[not set]");
386 }
387
388 if (param.Get("engine", "") == "analog_from_button") {
389 return ButtonToText(Common::ParamPackage{param.Get(dir, "")});
390 }
391
392 if (!param.Has("axis_x") || !param.Has("axis_y")) {
393 return QObject::tr("[unknown]");
394 }
395
396 const auto engine_str = param.Get("engine", "");
397 const QString axis_x_str = QString::fromStdString(param.Get("axis_x", ""));
398 const QString axis_y_str = QString::fromStdString(param.Get("axis_y", ""));
399 const bool invert_x = param.Get("invert_x", "+") == "-";
400 const bool invert_y = param.Get("invert_y", "+") == "-";
401
402 if (dir == "modifier") {
403 return QObject::tr("[unused]");
404 }
405
406 if (dir == "left") {
407 const QString invert_x_str = QString::fromStdString(invert_x ? "+" : "-");
408 return QObject::tr("Axis %1%2").arg(axis_x_str, invert_x_str);
409 }
410 if (dir == "right") {
411 const QString invert_x_str = QString::fromStdString(invert_x ? "-" : "+");
412 return QObject::tr("Axis %1%2").arg(axis_x_str, invert_x_str);
413 }
414 if (dir == "up") {
415 const QString invert_y_str = QString::fromStdString(invert_y ? "-" : "+");
416 return QObject::tr("Axis %1%2").arg(axis_y_str, invert_y_str);
417 }
418 if (dir == "down") {
419 const QString invert_y_str = QString::fromStdString(invert_y ? "+" : "-");
420 return QObject::tr("Axis %1%2").arg(axis_y_str, invert_y_str);
421 }
422
423 return QObject::tr("[unknown]");
424} \ No newline at end of file
diff --git a/src/yuzu/configuration/configure_ringcon.h b/src/yuzu/configuration/configure_ringcon.h
new file mode 100644
index 000000000..cf9e54f09
--- /dev/null
+++ b/src/yuzu/configuration/configure_ringcon.h
@@ -0,0 +1,85 @@
1// Copyright 2022 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <functional>
8#include <memory>
9#include <QDialog>
10
11namespace InputCommon {
12class InputSubsystem;
13} // namespace InputCommon
14
15namespace Core::HID {
16class HIDCore;
17class EmulatedDevices;
18} // namespace Core::HID
19
20namespace Ui {
21class ConfigureRingController;
22} // namespace Ui
23
24class ConfigureRingController : public QDialog {
25 Q_OBJECT
26
27public:
28 explicit ConfigureRingController(QWidget* parent, InputCommon::InputSubsystem* input_subsystem_,
29 Core::HID::HIDCore& hid_core_);
30 ~ConfigureRingController() override;
31
32 void ApplyConfiguration();
33
34private:
35 void changeEvent(QEvent* event) override;
36 void RetranslateUI();
37
38 void UpdateUI();
39
40 /// Load configuration settings.
41 void LoadConfiguration();
42
43 /// Restore all buttons to their default values.
44 void RestoreDefaults();
45
46 /// Called when the button was pressed.
47 void HandleClick(QPushButton* button,
48 std::function<void(const Common::ParamPackage&)> new_input_setter,
49 InputCommon::Polling::InputType type);
50
51 /// Finish polling and configure input using the input_setter.
52 void SetPollingResult(const Common::ParamPackage& params, bool abort);
53
54 /// Checks whether a given input can be accepted.
55 bool IsInputAcceptable(const Common::ParamPackage& params) const;
56
57 /// Handle mouse button press events.
58 void mousePressEvent(QMouseEvent* event) override;
59
60 /// Handle key press events.
61 void keyPressEvent(QKeyEvent* event) override;
62
63 QString ButtonToText(const Common::ParamPackage& param);
64
65 QString AnalogToText(const Common::ParamPackage& param, const std::string& dir);
66
67 static constexpr int ANALOG_SUB_BUTTONS_NUM = 2;
68
69 // A group of four QPushButtons represent one analog input. The buttons each represent left,
70 // right, respectively.
71 std::array<QPushButton*, ANALOG_SUB_BUTTONS_NUM> analog_map_buttons;
72
73 static const std::array<std::string, ANALOG_SUB_BUTTONS_NUM> analog_sub_buttons;
74
75 std::unique_ptr<QTimer> timeout_timer;
76 std::unique_ptr<QTimer> poll_timer;
77
78 /// This will be the the setting function when an input is awaiting configuration.
79 std::optional<std::function<void(const Common::ParamPackage&)>> input_setter;
80
81 InputCommon::InputSubsystem* input_subsystem;
82 Core::HID::EmulatedDevices* emulated_device;
83
84 std::unique_ptr<Ui::ConfigureRingController> ui;
85};
diff --git a/src/yuzu/configuration/configure_ringcon.ui b/src/yuzu/configuration/configure_ringcon.ui
new file mode 100644
index 000000000..9ec634dd4
--- /dev/null
+++ b/src/yuzu/configuration/configure_ringcon.ui
@@ -0,0 +1,278 @@
1<?xml version="1.0" encoding="UTF-8"?>
2<ui version="4.0">
3 <class>ConfigureRingController</class>
4 <widget class="QDialog" name="ConfigureRingController">
5 <property name="geometry">
6 <rect>
7 <x>0</x>
8 <y>0</y>
9 <width>298</width>
10 <height>339</height>
11 </rect>
12 </property>
13 <property name="windowTitle">
14 <string>Configure Ring Controller</string>
15 </property>
16 <layout class="QVBoxLayout" name="verticalLayout">
17 <item>
18 <widget class="QLabel" name="label_2">
19 <property name="minimumSize">
20 <size>
21 <width>280</width>
22 <height>0</height>
23 </size>
24 </property>
25 <property name="text">
26 <string>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.</string>
27 </property>
28 <property name="wordWrap">
29 <bool>true</bool>
30 </property>
31 </widget>
32 </item>
33 <item>
34 <spacer name="verticalSpacer_2">
35 <property name="orientation">
36 <enum>Qt::Vertical</enum>
37 </property>
38 <property name="sizeType">
39 <enum>QSizePolicy::Fixed</enum>
40 </property>
41 <property name="sizeHint" stdset="0">
42 <size>
43 <width>20</width>
44 <height>10</height>
45 </size>
46 </property>
47 </spacer>
48 </item>
49 <item>
50 <widget class="QGroupBox" name="RingAnalog">
51 <property name="title">
52 <string>Ring Sensor Parameters</string>
53 </property>
54 <property name="alignment">
55 <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
56 </property>
57 <layout class="QVBoxLayout" name="verticalLayout_3">
58 <property name="spacing">
59 <number>0</number>
60 </property>
61 <property name="sizeConstraint">
62 <enum>QLayout::SetDefaultConstraint</enum>
63 </property>
64 <property name="leftMargin">
65 <number>3</number>
66 </property>
67 <property name="topMargin">
68 <number>6</number>
69 </property>
70 <property name="rightMargin">
71 <number>3</number>
72 </property>
73 <property name="bottomMargin">
74 <number>0</number>
75 </property>
76 <item>
77 <layout class="QHBoxLayout" name="buttonRingAnalogPullHorizontaLayout">
78 <property name="spacing">
79 <number>3</number>
80 </property>
81 <item alignment="Qt::AlignHCenter">
82 <widget class="QGroupBox" name="buttonRingAnalogPullGroup">
83 <property name="title">
84 <string>Pull</string>
85 </property>
86 <property name="alignment">
87 <set>Qt::AlignCenter</set>
88 </property>
89 <layout class="QVBoxLayout" name="buttonRingAnalogPullVerticalLayout">
90 <property name="spacing">
91 <number>3</number>
92 </property>
93 <property name="leftMargin">
94 <number>3</number>
95 </property>
96 <property name="topMargin">
97 <number>3</number>
98 </property>
99 <property name="rightMargin">
100 <number>3</number>
101 </property>
102 <property name="bottomMargin">
103 <number>3</number>
104 </property>
105 <item>
106 <widget class="QPushButton" name="buttonRingAnalogPull">
107 <property name="minimumSize">
108 <size>
109 <width>68</width>
110 <height>0</height>
111 </size>
112 </property>
113 <property name="maximumSize">
114 <size>
115 <width>68</width>
116 <height>16777215</height>
117 </size>
118 </property>
119 <property name="styleSheet">
120 <string notr="true">min-width: 68px;</string>
121 </property>
122 <property name="text">
123 <string>Pull</string>
124 </property>
125 </widget>
126 </item>
127 </layout>
128 </widget>
129 </item>
130 <item alignment="Qt::AlignHCenter">
131 <widget class="QGroupBox" name="buttonRingAnalogPushGroup">
132 <property name="title">
133 <string>Push</string>
134 </property>
135 <property name="alignment">
136 <set>Qt::AlignCenter</set>
137 </property>
138 <layout class="QVBoxLayout" name="buttonRingAnalogPushVerticalLayout">
139 <property name="spacing">
140 <number>3</number>
141 </property>
142 <property name="leftMargin">
143 <number>3</number>
144 </property>
145 <property name="topMargin">
146 <number>3</number>
147 </property>
148 <property name="rightMargin">
149 <number>3</number>
150 </property>
151 <property name="bottomMargin">
152 <number>3</number>
153 </property>
154 <item>
155 <widget class="QPushButton" name="buttonRingAnalogPush">
156 <property name="minimumSize">
157 <size>
158 <width>68</width>
159 <height>0</height>
160 </size>
161 </property>
162 <property name="maximumSize">
163 <size>
164 <width>68</width>
165 <height>16777215</height>
166 </size>
167 </property>
168 <property name="styleSheet">
169 <string notr="true">min-width: 68px;</string>
170 </property>
171 <property name="text">
172 <string>Push</string>
173 </property>
174 </widget>
175 </item>
176 </layout>
177 </widget>
178 </item>
179 </layout>
180 </item>
181 <item>
182 <layout class="QVBoxLayout" name="sliderRingAnalogDeadzoneVerticalLayout">
183 <property name="spacing">
184 <number>3</number>
185 </property>
186 <property name="sizeConstraint">
187 <enum>QLayout::SetDefaultConstraint</enum>
188 </property>
189 <property name="leftMargin">
190 <number>0</number>
191 </property>
192 <property name="topMargin">
193 <number>10</number>
194 </property>
195 <property name="rightMargin">
196 <number>0</number>
197 </property>
198 <property name="bottomMargin">
199 <number>3</number>
200 </property>
201 <item>
202 <layout class="QHBoxLayout" name="sliderRingAnalogDeadzoneHorizontalLayout">
203 <item>
204 <widget class="QLabel" name="labelRingAnalogDeadzone">
205 <property name="text">
206 <string>Deadzone: 0%</string>
207 </property>
208 <property name="alignment">
209 <set>Qt::AlignHCenter</set>
210 </property>
211 </widget>
212 </item>
213 </layout>
214 </item>
215 <item>
216 <widget class="QSlider" name="sliderRingAnalogDeadzone">
217 <property name="maximum">
218 <number>100</number>
219 </property>
220 <property name="orientation">
221 <enum>Qt::Horizontal</enum>
222 </property>
223 </widget>
224 </item>
225 </layout>
226 </item>
227 </layout>
228 </widget>
229 </item>
230 <item>
231 <spacer name="verticalSpacer">
232 <property name="orientation">
233 <enum>Qt::Vertical</enum>
234 </property>
235 <property name="sizeHint" stdset="0">
236 <size>
237 <width>20</width>
238 <height>40</height>
239 </size>
240 </property>
241 </spacer>
242 </item>
243 <item>
244 <layout class="QHBoxLayout" name="horizontalLayout">
245 <item>
246 <widget class="QPushButton" name="restore_defaults_button">
247 <property name="text">
248 <string>Restore Defaults</string>
249 </property>
250 </widget>
251 </item>
252 <item>
253 <widget class="QDialogButtonBox" name="buttonBox">
254 <property name="standardButtons">
255 <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
256 </property>
257 </widget>
258 </item>
259 </layout>
260 </item>
261 </layout>
262 </widget>
263 <resources/>
264 <connections>
265 <connection>
266 <sender>buttonBox</sender>
267 <signal>accepted()</signal>
268 <receiver>ConfigureRingController</receiver>
269 <slot>accept()</slot>
270 </connection>
271 <connection>
272 <sender>buttonBox</sender>
273 <signal>rejected()</signal>
274 <receiver>ConfigureRingController</receiver>
275 <slot>reject()</slot>
276 </connection>
277 </connections>
278</ui>
diff --git a/src/yuzu/game_list_p.h b/src/yuzu/game_list_p.h
index 211a84dde..f2a986ed8 100644
--- a/src/yuzu/game_list_p.h
+++ b/src/yuzu/game_list_p.h
@@ -164,8 +164,8 @@ public:
164 } 164 }
165 const CompatStatus& status = iterator->second; 165 const CompatStatus& status = iterator->second;
166 setData(compatibility, CompatNumberRole); 166 setData(compatibility, CompatNumberRole);
167 setText(QObject::tr(status.text)); 167 setText(tr(status.text));
168 setToolTip(QObject::tr(status.tooltip)); 168 setToolTip(tr(status.tooltip));
169 setData(CreateCirclePixmapFromColor(status.color), Qt::DecorationRole); 169 setData(CreateCirclePixmapFromColor(status.color), Qt::DecorationRole);
170 } 170 }
171 171
diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp
index ecd2f9f24..5e26aad29 100644
--- a/src/yuzu/main.cpp
+++ b/src/yuzu/main.cpp
@@ -24,7 +24,7 @@
24#include "core/file_sys/vfs_real.h" 24#include "core/file_sys/vfs_real.h"
25#include "core/frontend/applets/controller.h" 25#include "core/frontend/applets/controller.h"
26#include "core/frontend/applets/general_frontend.h" 26#include "core/frontend/applets/general_frontend.h"
27#include "core/frontend/applets/mii.h" 27#include "core/frontend/applets/mii_edit.h"
28#include "core/frontend/applets/software_keyboard.h" 28#include "core/frontend/applets/software_keyboard.h"
29#include "core/hid/emulated_controller.h" 29#include "core/hid/emulated_controller.h"
30#include "core/hid/hid_core.h" 30#include "core/hid/hid_core.h"
@@ -152,7 +152,8 @@ __declspec(dllexport) int AmdPowerXpressRequestHighPerformance = 1;
152} 152}
153#endif 153#endif
154 154
155constexpr int default_mouse_timeout = 2500; 155constexpr int default_mouse_hide_timeout = 2500;
156constexpr int default_mouse_center_timeout = 10;
156 157
157/** 158/**
158 * "Callouts" are one-time instructional messages shown to the user. In the config settings, there 159 * "Callouts" are one-time instructional messages shown to the user. In the config settings, there
@@ -287,13 +288,14 @@ GMainWindow::GMainWindow()
287 ui->menubar->setCursor(QCursor()); 288 ui->menubar->setCursor(QCursor());
288 statusBar()->setCursor(QCursor()); 289 statusBar()->setCursor(QCursor());
289 290
290 mouse_hide_timer.setInterval(default_mouse_timeout); 291 mouse_hide_timer.setInterval(default_mouse_hide_timeout);
291 connect(&mouse_hide_timer, &QTimer::timeout, this, &GMainWindow::HideMouseCursor); 292 connect(&mouse_hide_timer, &QTimer::timeout, this, &GMainWindow::HideMouseCursor);
292 connect(ui->menubar, &QMenuBar::hovered, this, &GMainWindow::ShowMouseCursor); 293 connect(ui->menubar, &QMenuBar::hovered, this, &GMainWindow::ShowMouseCursor);
293 294
294 MigrateConfigFiles(); 295 mouse_center_timer.setInterval(default_mouse_center_timeout);
296 connect(&mouse_center_timer, &QTimer::timeout, this, &GMainWindow::CenterMouseCursor);
295 297
296 ui->action_Fullscreen->setChecked(false); 298 MigrateConfigFiles();
297 299
298#if defined(HAVE_SDL2) && !defined(_WIN32) 300#if defined(HAVE_SDL2) && !defined(_WIN32)
299 SDL_InitSubSystem(SDL_INIT_VIDEO); 301 SDL_InitSubSystem(SDL_INIT_VIDEO);
@@ -312,17 +314,20 @@ GMainWindow::GMainWindow()
312 } 314 }
313 315
314 QString game_path; 316 QString game_path;
317 bool has_gamepath = false;
318 bool is_fullscreen = false;
315 319
316 for (int i = 1; i < args.size(); ++i) { 320 for (int i = 1; i < args.size(); ++i) {
317 // Preserves drag/drop functionality 321 // Preserves drag/drop functionality
318 if (args.size() == 2 && !args[1].startsWith(QChar::fromLatin1('-'))) { 322 if (args.size() == 2 && !args[1].startsWith(QChar::fromLatin1('-'))) {
319 game_path = args[1]; 323 game_path = args[1];
324 has_gamepath = true;
320 break; 325 break;
321 } 326 }
322 327
323 // Launch game in fullscreen mode 328 // Launch game in fullscreen mode
324 if (args[i] == QStringLiteral("-f")) { 329 if (args[i] == QStringLiteral("-f")) {
325 ui->action_Fullscreen->setChecked(true); 330 is_fullscreen = true;
326 continue; 331 continue;
327 } 332 }
328 333
@@ -365,9 +370,15 @@ GMainWindow::GMainWindow()
365 } 370 }
366 371
367 game_path = args[++i]; 372 game_path = args[++i];
373 has_gamepath = true;
368 } 374 }
369 } 375 }
370 376
377 // Override fullscreen setting if gamepath or argument is provided
378 if (has_gamepath || is_fullscreen) {
379 ui->action_Fullscreen->setChecked(is_fullscreen);
380 }
381
371 if (!game_path.isEmpty()) { 382 if (!game_path.isEmpty()) {
372 BootGame(game_path); 383 BootGame(game_path);
373 } 384 }
@@ -582,7 +593,7 @@ void GMainWindow::WebBrowserOpenWebPage(const std::string& main_url,
582#ifdef YUZU_USE_QT_WEB_ENGINE 593#ifdef YUZU_USE_QT_WEB_ENGINE
583 594
584 // Raw input breaks with the web applet, Disable web applets if enabled 595 // Raw input breaks with the web applet, Disable web applets if enabled
585 if (disable_web_applet || Settings::values.enable_raw_input) { 596 if (UISettings::values.disable_web_applet || Settings::values.enable_raw_input) {
586 emit WebBrowserClosed(Service::AM::Applets::WebExitReason::WindowClosed, 597 emit WebBrowserClosed(Service::AM::Applets::WebExitReason::WindowClosed,
587 "http://localhost/"); 598 "http://localhost/");
588 return; 599 return;
@@ -647,12 +658,12 @@ void GMainWindow::WebBrowserOpenWebPage(const std::string& main_url,
647 connect(exit_action, &QAction::triggered, this, [this, &web_browser_view] { 658 connect(exit_action, &QAction::triggered, this, [this, &web_browser_view] {
648 const auto result = QMessageBox::warning( 659 const auto result = QMessageBox::warning(
649 this, tr("Disable Web Applet"), 660 this, tr("Disable Web Applet"),
650 tr("Disabling the web applet will cause it to not be shown again for the rest of the " 661 tr("Disabling the web applet can lead to undefined behavior and should only be used "
651 "emulated session. This can lead to undefined behavior and should only be used with " 662 "with Super Mario 3D All-Stars. Are you sure you want to disable the web "
652 "Super Mario 3D All-Stars. Are you sure you want to disable the web applet?"), 663 "applet?\n(This can be re-enabled in the Debug settings.)"),
653 QMessageBox::Yes | QMessageBox::No); 664 QMessageBox::Yes | QMessageBox::No);
654 if (result == QMessageBox::Yes) { 665 if (result == QMessageBox::Yes) {
655 disable_web_applet = true; 666 UISettings::values.disable_web_applet = true;
656 web_browser_view.SetFinished(true); 667 web_browser_view.SetFinished(true);
657 } 668 }
658 }); 669 });
@@ -1280,8 +1291,8 @@ bool GMainWindow::LoadROM(const QString& filename, u64 program_id, std::size_t p
1280 system->SetAppletFrontendSet({ 1291 system->SetAppletFrontendSet({
1281 std::make_unique<QtControllerSelector>(*this), // Controller Selector 1292 std::make_unique<QtControllerSelector>(*this), // Controller Selector
1282 std::make_unique<QtErrorDisplay>(*this), // Error Display 1293 std::make_unique<QtErrorDisplay>(*this), // Error Display
1294 nullptr, // Mii Editor
1283 nullptr, // Parental Controls 1295 nullptr, // Parental Controls
1284 nullptr, // Mii editor
1285 nullptr, // Photo Viewer 1296 nullptr, // Photo Viewer
1286 std::make_unique<QtProfileSelector>(*this), // Profile Selector 1297 std::make_unique<QtProfileSelector>(*this), // Profile Selector
1287 std::make_unique<QtSoftwareKeyboard>(*this), // Software Keyboard 1298 std::make_unique<QtSoftwareKeyboard>(*this), // Software Keyboard
@@ -3294,10 +3305,26 @@ void GMainWindow::ShowMouseCursor() {
3294 } 3305 }
3295} 3306}
3296 3307
3308void GMainWindow::CenterMouseCursor() {
3309 if (emu_thread == nullptr || !Settings::values.mouse_panning) {
3310 mouse_center_timer.stop();
3311 return;
3312 }
3313 if (!this->isActiveWindow()) {
3314 mouse_center_timer.stop();
3315 return;
3316 }
3317 const int center_x = render_window->width() / 2;
3318 const int center_y = render_window->height() / 2;
3319
3320 QCursor::setPos(mapToGlobal({center_x, center_y}));
3321}
3322
3297void GMainWindow::OnMouseActivity() { 3323void GMainWindow::OnMouseActivity() {
3298 if (!Settings::values.mouse_panning) { 3324 if (!Settings::values.mouse_panning) {
3299 ShowMouseCursor(); 3325 ShowMouseCursor();
3300 } 3326 }
3327 mouse_center_timer.stop();
3301} 3328}
3302 3329
3303void GMainWindow::OnCoreError(Core::SystemResultStatus result, std::string details) { 3330void GMainWindow::OnCoreError(Core::SystemResultStatus result, std::string details) {
@@ -3570,6 +3597,22 @@ void GMainWindow::dragMoveEvent(QDragMoveEvent* event) {
3570 AcceptDropEvent(event); 3597 AcceptDropEvent(event);
3571} 3598}
3572 3599
3600void GMainWindow::leaveEvent(QEvent* event) {
3601 if (Settings::values.mouse_panning) {
3602 const QRect& rect = geometry();
3603 QPoint position = QCursor::pos();
3604
3605 qint32 x = qBound(rect.left(), position.x(), rect.right());
3606 qint32 y = qBound(rect.top(), position.y(), rect.bottom());
3607 // Only start the timer if the mouse has left the window bound.
3608 // The leave event is also triggered when the window looses focus.
3609 if (x != position.x() || y != position.y()) {
3610 mouse_center_timer.start();
3611 }
3612 event->accept();
3613 }
3614}
3615
3573bool GMainWindow::ConfirmChangeGame() { 3616bool GMainWindow::ConfirmChangeGame() {
3574 if (emu_thread == nullptr) 3617 if (emu_thread == nullptr)
3575 return true; 3618 return true;
@@ -3645,6 +3688,14 @@ void GMainWindow::UpdateUITheme() {
3645 setStyleSheet({}); 3688 setStyleSheet({});
3646 } 3689 }
3647 3690
3691 QPalette new_pal(qApp->palette());
3692 if (UISettings::IsDarkTheme()) {
3693 new_pal.setColor(QPalette::Link, QColor(0, 190, 255, 255));
3694 } else {
3695 new_pal.setColor(QPalette::Link, QColor(0, 140, 200, 255));
3696 }
3697 qApp->setPalette(new_pal);
3698
3648 QIcon::setThemeName(current_theme); 3699 QIcon::setThemeName(current_theme);
3649 QIcon::setThemeSearchPaths(theme_paths); 3700 QIcon::setThemeSearchPaths(theme_paths);
3650} 3701}
diff --git a/src/yuzu/main.h b/src/yuzu/main.h
index 7659c8000..b399e9b01 100644
--- a/src/yuzu/main.h
+++ b/src/yuzu/main.h
@@ -328,6 +328,7 @@ private:
328 void UpdateUISettings(); 328 void UpdateUISettings();
329 void HideMouseCursor(); 329 void HideMouseCursor();
330 void ShowMouseCursor(); 330 void ShowMouseCursor();
331 void CenterMouseCursor();
331 void OpenURL(const QUrl& url); 332 void OpenURL(const QUrl& url);
332 void LoadTranslation(); 333 void LoadTranslation();
333 void OpenPerGameConfiguration(u64 title_id, const std::string& file_name); 334 void OpenPerGameConfiguration(u64 title_id, const std::string& file_name);
@@ -372,6 +373,7 @@ private:
372 bool auto_paused = false; 373 bool auto_paused = false;
373 bool auto_muted = false; 374 bool auto_muted = false;
374 QTimer mouse_hide_timer; 375 QTimer mouse_hide_timer;
376 QTimer mouse_center_timer;
375 377
376 // FS 378 // FS
377 std::shared_ptr<FileSys::VfsFilesystem> vfs; 379 std::shared_ptr<FileSys::VfsFilesystem> vfs;
@@ -398,9 +400,6 @@ private:
398 // Last game booted, used for multi-process apps 400 // Last game booted, used for multi-process apps
399 QString last_filename_booted; 401 QString last_filename_booted;
400 402
401 // Disables the web applet for the rest of the emulated session
402 bool disable_web_applet{};
403
404 // Applets 403 // Applets
405 QtSoftwareKeyboardDialog* software_keyboard = nullptr; 404 QtSoftwareKeyboardDialog* software_keyboard = nullptr;
406 405
@@ -421,4 +420,5 @@ protected:
421 void dropEvent(QDropEvent* event) override; 420 void dropEvent(QDropEvent* event) override;
422 void dragEnterEvent(QDragEnterEvent* event) override; 421 void dragEnterEvent(QDragEnterEvent* event) override;
423 void dragMoveEvent(QDragMoveEvent* event) override; 422 void dragMoveEvent(QDragMoveEvent* event) override;
423 void leaveEvent(QEvent* event) override;
424}; 424};
diff --git a/src/yuzu/uisettings.cpp b/src/yuzu/uisettings.cpp
index 21683576c..f683b80f7 100644
--- a/src/yuzu/uisettings.cpp
+++ b/src/yuzu/uisettings.cpp
@@ -15,6 +15,14 @@ const Themes themes{{
15 {"Midnight Blue Colorful", "colorful_midnight_blue"}, 15 {"Midnight Blue Colorful", "colorful_midnight_blue"},
16}}; 16}};
17 17
18bool IsDarkTheme() {
19 const auto& theme = UISettings::values.theme;
20 return theme == QStringLiteral("qdarkstyle") ||
21 theme == QStringLiteral("qdarkstyle_midnight_blue") ||
22 theme == QStringLiteral("colorful_dark") ||
23 theme == QStringLiteral("colorful_midnight_blue");
24}
25
18Values values = {}; 26Values values = {};
19 27
20} // namespace UISettings 28} // namespace UISettings
diff --git a/src/yuzu/uisettings.h b/src/yuzu/uisettings.h
index 06e8b46da..15ba9ea17 100644
--- a/src/yuzu/uisettings.h
+++ b/src/yuzu/uisettings.h
@@ -17,6 +17,8 @@
17 17
18namespace UISettings { 18namespace UISettings {
19 19
20bool IsDarkTheme();
21
20struct ContextualShortcut { 22struct ContextualShortcut {
21 QString keyseq; 23 QString keyseq;
22 QString controller_keyseq; 24 QString controller_keyseq;
@@ -114,6 +116,7 @@ struct Values {
114 116
115 bool configuration_applied; 117 bool configuration_applied;
116 bool reset_to_defaults; 118 bool reset_to_defaults;
119 Settings::BasicSetting<bool> disable_web_applet{true, "disable_web_applet"};
117}; 120};
118 121
119extern Values values; 122extern Values values;
diff --git a/src/yuzu/util/controller_navigation.cpp b/src/yuzu/util/controller_navigation.cpp
index c2b13123d..9a1868cae 100644
--- a/src/yuzu/util/controller_navigation.cpp
+++ b/src/yuzu/util/controller_navigation.cpp
@@ -39,7 +39,7 @@ void ControllerNavigation::TriggerButton(Settings::NativeButton::Values native_b
39} 39}
40 40
41void ControllerNavigation::ControllerUpdateEvent(Core::HID::ControllerTriggerType type) { 41void ControllerNavigation::ControllerUpdateEvent(Core::HID::ControllerTriggerType type) {
42 std::lock_guard lock{mutex}; 42 std::scoped_lock lock{mutex};
43 if (!Settings::values.controller_navigation) { 43 if (!Settings::values.controller_navigation) {
44 return; 44 return;
45 } 45 }
diff --git a/src/yuzu_cmd/CMakeLists.txt b/src/yuzu_cmd/CMakeLists.txt
index 74fc24972..c8901f2df 100644
--- a/src/yuzu_cmd/CMakeLists.txt
+++ b/src/yuzu_cmd/CMakeLists.txt
@@ -45,7 +45,7 @@ if (YUZU_USE_EXTERNAL_SDL2)
45endif() 45endif()
46 46
47if(UNIX AND NOT APPLE) 47if(UNIX AND NOT APPLE)
48 install(TARGETS yuzu-cmd RUNTIME DESTINATION "${CMAKE_INSTALL_PREFIX}/bin") 48 install(TARGETS yuzu-cmd)
49endif() 49endif()
50 50
51if (MSVC) 51if (MSVC)
diff --git a/src/yuzu_cmd/default_ini.h b/src/yuzu_cmd/default_ini.h
index 34782c378..f34d6b728 100644
--- a/src/yuzu_cmd/default_ini.h
+++ b/src/yuzu_cmd/default_ini.h
@@ -342,12 +342,6 @@ fps_cap =
342# null: No audio output 342# null: No audio output
343output_engine = 343output_engine =
344 344
345# Whether or not to enable the audio-stretching post-processing effect.
346# This effect adjusts audio speed to match emulation speed and helps prevent audio stutter,
347# at the cost of increasing audio latency.
348# 0: No, 1 (default): Yes
349enable_audio_stretching =
350
351# Which audio device to use. 345# Which audio device to use.
352# auto (default): Auto-select 346# auto (default): Auto-select
353output_device = 347output_device =
diff --git a/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp b/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp
index 57f807826..ae2e62dc5 100644
--- a/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp
+++ b/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp
@@ -123,14 +123,15 @@ void EmuWindow_SDL2::ShowCursor(bool show_cursor) {
123} 123}
124 124
125void EmuWindow_SDL2::Fullscreen() { 125void EmuWindow_SDL2::Fullscreen() {
126 SDL_DisplayMode display_mode;
126 switch (Settings::values.fullscreen_mode.GetValue()) { 127 switch (Settings::values.fullscreen_mode.GetValue()) {
127 case Settings::FullscreenMode::Exclusive: 128 case Settings::FullscreenMode::Exclusive:
128 // Set window size to render size before entering fullscreen -- SDL does not resize to 129 // Set window size to render size before entering fullscreen -- SDL2 does not resize window
129 // display dimensions in this mode. 130 // to display dimensions automatically in this mode.
130 // TODO: Multiply the window size by resolution_factor (for both docked modes) 131 if (SDL_GetDesktopDisplayMode(0, &display_mode) == 0) {
131 if (Settings::values.use_docked_mode) { 132 SDL_SetWindowSize(render_window, display_mode.w, display_mode.h);
132 SDL_SetWindowSize(render_window, Layout::ScreenDocked::Width, 133 } else {
133 Layout::ScreenDocked::Height); 134 LOG_ERROR(Frontend, "SDL_GetDesktopDisplayMode failed: {}", SDL_GetError());
134 } 135 }
135 136
136 if (SDL_SetWindowFullscreen(render_window, SDL_WINDOW_FULLSCREEN) == 0) { 137 if (SDL_SetWindowFullscreen(render_window, SDL_WINDOW_FULLSCREEN) == 0) {
diff --git a/src/yuzu_cmd/yuzu.cpp b/src/yuzu_cmd/yuzu.cpp
index 14bf82f39..ab12dd15d 100644
--- a/src/yuzu_cmd/yuzu.cpp
+++ b/src/yuzu_cmd/yuzu.cpp
@@ -74,6 +74,7 @@ static void PrintVersion() {
74int main(int argc, char** argv) { 74int main(int argc, char** argv) {
75 Common::Log::Initialize(); 75 Common::Log::Initialize();
76 Common::Log::SetColorConsoleBackendEnabled(true); 76 Common::Log::SetColorConsoleBackendEnabled(true);
77 Common::Log::Start();
77 Common::DetachedTasks detached_tasks; 78 Common::DetachedTasks detached_tasks;
78 79
79 int option_index = 0; 80 int option_index = 0;