summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/common/CMakeLists.txt4
-rw-r--r--src/common/address_space.h7
-rw-r--r--src/common/alignment.h18
-rw-r--r--src/common/atomic_helpers.h2
-rw-r--r--src/common/bit_util.h6
-rw-r--r--src/common/concepts.h6
-rw-r--r--src/common/demangle.cpp35
-rw-r--r--src/common/demangle.h12
-rw-r--r--src/common/div_ceil.h4
-rw-r--r--src/common/expected.h60
-rw-r--r--src/common/input.h70
-rw-r--r--src/common/intrusive_red_black_tree.h20
-rw-r--r--src/common/make_unique_for_overwrite.h8
-rw-r--r--src/common/polyfill_ranges.h8
-rw-r--r--src/common/polyfill_thread.h123
-rw-r--r--src/common/settings.h13
-rw-r--r--src/common/string_util.cpp2
-rw-r--r--src/common/string_util.h3
-rw-r--r--src/common/tree.h74
-rw-r--r--src/common/vector_math.h16
-rw-r--r--src/core/CMakeLists.txt41
-rw-r--r--src/core/arm/arm_interface.cpp22
-rw-r--r--src/core/debugger/gdbstub.cpp26
-rw-r--r--src/core/hardware_properties.h20
-rw-r--r--src/core/hid/emulated_controller.cpp373
-rw-r--r--src/core/hid/emulated_controller.h70
-rw-r--r--src/core/hid/emulated_devices.cpp46
-rw-r--r--src/core/hid/emulated_devices.h18
-rw-r--r--src/core/hid/input_converter.cpp12
-rw-r--r--src/core/hid/input_converter.h10
-rw-r--r--src/core/hle/kernel/hle_ipc.cpp30
-rw-r--r--src/core/hle/kernel/hle_ipc.h8
-rw-r--r--src/core/hle/kernel/init/init_slab_setup.cpp2
-rw-r--r--src/core/hle/kernel/k_auto_object.h20
-rw-r--r--src/core/hle/kernel/k_capabilities.cpp358
-rw-r--r--src/core/hle/kernel/k_capabilities.h295
-rw-r--r--src/core/hle/kernel/k_code_memory.cpp12
-rw-r--r--src/core/hle/kernel/k_condition_variable.cpp2
-rw-r--r--src/core/hle/kernel/k_device_address_space.cpp150
-rw-r--r--src/core/hle/kernel/k_device_address_space.h60
-rw-r--r--src/core/hle/kernel/k_light_lock.cpp2
-rw-r--r--src/core/hle/kernel/k_memory_layout.h6
-rw-r--r--src/core/hle/kernel/k_page_table.cpp544
-rw-r--r--src/core/hle/kernel/k_page_table.h86
-rw-r--r--src/core/hle/kernel/k_priority_queue.h54
-rw-r--r--src/core/hle/kernel/k_process.cpp40
-rw-r--r--src/core/hle/kernel/k_scoped_lock.h11
-rw-r--r--src/core/hle/kernel/k_shared_memory.cpp6
-rw-r--r--src/core/hle/kernel/k_thread.cpp21
-rw-r--r--src/core/hle/kernel/k_thread.h32
-rw-r--r--src/core/hle/kernel/k_thread_local_page.h6
-rw-r--r--src/core/hle/kernel/kernel.cpp39
-rw-r--r--src/core/hle/kernel/kernel.h4
-rw-r--r--src/core/hle/kernel/svc.cpp2686
-rw-r--r--src/core/hle/kernel/svc.h156
-rw-r--r--src/core/hle/kernel/svc/svc_activity.cpp44
-rw-r--r--src/core/hle/kernel/svc/svc_address_arbiter.cpp113
-rw-r--r--src/core/hle/kernel/svc/svc_address_translation.cpp6
-rw-r--r--src/core/hle/kernel/svc/svc_cache.cpp31
-rw-r--r--src/core/hle/kernel/svc/svc_code_memory.cpp154
-rw-r--r--src/core/hle/kernel/svc/svc_condition_variable.cpp69
-rw-r--r--src/core/hle/kernel/svc/svc_debug.cpp6
-rw-r--r--src/core/hle/kernel/svc/svc_debug_string.cpp25
-rw-r--r--src/core/hle/kernel/svc/svc_device_address_space.cpp6
-rw-r--r--src/core/hle/kernel/svc/svc_event.cpp111
-rw-r--r--src/core/hle/kernel/svc/svc_exception.cpp121
-rw-r--r--src/core/hle/kernel/svc/svc_info.cpp282
-rw-r--r--src/core/hle/kernel/svc/svc_interrupt_event.cpp6
-rw-r--r--src/core/hle/kernel/svc/svc_io_pool.cpp6
-rw-r--r--src/core/hle/kernel/svc/svc_ipc.cpp89
-rw-r--r--src/core/hle/kernel/svc/svc_kernel_debug.cpp19
-rw-r--r--src/core/hle/kernel/svc/svc_light_ipc.cpp6
-rw-r--r--src/core/hle/kernel/svc/svc_lock.cpp57
-rw-r--r--src/core/hle/kernel/svc/svc_memory.cpp189
-rw-r--r--src/core/hle/kernel/svc/svc_physical_memory.cpp137
-rw-r--r--src/core/hle/kernel/svc/svc_port.cpp71
-rw-r--r--src/core/hle/kernel/svc/svc_power_management.cpp6
-rw-r--r--src/core/hle/kernel/svc/svc_process.cpp124
-rw-r--r--src/core/hle/kernel/svc/svc_process_memory.cpp274
-rw-r--r--src/core/hle/kernel/svc/svc_processor.cpp21
-rw-r--r--src/core/hle/kernel/svc/svc_query_memory.cpp55
-rw-r--r--src/core/hle/kernel/svc/svc_register.cpp6
-rw-r--r--src/core/hle/kernel/svc/svc_resource_limit.cpp95
-rw-r--r--src/core/hle/kernel/svc/svc_secure_monitor_call.cpp6
-rw-r--r--src/core/hle/kernel/svc/svc_session.cpp103
-rw-r--r--src/core/hle/kernel/svc/svc_shared_memory.cpp106
-rw-r--r--src/core/hle/kernel/svc/svc_synchronization.cpp139
-rw-r--r--src/core/hle/kernel/svc/svc_thread.cpp396
-rw-r--r--src/core/hle/kernel/svc/svc_thread_profiler.cpp6
-rw-r--r--src/core/hle/kernel/svc/svc_tick.cpp33
-rw-r--r--src/core/hle/kernel/svc/svc_transfer_memory.cpp79
-rw-r--r--src/core/hle/kernel/svc_types.h19
-rw-r--r--src/core/hle/kernel/svc_version.h58
-rw-r--r--src/core/hle/kernel/svc_wrap.h8
-rw-r--r--src/core/hle/service/am/am.cpp2
-rw-r--r--src/core/hle/service/audio/audren_u.cpp2
-rw-r--r--src/core/hle/service/audio/hwopus.cpp2
-rw-r--r--src/core/hle/service/es/es.cpp2
-rw-r--r--src/core/hle/service/filesystem/fsp_srv.cpp11
-rw-r--r--src/core/hle/service/glue/arp.cpp3
-rw-r--r--src/core/hle/service/hid/controllers/npad.cpp26
-rw-r--r--src/core/hle/service/hid/controllers/npad.h3
-rw-r--r--src/core/hle/service/hid/hid.cpp4
-rw-r--r--src/core/hle/service/hid/hidbus.cpp24
-rw-r--r--src/core/hle/service/hid/hidbus/hidbus_base.h3
-rw-r--r--src/core/hle/service/hid/hidbus/ringcon.cpp10
-rw-r--r--src/core/hle/service/hid/hidbus/ringcon.h7
-rw-r--r--src/core/hle/service/hid/hidbus/starlink.cpp2
-rw-r--r--src/core/hle/service/hid/hidbus/starlink.h2
-rw-r--r--src/core/hle/service/hid/hidbus/stubbed.cpp2
-rw-r--r--src/core/hle/service/hid/hidbus/stubbed.h2
-rw-r--r--src/core/hle/service/hid/irs.cpp18
-rw-r--r--src/core/hle/service/jit/jit.cpp4
-rw-r--r--src/core/hle/service/ldn/ldn.cpp4
-rw-r--r--src/core/hle/service/nfc/nfc_device.cpp7
-rw-r--r--src/core/hle/service/nfp/nfp_device.cpp7
-rw-r--r--src/core/hle/service/nvdrv/devices/nvdevice.h10
-rw-r--r--src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp8
-rw-r--r--src/core/hle/service/nvdrv/devices/nvdisp_disp0.h10
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp26
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_as_gpu.h28
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp21
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_ctrl.h22
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp31
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.h32
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp35
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_gpu.h36
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp8
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_nvdec.h10
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.cpp17
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.h14
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_nvjpg.cpp10
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_nvjpg.h12
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_vic.cpp8
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_vic.h10
-rw-r--r--src/core/hle/service/nvdrv/devices/nvmap.cpp20
-rw-r--r--src/core/hle/service/nvdrv/devices/nvmap.h22
-rw-r--r--src/core/hle/service/nvdrv/nvdrv.cpp8
-rw-r--r--src/core/hle/service/nvdrv/nvdrv.h12
-rw-r--r--src/core/hle/service/nvflinger/buffer_queue_producer.cpp4
-rw-r--r--src/core/hle/service/nvflinger/graphic_buffer_producer.cpp2
-rw-r--r--src/core/hle/service/nvflinger/graphic_buffer_producer.h4
-rw-r--r--src/core/hle/service/nvflinger/parcel.h87
-rw-r--r--src/core/hle/service/prepo/prepo.cpp8
-rw-r--r--src/core/hle/service/sockets/bsd.cpp15
-rw-r--r--src/core/hle/service/sockets/bsd.h23
-rw-r--r--src/core/hle/service/sockets/sfdnsres.cpp2
-rw-r--r--src/core/hle/service/ssl/ssl.cpp8
-rw-r--r--src/core/hle/service/vi/vi.cpp4
-rw-r--r--src/core/internal_network/network.cpp4
-rw-r--r--src/core/internal_network/socket_proxy.cpp4
-rw-r--r--src/core/internal_network/socket_proxy.h5
-rw-r--r--src/core/internal_network/sockets.h9
-rw-r--r--src/core/memory.cpp6
-rw-r--r--src/core/reporter.cpp2
-rw-r--r--src/core/reporter.h4
-rw-r--r--src/input_common/CMakeLists.txt21
-rw-r--r--src/input_common/drivers/camera.cpp4
-rw-r--r--src/input_common/drivers/camera.h4
-rw-r--r--src/input_common/drivers/gc_adapter.cpp10
-rw-r--r--src/input_common/drivers/gc_adapter.h2
-rw-r--r--src/input_common/drivers/joycon.cpp677
-rw-r--r--src/input_common/drivers/joycon.h111
-rw-r--r--src/input_common/drivers/sdl_driver.cpp77
-rw-r--r--src/input_common/drivers/sdl_driver.h2
-rw-r--r--src/input_common/drivers/virtual_amiibo.cpp11
-rw-r--r--src/input_common/drivers/virtual_amiibo.h2
-rw-r--r--src/input_common/helpers/joycon_driver.cpp575
-rw-r--r--src/input_common/helpers/joycon_driver.h150
-rw-r--r--src/input_common/helpers/joycon_protocol/calibration.cpp218
-rw-r--r--src/input_common/helpers/joycon_protocol/calibration.h82
-rw-r--r--src/input_common/helpers/joycon_protocol/common_protocol.cpp316
-rw-r--r--src/input_common/helpers/joycon_protocol/common_protocol.h201
-rw-r--r--src/input_common/helpers/joycon_protocol/generic_functions.cpp136
-rw-r--r--src/input_common/helpers/joycon_protocol/generic_functions.h114
-rw-r--r--src/input_common/helpers/joycon_protocol/irs.cpp299
-rw-r--r--src/input_common/helpers/joycon_protocol/irs.h63
-rw-r--r--src/input_common/helpers/joycon_protocol/joycon_types.h697
-rw-r--r--src/input_common/helpers/joycon_protocol/nfc.cpp406
-rw-r--r--src/input_common/helpers/joycon_protocol/nfc.h61
-rw-r--r--src/input_common/helpers/joycon_protocol/poller.cpp337
-rw-r--r--src/input_common/helpers/joycon_protocol/poller.h81
-rw-r--r--src/input_common/helpers/joycon_protocol/ringcon.cpp115
-rw-r--r--src/input_common/helpers/joycon_protocol/ringcon.h38
-rw-r--r--src/input_common/helpers/joycon_protocol/rumble.cpp299
-rw-r--r--src/input_common/helpers/joycon_protocol/rumble.h33
-rw-r--r--src/input_common/helpers/stick_from_buttons.cpp50
-rw-r--r--src/input_common/input_engine.cpp37
-rw-r--r--src/input_common/input_engine.h25
-rw-r--r--src/input_common/input_poller.cpp108
-rw-r--r--src/input_common/input_poller.h11
-rw-r--r--src/input_common/main.cpp14
-rw-r--r--src/shader_recompiler/backend/glasm/emit_glasm.cpp2
-rw-r--r--src/shader_recompiler/backend/glasm/emit_glasm_bitwise_conversion.cpp4
-rw-r--r--src/shader_recompiler/backend/glasm/emit_glasm_image.cpp23
-rw-r--r--src/shader_recompiler/backend/glasm/emit_glasm_instructions.h3
-rw-r--r--src/shader_recompiler/backend/glsl/emit_glsl_bitwise_conversion.cpp4
-rw-r--r--src/shader_recompiler/backend/glsl/emit_glsl_image.cpp53
-rw-r--r--src/shader_recompiler/backend/glsl/emit_glsl_instructions.h3
-rw-r--r--src/shader_recompiler/backend/glsl/glsl_emit_context.cpp49
-rw-r--r--src/shader_recompiler/backend/spirv/emit_spirv_bitwise_conversion.cpp4
-rw-r--r--src/shader_recompiler/backend/spirv/emit_spirv_image.cpp35
-rw-r--r--src/shader_recompiler/backend/spirv/emit_spirv_instructions.h4
-rw-r--r--src/shader_recompiler/backend/spirv/spirv_emit_context.cpp6
-rw-r--r--src/shader_recompiler/backend/spirv/spirv_emit_context.h1
-rw-r--r--src/shader_recompiler/frontend/ir/ir_emitter.cpp14
-rw-r--r--src/shader_recompiler/frontend/ir/ir_emitter.h8
-rw-r--r--src/shader_recompiler/frontend/ir/opcodes.h1
-rw-r--r--src/shader_recompiler/frontend/ir/opcodes.inc7
-rw-r--r--src/shader_recompiler/frontend/ir/type.h31
-rw-r--r--src/shader_recompiler/frontend/ir/value.cpp3
-rw-r--r--src/shader_recompiler/frontend/ir/value.h25
-rw-r--r--src/shader_recompiler/frontend/maxwell/translate/impl/texture_query.cpp8
-rw-r--r--src/shader_recompiler/ir_opt/texture_pass.cpp44
-rw-r--r--src/shader_recompiler/object_pool.h4
-rw-r--r--src/shader_recompiler/shader_info.h1
-rw-r--r--src/tests/CMakeLists.txt3
-rw-r--r--src/tests/common/bit_field.cpp2
-rw-r--r--src/tests/common/cityhash.cpp2
-rw-r--r--src/tests/common/fibers.cpp2
-rw-r--r--src/tests/common/host_memory.cpp2
-rw-r--r--src/tests/common/param_package.cpp2
-rw-r--r--src/tests/common/range_map.cpp2
-rw-r--r--src/tests/common/ring_buffer.cpp2
-rw-r--r--src/tests/common/scratch_buffer.cpp2
-rw-r--r--src/tests/common/unique_function.cpp2
-rw-r--r--src/tests/core/core_timing.cpp2
-rw-r--r--src/tests/core/internal_network/network.cpp2
-rw-r--r--src/tests/input_common/calibration_configuration_job.cpp2
-rw-r--r--src/tests/tests.cpp8
-rw-r--r--src/tests/video_core/buffer_base.cpp2
-rw-r--r--src/video_core/CMakeLists.txt6
-rw-r--r--src/video_core/engines/draw_manager.cpp31
-rw-r--r--src/video_core/engines/draw_manager.h20
-rw-r--r--src/video_core/engines/maxwell_3d.cpp1
-rw-r--r--src/video_core/engines/maxwell_3d.h16
-rw-r--r--src/video_core/fsr.cpp148
-rw-r--r--src/video_core/fsr.h19
-rw-r--r--src/video_core/host_shaders/CMakeLists.txt34
-rw-r--r--src/video_core/host_shaders/blit_color_float.frag (renamed from src/video_core/host_shaders/vulkan_blit_color_float.frag)0
-rw-r--r--src/video_core/host_shaders/full_screen_triangle.vert13
-rw-r--r--src/video_core/host_shaders/opengl_fidelityfx_fsr.frag108
-rw-r--r--src/video_core/host_shaders/opengl_fidelityfx_fsr_easu.frag9
-rw-r--r--src/video_core/host_shaders/opengl_fidelityfx_fsr_rcas.frag9
-rw-r--r--src/video_core/host_shaders/vulkan_color_clear.frag14
-rw-r--r--src/video_core/host_shaders/vulkan_color_clear.vert10
-rw-r--r--src/video_core/memory_manager.cpp40
-rw-r--r--src/video_core/memory_manager.h3
-rw-r--r--src/video_core/rasterizer_interface.h3
-rw-r--r--src/video_core/renderer_null/null_rasterizer.cpp1
-rw-r--r--src/video_core/renderer_null/null_rasterizer.h1
-rw-r--r--src/video_core/renderer_opengl/blit_image.cpp59
-rw-r--r--src/video_core/renderer_opengl/blit_image.h38
-rw-r--r--src/video_core/renderer_opengl/gl_compute_pipeline.cpp23
-rw-r--r--src/video_core/renderer_opengl/gl_compute_pipeline.h10
-rw-r--r--src/video_core/renderer_opengl/gl_device.cpp1
-rw-r--r--src/video_core/renderer_opengl/gl_device.h7
-rw-r--r--src/video_core/renderer_opengl/gl_fsr.cpp101
-rw-r--r--src/video_core/renderer_opengl/gl_fsr.h43
-rw-r--r--src/video_core/renderer_opengl/gl_graphics_pipeline.cpp7
-rw-r--r--src/video_core/renderer_opengl/gl_graphics_pipeline.h2
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer.cpp44
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer.h4
-rw-r--r--src/video_core/renderer_opengl/gl_shader_cache.cpp17
-rw-r--r--src/video_core/renderer_opengl/gl_shader_cache.h6
-rw-r--r--src/video_core/renderer_opengl/gl_shader_manager.cpp121
-rw-r--r--src/video_core/renderer_opengl/gl_shader_manager.h129
-rw-r--r--src/video_core/renderer_opengl/renderer_opengl.cpp90
-rw-r--r--src/video_core/renderer_opengl/renderer_opengl.h3
-rw-r--r--src/video_core/renderer_vulkan/blit_image.cpp212
-rw-r--r--src/video_core/renderer_vulkan/blit_image.h16
-rw-r--r--src/video_core/renderer_vulkan/vk_fsr.cpp144
-rw-r--r--src/video_core/renderer_vulkan/vk_rasterizer.cpp39
-rw-r--r--src/video_core/renderer_vulkan/vk_rasterizer.h1
-rw-r--r--src/video_core/texture_cache/descriptor_table.h4
-rw-r--r--src/video_core/texture_cache/image_info.cpp4
-rw-r--r--src/video_core/texture_cache/samples_helper.h44
-rw-r--r--src/video_core/texture_cache/slot_vector.h2
-rw-r--r--src/video_core/texture_cache/texture_cache.h7
-rw-r--r--src/video_core/texture_cache/texture_cache_base.h3
-rw-r--r--src/video_core/vulkan_common/nsight_aftermath_tracker.cpp8
-rw-r--r--src/yuzu/CMakeLists.txt2
-rw-r--r--src/yuzu/configuration/config.cpp51
-rw-r--r--src/yuzu/configuration/configuration_shared.cpp10
-rw-r--r--src/yuzu/configuration/configuration_shared.h3
-rw-r--r--src/yuzu/configuration/configure_dialog.cpp1
-rw-r--r--src/yuzu/configuration/configure_graphics.ui2
-rw-r--r--src/yuzu/configuration/configure_input_advanced.cpp2
-rw-r--r--src/yuzu/configuration/configure_input_advanced.ui22
-rw-r--r--src/yuzu/configuration/configure_input_player.cpp37
-rw-r--r--src/yuzu/configuration/configure_input_player_widget.cpp45
-rw-r--r--src/yuzu/configuration/configure_input_player_widget.h2
-rw-r--r--src/yuzu/configuration/configure_motion_touch.cpp1
-rw-r--r--src/yuzu/configuration/configure_per_game.cpp2
-rw-r--r--src/yuzu/configuration/configure_ringcon.cpp105
-rw-r--r--src/yuzu/configuration/configure_ringcon.h14
-rw-r--r--src/yuzu/configuration/configure_ringcon.ui396
-rw-r--r--src/yuzu/configuration/configure_system.cpp10
-rw-r--r--src/yuzu/configuration/configure_system.h6
-rw-r--r--src/yuzu/configuration/configure_tas.cpp1
-rw-r--r--src/yuzu/configuration/input_profiles.cpp7
-rw-r--r--src/yuzu/debugger/controller.cpp5
-rw-r--r--src/yuzu/debugger/profiler.cpp5
-rw-r--r--src/yuzu/discord_impl.cpp67
-rw-r--r--src/yuzu/hotkeys.cpp6
-rw-r--r--src/yuzu/hotkeys.h1
-rw-r--r--src/yuzu/install_dialog.cpp1
-rw-r--r--src/yuzu/main.cpp17
-rw-r--r--src/yuzu/multiplayer/direct_connect.cpp21
-rw-r--r--src/yuzu/multiplayer/direct_connect.ui23
-rw-r--r--src/yuzu/multiplayer/lobby.cpp16
-rw-r--r--src/yuzu/multiplayer/lobby.h2
-rw-r--r--src/yuzu/multiplayer/lobby.ui7
-rw-r--r--src/yuzu/multiplayer/validation.h25
-rw-r--r--src/yuzu/uisettings.h1
-rw-r--r--src/yuzu/util/limitable_input_dialog.cpp2
-rw-r--r--src/yuzu/util/sequence_dialog/sequence_dialog.cpp1
-rw-r--r--src/yuzu_cmd/config.cpp3
-rw-r--r--src/yuzu_cmd/default_ini.h40
-rw-r--r--src/yuzu_cmd/emu_window/emu_window_sdl2.cpp48
-rw-r--r--src/yuzu_cmd/emu_window/emu_window_sdl2.h10
-rw-r--r--src/yuzu_cmd/yuzu.cpp55
322 files changed, 13402 insertions, 4698 deletions
diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt
index 45332cf95..9884a4a0b 100644
--- a/src/common/CMakeLists.txt
+++ b/src/common/CMakeLists.txt
@@ -38,6 +38,8 @@ add_library(common STATIC
38 common_precompiled_headers.h 38 common_precompiled_headers.h
39 common_types.h 39 common_types.h
40 concepts.h 40 concepts.h
41 demangle.cpp
42 demangle.h
41 div_ceil.h 43 div_ceil.h
42 dynamic_library.cpp 44 dynamic_library.cpp
43 dynamic_library.h 45 dynamic_library.h
@@ -175,7 +177,7 @@ endif()
175create_target_directory_groups(common) 177create_target_directory_groups(common)
176 178
177target_link_libraries(common PUBLIC ${Boost_LIBRARIES} fmt::fmt microprofile Threads::Threads) 179target_link_libraries(common PUBLIC ${Boost_LIBRARIES} fmt::fmt microprofile Threads::Threads)
178target_link_libraries(common PRIVATE lz4::lz4 zstd::zstd) 180target_link_libraries(common PRIVATE lz4::lz4 zstd::zstd LLVM::Demangle)
179 181
180if (YUZU_USE_PRECOMPILED_HEADERS) 182if (YUZU_USE_PRECOMPILED_HEADERS)
181 target_precompile_headers(common PRIVATE precompiled_headers.h) 183 target_precompile_headers(common PRIVATE precompiled_headers.h)
diff --git a/src/common/address_space.h b/src/common/address_space.h
index 9222b2fdc..8683c23c3 100644
--- a/src/common/address_space.h
+++ b/src/common/address_space.h
@@ -12,7 +12,8 @@
12 12
13namespace Common { 13namespace Common {
14template <typename VaType, size_t AddressSpaceBits> 14template <typename VaType, size_t AddressSpaceBits>
15concept AddressSpaceValid = std::is_unsigned_v<VaType> && sizeof(VaType) * 8 >= AddressSpaceBits; 15concept AddressSpaceValid = std::is_unsigned_v<VaType> && sizeof(VaType) * 8 >=
16AddressSpaceBits;
16 17
17struct EmptyStruct {}; 18struct EmptyStruct {};
18 19
@@ -21,7 +22,7 @@ struct EmptyStruct {};
21 */ 22 */
22template <typename VaType, VaType UnmappedVa, typename PaType, PaType UnmappedPa, 23template <typename VaType, VaType UnmappedVa, typename PaType, PaType UnmappedPa,
23 bool PaContigSplit, size_t AddressSpaceBits, typename ExtraBlockInfo = EmptyStruct> 24 bool PaContigSplit, size_t AddressSpaceBits, typename ExtraBlockInfo = EmptyStruct>
24requires AddressSpaceValid<VaType, AddressSpaceBits> 25 requires AddressSpaceValid<VaType, AddressSpaceBits>
25class FlatAddressSpaceMap { 26class FlatAddressSpaceMap {
26public: 27public:
27 /// The maximum VA that this AS can technically reach 28 /// The maximum VA that this AS can technically reach
@@ -109,7 +110,7 @@ private:
109 * initial, fast linear pass and a subsequent slower pass that iterates until it finds a free block 110 * initial, fast linear pass and a subsequent slower pass that iterates until it finds a free block
110 */ 111 */
111template <typename VaType, VaType UnmappedVa, size_t AddressSpaceBits> 112template <typename VaType, VaType UnmappedVa, size_t AddressSpaceBits>
112requires AddressSpaceValid<VaType, AddressSpaceBits> 113 requires AddressSpaceValid<VaType, AddressSpaceBits>
113class FlatAllocator 114class FlatAllocator
114 : public FlatAddressSpaceMap<VaType, UnmappedVa, bool, false, false, AddressSpaceBits> { 115 : public FlatAddressSpaceMap<VaType, UnmappedVa, bool, false, false, AddressSpaceBits> {
115private: 116private:
diff --git a/src/common/alignment.h b/src/common/alignment.h
index 7e897334b..fa715d497 100644
--- a/src/common/alignment.h
+++ b/src/common/alignment.h
@@ -10,7 +10,7 @@
10namespace Common { 10namespace Common {
11 11
12template <typename T> 12template <typename T>
13requires std::is_unsigned_v<T> 13 requires std::is_unsigned_v<T>
14[[nodiscard]] constexpr T AlignUp(T value, size_t size) { 14[[nodiscard]] constexpr T AlignUp(T value, size_t size) {
15 auto mod{static_cast<T>(value % size)}; 15 auto mod{static_cast<T>(value % size)};
16 value -= mod; 16 value -= mod;
@@ -18,31 +18,31 @@ requires std::is_unsigned_v<T>
18} 18}
19 19
20template <typename T> 20template <typename T>
21requires std::is_unsigned_v<T> 21 requires std::is_unsigned_v<T>
22[[nodiscard]] constexpr T AlignUpLog2(T value, size_t align_log2) { 22[[nodiscard]] constexpr T AlignUpLog2(T value, size_t align_log2) {
23 return static_cast<T>((value + ((1ULL << align_log2) - 1)) >> align_log2 << align_log2); 23 return static_cast<T>((value + ((1ULL << align_log2) - 1)) >> align_log2 << align_log2);
24} 24}
25 25
26template <typename T> 26template <typename T>
27requires std::is_unsigned_v<T> 27 requires std::is_unsigned_v<T>
28[[nodiscard]] constexpr T AlignDown(T value, size_t size) { 28[[nodiscard]] constexpr T AlignDown(T value, size_t size) {
29 return static_cast<T>(value - value % size); 29 return static_cast<T>(value - value % size);
30} 30}
31 31
32template <typename T> 32template <typename T>
33requires std::is_unsigned_v<T> 33 requires std::is_unsigned_v<T>
34[[nodiscard]] constexpr bool Is4KBAligned(T value) { 34[[nodiscard]] constexpr bool Is4KBAligned(T value) {
35 return (value & 0xFFF) == 0; 35 return (value & 0xFFF) == 0;
36} 36}
37 37
38template <typename T> 38template <typename T>
39requires std::is_unsigned_v<T> 39 requires std::is_unsigned_v<T>
40[[nodiscard]] constexpr bool IsWordAligned(T value) { 40[[nodiscard]] constexpr bool IsWordAligned(T value) {
41 return (value & 0b11) == 0; 41 return (value & 0b11) == 0;
42} 42}
43 43
44template <typename T> 44template <typename T>
45requires std::is_integral_v<T> 45 requires std::is_integral_v<T>
46[[nodiscard]] constexpr bool IsAligned(T value, size_t alignment) { 46[[nodiscard]] constexpr bool IsAligned(T value, size_t alignment) {
47 using U = typename std::make_unsigned_t<T>; 47 using U = typename std::make_unsigned_t<T>;
48 const U mask = static_cast<U>(alignment - 1); 48 const U mask = static_cast<U>(alignment - 1);
@@ -50,7 +50,7 @@ requires std::is_integral_v<T>
50} 50}
51 51
52template <typename T, typename U> 52template <typename T, typename U>
53requires std::is_integral_v<T> 53 requires std::is_integral_v<T>
54[[nodiscard]] constexpr T DivideUp(T x, U y) { 54[[nodiscard]] constexpr T DivideUp(T x, U y) {
55 return (x + (y - 1)) / y; 55 return (x + (y - 1)) / y;
56} 56}
@@ -73,11 +73,11 @@ public:
73 constexpr AlignmentAllocator(const AlignmentAllocator<T2, Align>&) noexcept {} 73 constexpr AlignmentAllocator(const AlignmentAllocator<T2, Align>&) noexcept {}
74 74
75 [[nodiscard]] T* allocate(size_type n) { 75 [[nodiscard]] T* allocate(size_type n) {
76 return static_cast<T*>(::operator new (n * sizeof(T), std::align_val_t{Align})); 76 return static_cast<T*>(::operator new(n * sizeof(T), std::align_val_t{Align}));
77 } 77 }
78 78
79 void deallocate(T* p, size_type n) { 79 void deallocate(T* p, size_type n) {
80 ::operator delete (p, n * sizeof(T), std::align_val_t{Align}); 80 ::operator delete(p, n * sizeof(T), std::align_val_t{Align});
81 } 81 }
82 82
83 template <typename T2> 83 template <typename T2>
diff --git a/src/common/atomic_helpers.h b/src/common/atomic_helpers.h
index aef3b66a4..d997f10ba 100644
--- a/src/common/atomic_helpers.h
+++ b/src/common/atomic_helpers.h
@@ -75,7 +75,7 @@ extern "C" void AnnotateHappensAfter(const char*, int, void*);
75#if defined(AE_VCPP) || defined(AE_ICC) 75#if defined(AE_VCPP) || defined(AE_ICC)
76#define AE_FORCEINLINE __forceinline 76#define AE_FORCEINLINE __forceinline
77#elif defined(AE_GCC) 77#elif defined(AE_GCC)
78//#define AE_FORCEINLINE __attribute__((always_inline)) 78// #define AE_FORCEINLINE __attribute__((always_inline))
79#define AE_FORCEINLINE inline 79#define AE_FORCEINLINE inline
80#else 80#else
81#define AE_FORCEINLINE inline 81#define AE_FORCEINLINE inline
diff --git a/src/common/bit_util.h b/src/common/bit_util.h
index e4e6287f3..13368b439 100644
--- a/src/common/bit_util.h
+++ b/src/common/bit_util.h
@@ -45,19 +45,19 @@ template <typename T>
45} 45}
46 46
47template <typename T> 47template <typename T>
48requires std::is_unsigned_v<T> 48 requires std::is_unsigned_v<T>
49[[nodiscard]] constexpr bool IsPow2(T value) { 49[[nodiscard]] constexpr bool IsPow2(T value) {
50 return std::has_single_bit(value); 50 return std::has_single_bit(value);
51} 51}
52 52
53template <typename T> 53template <typename T>
54requires std::is_integral_v<T> 54 requires std::is_integral_v<T>
55[[nodiscard]] T NextPow2(T value) { 55[[nodiscard]] T NextPow2(T value) {
56 return static_cast<T>(1ULL << ((8U * sizeof(T)) - std::countl_zero(value - 1U))); 56 return static_cast<T>(1ULL << ((8U * sizeof(T)) - std::countl_zero(value - 1U)));
57} 57}
58 58
59template <size_t bit_index, typename T> 59template <size_t bit_index, typename T>
60requires std::is_integral_v<T> 60 requires std::is_integral_v<T>
61[[nodiscard]] constexpr bool Bit(const T value) { 61[[nodiscard]] constexpr bool Bit(const T value) {
62 static_assert(bit_index < BitSize<T>(), "bit_index must be smaller than size of T"); 62 static_assert(bit_index < BitSize<T>(), "bit_index must be smaller than size of T");
63 return ((value >> bit_index) & T(1)) == T(1); 63 return ((value >> bit_index) & T(1)) == T(1);
diff --git a/src/common/concepts.h b/src/common/concepts.h
index a9acff3e7..61df1d32a 100644
--- a/src/common/concepts.h
+++ b/src/common/concepts.h
@@ -16,9 +16,9 @@ concept IsContiguousContainer = std::contiguous_iterator<typename T::iterator>;
16// is available on all supported platforms. 16// is available on all supported platforms.
17template <typename Derived, typename Base> 17template <typename Derived, typename Base>
18concept DerivedFrom = requires { 18concept DerivedFrom = requires {
19 std::is_base_of_v<Base, Derived>; 19 std::is_base_of_v<Base, Derived>;
20 std::is_convertible_v<const volatile Derived*, const volatile Base*>; 20 std::is_convertible_v<const volatile Derived*, const volatile Base*>;
21}; 21 };
22 22
23// TODO: Replace with std::convertible_to when libc++ implements it. 23// TODO: Replace with std::convertible_to when libc++ implements it.
24template <typename From, typename To> 24template <typename From, typename To>
diff --git a/src/common/demangle.cpp b/src/common/demangle.cpp
new file mode 100644
index 000000000..3310faf86
--- /dev/null
+++ b/src/common/demangle.cpp
@@ -0,0 +1,35 @@
1// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include <llvm/Demangle/Demangle.h>
5
6#include "common/demangle.h"
7#include "common/scope_exit.h"
8
9namespace Common {
10
11std::string DemangleSymbol(const std::string& mangled) {
12 auto is_itanium = [](const std::string& name) -> bool {
13 // A valid Itanium encoding requires 1-4 leading underscores, followed by 'Z'.
14 auto pos = name.find_first_not_of('_');
15 return pos > 0 && pos <= 4 && pos < name.size() && name[pos] == 'Z';
16 };
17
18 if (mangled.empty()) {
19 return mangled;
20 }
21
22 char* demangled = nullptr;
23 SCOPE_EXIT({ std::free(demangled); });
24
25 if (is_itanium(mangled)) {
26 demangled = llvm::itaniumDemangle(mangled.c_str(), nullptr, nullptr, nullptr);
27 }
28
29 if (!demangled) {
30 return mangled;
31 }
32 return demangled;
33}
34
35} // namespace Common
diff --git a/src/common/demangle.h b/src/common/demangle.h
new file mode 100644
index 000000000..f072d22f3
--- /dev/null
+++ b/src/common/demangle.h
@@ -0,0 +1,12 @@
1// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include <string>
7
8namespace Common {
9
10std::string DemangleSymbol(const std::string& mangled);
11
12} // namespace Common
diff --git a/src/common/div_ceil.h b/src/common/div_ceil.h
index eebc279c2..c12477d42 100644
--- a/src/common/div_ceil.h
+++ b/src/common/div_ceil.h
@@ -10,14 +10,14 @@ namespace Common {
10 10
11/// Ceiled integer division. 11/// Ceiled integer division.
12template <typename N, typename D> 12template <typename N, typename D>
13requires std::is_integral_v<N> && std::is_unsigned_v<D> 13 requires std::is_integral_v<N> && std::is_unsigned_v<D>
14[[nodiscard]] constexpr N DivCeil(N number, D divisor) { 14[[nodiscard]] constexpr N DivCeil(N number, D divisor) {
15 return static_cast<N>((static_cast<D>(number) + divisor - 1) / divisor); 15 return static_cast<N>((static_cast<D>(number) + divisor - 1) / divisor);
16} 16}
17 17
18/// Ceiled integer division with logarithmic divisor in base 2 18/// Ceiled integer division with logarithmic divisor in base 2
19template <typename N, typename D> 19template <typename N, typename D>
20requires std::is_integral_v<N> && std::is_unsigned_v<D> 20 requires std::is_integral_v<N> && std::is_unsigned_v<D>
21[[nodiscard]] constexpr N DivCeilLog2(N value, D alignment_log2) { 21[[nodiscard]] constexpr N DivCeilLog2(N value, D alignment_log2) {
22 return static_cast<N>((static_cast<D>(value) + (D(1) << alignment_log2) - 1) >> alignment_log2); 22 return static_cast<N>((static_cast<D>(value) + (D(1) << alignment_log2) - 1) >> alignment_log2);
23} 23}
diff --git a/src/common/expected.h b/src/common/expected.h
index 6e6c86ee7..5fccfbcbd 100644
--- a/src/common/expected.h
+++ b/src/common/expected.h
@@ -64,7 +64,7 @@ struct no_init_t {
64 * Additionally, this requires E to be trivially destructible 64 * Additionally, this requires E to be trivially destructible
65 */ 65 */
66template <typename T, typename E, bool = std::is_trivially_destructible_v<T>> 66template <typename T, typename E, bool = std::is_trivially_destructible_v<T>>
67requires std::is_trivially_destructible_v<E> 67 requires std::is_trivially_destructible_v<E>
68struct expected_storage_base { 68struct expected_storage_base {
69 constexpr expected_storage_base() : m_val{T{}}, m_has_val{true} {} 69 constexpr expected_storage_base() : m_val{T{}}, m_has_val{true} {}
70 70
@@ -111,7 +111,7 @@ struct expected_storage_base {
111 * Additionally, this requires E to be trivially destructible 111 * Additionally, this requires E to be trivially destructible
112 */ 112 */
113template <typename T, typename E> 113template <typename T, typename E>
114requires std::is_trivially_destructible_v<E> 114 requires std::is_trivially_destructible_v<E>
115struct expected_storage_base<T, E, true> { 115struct expected_storage_base<T, E, true> {
116 constexpr expected_storage_base() : m_val{T{}}, m_has_val{true} {} 116 constexpr expected_storage_base() : m_val{T{}}, m_has_val{true} {}
117 117
@@ -251,7 +251,7 @@ struct expected_operations_base : expected_storage_base<T, E> {
251 * Additionally, this requires E to be trivially copy constructible 251 * Additionally, this requires E to be trivially copy constructible
252 */ 252 */
253template <typename T, typename E, bool = std::is_trivially_copy_constructible_v<T>> 253template <typename T, typename E, bool = std::is_trivially_copy_constructible_v<T>>
254requires std::is_trivially_copy_constructible_v<E> 254 requires std::is_trivially_copy_constructible_v<E>
255struct expected_copy_base : expected_operations_base<T, E> { 255struct expected_copy_base : expected_operations_base<T, E> {
256 using expected_operations_base<T, E>::expected_operations_base; 256 using expected_operations_base<T, E>::expected_operations_base;
257}; 257};
@@ -261,7 +261,7 @@ struct expected_copy_base : expected_operations_base<T, E> {
261 * Additionally, this requires E to be trivially copy constructible 261 * Additionally, this requires E to be trivially copy constructible
262 */ 262 */
263template <typename T, typename E> 263template <typename T, typename E>
264requires std::is_trivially_copy_constructible_v<E> 264 requires std::is_trivially_copy_constructible_v<E>
265struct expected_copy_base<T, E, false> : expected_operations_base<T, E> { 265struct expected_copy_base<T, E, false> : expected_operations_base<T, E> {
266 using expected_operations_base<T, E>::expected_operations_base; 266 using expected_operations_base<T, E>::expected_operations_base;
267 267
@@ -289,7 +289,7 @@ struct expected_copy_base<T, E, false> : expected_operations_base<T, E> {
289 * Additionally, this requires E to be trivially move constructible 289 * Additionally, this requires E to be trivially move constructible
290 */ 290 */
291template <typename T, typename E, bool = std::is_trivially_move_constructible_v<T>> 291template <typename T, typename E, bool = std::is_trivially_move_constructible_v<T>>
292requires std::is_trivially_move_constructible_v<E> 292 requires std::is_trivially_move_constructible_v<E>
293struct expected_move_base : expected_copy_base<T, E> { 293struct expected_move_base : expected_copy_base<T, E> {
294 using expected_copy_base<T, E>::expected_copy_base; 294 using expected_copy_base<T, E>::expected_copy_base;
295}; 295};
@@ -299,7 +299,7 @@ struct expected_move_base : expected_copy_base<T, E> {
299 * Additionally, this requires E to be trivially move constructible 299 * Additionally, this requires E to be trivially move constructible
300 */ 300 */
301template <typename T, typename E> 301template <typename T, typename E>
302requires std::is_trivially_move_constructible_v<E> 302 requires std::is_trivially_move_constructible_v<E>
303struct expected_move_base<T, E, false> : expected_copy_base<T, E> { 303struct expected_move_base<T, E, false> : expected_copy_base<T, E> {
304 using expected_copy_base<T, E>::expected_copy_base; 304 using expected_copy_base<T, E>::expected_copy_base;
305 305
@@ -330,9 +330,9 @@ template <typename T, typename E,
330 bool = std::conjunction_v<std::is_trivially_copy_assignable<T>, 330 bool = std::conjunction_v<std::is_trivially_copy_assignable<T>,
331 std::is_trivially_copy_constructible<T>, 331 std::is_trivially_copy_constructible<T>,
332 std::is_trivially_destructible<T>>> 332 std::is_trivially_destructible<T>>>
333requires std::conjunction_v<std::is_trivially_copy_assignable<E>, 333 requires std::conjunction_v<std::is_trivially_copy_assignable<E>,
334 std::is_trivially_copy_constructible<E>, 334 std::is_trivially_copy_constructible<E>,
335 std::is_trivially_destructible<E>> 335 std::is_trivially_destructible<E>>
336struct expected_copy_assign_base : expected_move_base<T, E> { 336struct expected_copy_assign_base : expected_move_base<T, E> {
337 using expected_move_base<T, E>::expected_move_base; 337 using expected_move_base<T, E>::expected_move_base;
338}; 338};
@@ -342,9 +342,9 @@ struct expected_copy_assign_base : expected_move_base<T, E> {
342 * Additionally, this requires E to be trivially copy assignable 342 * Additionally, this requires E to be trivially copy assignable
343 */ 343 */
344template <typename T, typename E> 344template <typename T, typename E>
345requires std::conjunction_v<std::is_trivially_copy_assignable<E>, 345 requires std::conjunction_v<std::is_trivially_copy_assignable<E>,
346 std::is_trivially_copy_constructible<E>, 346 std::is_trivially_copy_constructible<E>,
347 std::is_trivially_destructible<E>> 347 std::is_trivially_destructible<E>>
348struct expected_copy_assign_base<T, E, false> : expected_move_base<T, E> { 348struct expected_copy_assign_base<T, E, false> : expected_move_base<T, E> {
349 using expected_move_base<T, E>::expected_move_base; 349 using expected_move_base<T, E>::expected_move_base;
350 350
@@ -371,9 +371,9 @@ template <typename T, typename E,
371 bool = std::conjunction_v<std::is_trivially_move_assignable<T>, 371 bool = std::conjunction_v<std::is_trivially_move_assignable<T>,
372 std::is_trivially_move_constructible<T>, 372 std::is_trivially_move_constructible<T>,
373 std::is_trivially_destructible<T>>> 373 std::is_trivially_destructible<T>>>
374requires std::conjunction_v<std::is_trivially_move_assignable<E>, 374 requires std::conjunction_v<std::is_trivially_move_assignable<E>,
375 std::is_trivially_move_constructible<E>, 375 std::is_trivially_move_constructible<E>,
376 std::is_trivially_destructible<E>> 376 std::is_trivially_destructible<E>>
377struct expected_move_assign_base : expected_copy_assign_base<T, E> { 377struct expected_move_assign_base : expected_copy_assign_base<T, E> {
378 using expected_copy_assign_base<T, E>::expected_copy_assign_base; 378 using expected_copy_assign_base<T, E>::expected_copy_assign_base;
379}; 379};
@@ -383,9 +383,9 @@ struct expected_move_assign_base : expected_copy_assign_base<T, E> {
383 * Additionally, this requires E to be trivially move assignable 383 * Additionally, this requires E to be trivially move assignable
384 */ 384 */
385template <typename T, typename E> 385template <typename T, typename E>
386requires std::conjunction_v<std::is_trivially_move_assignable<E>, 386 requires std::conjunction_v<std::is_trivially_move_assignable<E>,
387 std::is_trivially_move_constructible<E>, 387 std::is_trivially_move_constructible<E>,
388 std::is_trivially_destructible<E>> 388 std::is_trivially_destructible<E>>
389struct expected_move_assign_base<T, E, false> : expected_copy_assign_base<T, E> { 389struct expected_move_assign_base<T, E, false> : expected_copy_assign_base<T, E> {
390 using expected_copy_assign_base<T, E>::expected_copy_assign_base; 390 using expected_copy_assign_base<T, E>::expected_copy_assign_base;
391 391
@@ -412,7 +412,7 @@ struct expected_move_assign_base<T, E, false> : expected_copy_assign_base<T, E>
412 */ 412 */
413template <typename T, typename E, bool EnableCopy = std::is_copy_constructible_v<T>, 413template <typename T, typename E, bool EnableCopy = std::is_copy_constructible_v<T>,
414 bool EnableMove = std::is_move_constructible_v<T>> 414 bool EnableMove = std::is_move_constructible_v<T>>
415requires std::conjunction_v<std::is_copy_constructible<E>, std::is_move_constructible<E>> 415 requires std::conjunction_v<std::is_copy_constructible<E>, std::is_move_constructible<E>>
416struct expected_delete_ctor_base { 416struct expected_delete_ctor_base {
417 expected_delete_ctor_base() = default; 417 expected_delete_ctor_base() = default;
418 expected_delete_ctor_base(const expected_delete_ctor_base&) = default; 418 expected_delete_ctor_base(const expected_delete_ctor_base&) = default;
@@ -422,7 +422,7 @@ struct expected_delete_ctor_base {
422}; 422};
423 423
424template <typename T, typename E> 424template <typename T, typename E>
425requires std::conjunction_v<std::is_copy_constructible<E>, std::is_move_constructible<E>> 425 requires std::conjunction_v<std::is_copy_constructible<E>, std::is_move_constructible<E>>
426struct expected_delete_ctor_base<T, E, true, false> { 426struct expected_delete_ctor_base<T, E, true, false> {
427 expected_delete_ctor_base() = default; 427 expected_delete_ctor_base() = default;
428 expected_delete_ctor_base(const expected_delete_ctor_base&) = default; 428 expected_delete_ctor_base(const expected_delete_ctor_base&) = default;
@@ -432,7 +432,7 @@ struct expected_delete_ctor_base<T, E, true, false> {
432}; 432};
433 433
434template <typename T, typename E> 434template <typename T, typename E>
435requires std::conjunction_v<std::is_copy_constructible<E>, std::is_move_constructible<E>> 435 requires std::conjunction_v<std::is_copy_constructible<E>, std::is_move_constructible<E>>
436struct expected_delete_ctor_base<T, E, false, true> { 436struct expected_delete_ctor_base<T, E, false, true> {
437 expected_delete_ctor_base() = default; 437 expected_delete_ctor_base() = default;
438 expected_delete_ctor_base(const expected_delete_ctor_base&) = delete; 438 expected_delete_ctor_base(const expected_delete_ctor_base&) = delete;
@@ -442,7 +442,7 @@ struct expected_delete_ctor_base<T, E, false, true> {
442}; 442};
443 443
444template <typename T, typename E> 444template <typename T, typename E>
445requires std::conjunction_v<std::is_copy_constructible<E>, std::is_move_constructible<E>> 445 requires std::conjunction_v<std::is_copy_constructible<E>, std::is_move_constructible<E>>
446struct expected_delete_ctor_base<T, E, false, false> { 446struct expected_delete_ctor_base<T, E, false, false> {
447 expected_delete_ctor_base() = default; 447 expected_delete_ctor_base() = default;
448 expected_delete_ctor_base(const expected_delete_ctor_base&) = delete; 448 expected_delete_ctor_base(const expected_delete_ctor_base&) = delete;
@@ -460,8 +460,8 @@ template <
460 typename T, typename E, 460 typename T, typename E,
461 bool EnableCopy = std::conjunction_v<std::is_copy_constructible<T>, std::is_copy_assignable<T>>, 461 bool EnableCopy = std::conjunction_v<std::is_copy_constructible<T>, std::is_copy_assignable<T>>,
462 bool EnableMove = std::conjunction_v<std::is_move_constructible<T>, std::is_move_assignable<T>>> 462 bool EnableMove = std::conjunction_v<std::is_move_constructible<T>, std::is_move_assignable<T>>>
463requires std::conjunction_v<std::is_copy_constructible<E>, std::is_move_constructible<E>, 463 requires std::conjunction_v<std::is_copy_constructible<E>, std::is_move_constructible<E>,
464 std::is_copy_assignable<E>, std::is_move_assignable<E>> 464 std::is_copy_assignable<E>, std::is_move_assignable<E>>
465struct expected_delete_assign_base { 465struct expected_delete_assign_base {
466 expected_delete_assign_base() = default; 466 expected_delete_assign_base() = default;
467 expected_delete_assign_base(const expected_delete_assign_base&) = default; 467 expected_delete_assign_base(const expected_delete_assign_base&) = default;
@@ -471,8 +471,8 @@ struct expected_delete_assign_base {
471}; 471};
472 472
473template <typename T, typename E> 473template <typename T, typename E>
474requires std::conjunction_v<std::is_copy_constructible<E>, std::is_move_constructible<E>, 474 requires std::conjunction_v<std::is_copy_constructible<E>, std::is_move_constructible<E>,
475 std::is_copy_assignable<E>, std::is_move_assignable<E>> 475 std::is_copy_assignable<E>, std::is_move_assignable<E>>
476struct expected_delete_assign_base<T, E, true, false> { 476struct expected_delete_assign_base<T, E, true, false> {
477 expected_delete_assign_base() = default; 477 expected_delete_assign_base() = default;
478 expected_delete_assign_base(const expected_delete_assign_base&) = default; 478 expected_delete_assign_base(const expected_delete_assign_base&) = default;
@@ -482,8 +482,8 @@ struct expected_delete_assign_base<T, E, true, false> {
482}; 482};
483 483
484template <typename T, typename E> 484template <typename T, typename E>
485requires std::conjunction_v<std::is_copy_constructible<E>, std::is_move_constructible<E>, 485 requires std::conjunction_v<std::is_copy_constructible<E>, std::is_move_constructible<E>,
486 std::is_copy_assignable<E>, std::is_move_assignable<E>> 486 std::is_copy_assignable<E>, std::is_move_assignable<E>>
487struct expected_delete_assign_base<T, E, false, true> { 487struct expected_delete_assign_base<T, E, false, true> {
488 expected_delete_assign_base() = default; 488 expected_delete_assign_base() = default;
489 expected_delete_assign_base(const expected_delete_assign_base&) = default; 489 expected_delete_assign_base(const expected_delete_assign_base&) = default;
@@ -493,8 +493,8 @@ struct expected_delete_assign_base<T, E, false, true> {
493}; 493};
494 494
495template <typename T, typename E> 495template <typename T, typename E>
496requires std::conjunction_v<std::is_copy_constructible<E>, std::is_move_constructible<E>, 496 requires std::conjunction_v<std::is_copy_constructible<E>, std::is_move_constructible<E>,
497 std::is_copy_assignable<E>, std::is_move_assignable<E>> 497 std::is_copy_assignable<E>, std::is_move_assignable<E>>
498struct expected_delete_assign_base<T, E, false, false> { 498struct expected_delete_assign_base<T, E, false, false> {
499 expected_delete_assign_base() = default; 499 expected_delete_assign_base() = default;
500 expected_delete_assign_base(const expected_delete_assign_base&) = default; 500 expected_delete_assign_base(const expected_delete_assign_base&) = default;
diff --git a/src/common/input.h b/src/common/input.h
index d27b1d772..b5748a6c8 100644
--- a/src/common/input.h
+++ b/src/common/input.h
@@ -51,6 +51,8 @@ enum class PollingMode {
51 NFC, 51 NFC,
52 // Enable infrared camera polling 52 // Enable infrared camera polling
53 IR, 53 IR,
54 // Enable ring controller polling
55 Ring,
54}; 56};
55 57
56enum class CameraFormat { 58enum class CameraFormat {
@@ -62,21 +64,22 @@ enum class CameraFormat {
62 None, 64 None,
63}; 65};
64 66
65// Vibration reply from the controller 67// Different results that can happen from a device request
66enum class VibrationError { 68enum class DriverResult {
67 None, 69 Success,
70 WrongReply,
71 Timeout,
72 UnsupportedControllerType,
73 HandleInUse,
74 ErrorReadingData,
75 ErrorWritingData,
76 NoDeviceDetected,
77 InvalidHandle,
68 NotSupported, 78 NotSupported,
69 Disabled, 79 Disabled,
70 Unknown, 80 Unknown,
71}; 81};
72 82
73// Polling mode reply from the controller
74enum class PollingError {
75 None,
76 NotSupported,
77 Unknown,
78};
79
80// Nfc reply from the controller 83// Nfc reply from the controller
81enum class NfcState { 84enum class NfcState {
82 Success, 85 Success,
@@ -90,13 +93,6 @@ enum class NfcState {
90 Unknown, 93 Unknown,
91}; 94};
92 95
93// Ir camera reply from the controller
94enum class CameraError {
95 None,
96 NotSupported,
97 Unknown,
98};
99
100// Hint for amplification curve to be used 96// Hint for amplification curve to be used
101enum class VibrationAmplificationType { 97enum class VibrationAmplificationType {
102 Linear, 98 Linear,
@@ -134,6 +130,8 @@ struct ButtonStatus {
134 bool inverted{}; 130 bool inverted{};
135 // Press once to activate, press again to release 131 // Press once to activate, press again to release
136 bool toggle{}; 132 bool toggle{};
133 // Spams the button when active
134 bool turbo{};
137 // Internal lock for the toggle status 135 // Internal lock for the toggle status
138 bool locked{}; 136 bool locked{};
139}; 137};
@@ -190,6 +188,8 @@ struct TouchStatus {
190struct BodyColorStatus { 188struct BodyColorStatus {
191 u32 body{}; 189 u32 body{};
192 u32 buttons{}; 190 u32 buttons{};
191 u32 left_grip{};
192 u32 right_grip{};
193}; 193};
194 194
195// HD rumble data 195// HD rumble data
@@ -228,17 +228,31 @@ enum class ButtonNames {
228 Engine, 228 Engine,
229 // This will display the button by value instead of the button name 229 // This will display the button by value instead of the button name
230 Value, 230 Value,
231
232 // Joycon button names
231 ButtonLeft, 233 ButtonLeft,
232 ButtonRight, 234 ButtonRight,
233 ButtonDown, 235 ButtonDown,
234 ButtonUp, 236 ButtonUp,
235 TriggerZ,
236 TriggerR,
237 TriggerL,
238 ButtonA, 237 ButtonA,
239 ButtonB, 238 ButtonB,
240 ButtonX, 239 ButtonX,
241 ButtonY, 240 ButtonY,
241 ButtonPlus,
242 ButtonMinus,
243 ButtonHome,
244 ButtonCapture,
245 ButtonStickL,
246 ButtonStickR,
247 TriggerL,
248 TriggerZL,
249 TriggerSL,
250 TriggerR,
251 TriggerZR,
252 TriggerSR,
253
254 // GC button names
255 TriggerZ,
242 ButtonStart, 256 ButtonStart,
243 257
244 // DS4 button names 258 // DS4 button names
@@ -316,22 +330,24 @@ class OutputDevice {
316public: 330public:
317 virtual ~OutputDevice() = default; 331 virtual ~OutputDevice() = default;
318 332
319 virtual void SetLED([[maybe_unused]] const LedStatus& led_status) {} 333 virtual DriverResult SetLED([[maybe_unused]] const LedStatus& led_status) {
334 return DriverResult::NotSupported;
335 }
320 336
321 virtual VibrationError SetVibration([[maybe_unused]] const VibrationStatus& vibration_status) { 337 virtual DriverResult SetVibration([[maybe_unused]] const VibrationStatus& vibration_status) {
322 return VibrationError::NotSupported; 338 return DriverResult::NotSupported;
323 } 339 }
324 340
325 virtual bool IsVibrationEnabled() { 341 virtual bool IsVibrationEnabled() {
326 return false; 342 return false;
327 } 343 }
328 344
329 virtual PollingError SetPollingMode([[maybe_unused]] PollingMode polling_mode) { 345 virtual DriverResult SetPollingMode([[maybe_unused]] PollingMode polling_mode) {
330 return PollingError::NotSupported; 346 return DriverResult::NotSupported;
331 } 347 }
332 348
333 virtual CameraError SetCameraFormat([[maybe_unused]] CameraFormat camera_format) { 349 virtual DriverResult SetCameraFormat([[maybe_unused]] CameraFormat camera_format) {
334 return CameraError::NotSupported; 350 return DriverResult::NotSupported;
335 } 351 }
336 352
337 virtual NfcState SupportsNfc() const { 353 virtual NfcState SupportsNfc() const {
diff --git a/src/common/intrusive_red_black_tree.h b/src/common/intrusive_red_black_tree.h
index 93046615e..5f6b34e82 100644
--- a/src/common/intrusive_red_black_tree.h
+++ b/src/common/intrusive_red_black_tree.h
@@ -242,19 +242,21 @@ public:
242 242
243template <typename T> 243template <typename T>
244concept HasRedBlackKeyType = requires { 244concept HasRedBlackKeyType = requires {
245 { std::is_same<typename T::RedBlackKeyType, void>::value } -> std::convertible_to<bool>; 245 {
246}; 246 std::is_same<typename T::RedBlackKeyType, void>::value
247 } -> std::convertible_to<bool>;
248 };
247 249
248namespace impl { 250namespace impl {
249 251
250 template <typename T, typename Default> 252template <typename T, typename Default>
251 consteval auto* GetRedBlackKeyType() { 253consteval auto* GetRedBlackKeyType() {
252 if constexpr (HasRedBlackKeyType<T>) { 254 if constexpr (HasRedBlackKeyType<T>) {
253 return static_cast<typename T::RedBlackKeyType*>(nullptr); 255 return static_cast<typename T::RedBlackKeyType*>(nullptr);
254 } else { 256 } else {
255 return static_cast<Default*>(nullptr); 257 return static_cast<Default*>(nullptr);
256 }
257 } 258 }
259}
258 260
259} // namespace impl 261} // namespace impl
260 262
diff --git a/src/common/make_unique_for_overwrite.h b/src/common/make_unique_for_overwrite.h
index c7413cf51..17f81bba4 100644
--- a/src/common/make_unique_for_overwrite.h
+++ b/src/common/make_unique_for_overwrite.h
@@ -9,17 +9,19 @@
9namespace Common { 9namespace Common {
10 10
11template <class T> 11template <class T>
12requires(!std::is_array_v<T>) std::unique_ptr<T> make_unique_for_overwrite() { 12 requires(!std::is_array_v<T>)
13std::unique_ptr<T> make_unique_for_overwrite() {
13 return std::unique_ptr<T>(new T); 14 return std::unique_ptr<T>(new T);
14} 15}
15 16
16template <class T> 17template <class T>
17requires std::is_unbounded_array_v<T> std::unique_ptr<T> make_unique_for_overwrite(std::size_t n) { 18 requires std::is_unbounded_array_v<T>
19std::unique_ptr<T> make_unique_for_overwrite(std::size_t n) {
18 return std::unique_ptr<T>(new std::remove_extent_t<T>[n]); 20 return std::unique_ptr<T>(new std::remove_extent_t<T>[n]);
19} 21}
20 22
21template <class T, class... Args> 23template <class T, class... Args>
22requires std::is_bounded_array_v<T> 24 requires std::is_bounded_array_v<T>
23void make_unique_for_overwrite(Args&&...) = delete; 25void make_unique_for_overwrite(Args&&...) = delete;
24 26
25} // namespace Common 27} // namespace Common
diff --git a/src/common/polyfill_ranges.h b/src/common/polyfill_ranges.h
index ca44bfaef..512dbcbcb 100644
--- a/src/common/polyfill_ranges.h
+++ b/src/common/polyfill_ranges.h
@@ -18,9 +18,9 @@ namespace ranges {
18 18
19template <typename T> 19template <typename T>
20concept range = requires(T& t) { 20concept range = requires(T& t) {
21 begin(t); 21 begin(t);
22 end(t); 22 end(t);
23}; 23 };
24 24
25template <typename T> 25template <typename T>
26concept input_range = range<T>; 26concept input_range = range<T>;
@@ -421,7 +421,7 @@ struct generate_fn {
421 } 421 }
422 422
423 template <typename R, std::copy_constructible F> 423 template <typename R, std::copy_constructible F>
424 requires std::invocable<F&> && ranges::output_range<R> 424 requires std::invocable<F&> && ranges::output_range<R>
425 constexpr ranges::iterator_t<R> operator()(R&& r, F gen) const { 425 constexpr ranges::iterator_t<R> operator()(R&& r, F gen) const {
426 return operator()(ranges::begin(r), ranges::end(r), std::move(gen)); 426 return operator()(ranges::begin(r), ranges::end(r), std::move(gen));
427 } 427 }
diff --git a/src/common/polyfill_thread.h b/src/common/polyfill_thread.h
index 5a8d1ce08..b5ef055db 100644
--- a/src/common/polyfill_thread.h
+++ b/src/common/polyfill_thread.h
@@ -11,6 +11,8 @@
11 11
12#ifdef __cpp_lib_jthread 12#ifdef __cpp_lib_jthread
13 13
14#include <chrono>
15#include <condition_variable>
14#include <stop_token> 16#include <stop_token>
15#include <thread> 17#include <thread>
16 18
@@ -21,23 +23,36 @@ void CondvarWait(Condvar& cv, Lock& lock, std::stop_token token, Pred&& pred) {
21 cv.wait(lock, token, std::move(pred)); 23 cv.wait(lock, token, std::move(pred));
22} 24}
23 25
26template <typename Rep, typename Period>
27bool StoppableTimedWait(std::stop_token token, const std::chrono::duration<Rep, Period>& rel_time) {
28 std::condition_variable_any cv;
29 std::mutex m;
30
31 // Perform the timed wait.
32 std::unique_lock lk{m};
33 return !cv.wait_for(lk, token, rel_time, [&] { return token.stop_requested(); });
34}
35
24} // namespace Common 36} // namespace Common
25 37
26#else 38#else
27 39
28#include <atomic> 40#include <atomic>
41#include <chrono>
42#include <condition_variable>
29#include <functional> 43#include <functional>
30#include <list> 44#include <map>
31#include <memory> 45#include <memory>
32#include <mutex> 46#include <mutex>
33#include <optional> 47#include <optional>
34#include <thread> 48#include <thread>
35#include <type_traits> 49#include <type_traits>
50#include <utility>
36 51
37namespace std { 52namespace std {
38namespace polyfill { 53namespace polyfill {
39 54
40using stop_state_callbacks = list<function<void()>>; 55using stop_state_callback = size_t;
41 56
42class stop_state { 57class stop_state {
43public: 58public:
@@ -45,61 +60,69 @@ public:
45 ~stop_state() = default; 60 ~stop_state() = default;
46 61
47 bool request_stop() { 62 bool request_stop() {
48 stop_state_callbacks callbacks; 63 unique_lock lk{m_lock};
49 64
50 { 65 if (m_stop_requested) {
51 scoped_lock lk{m_lock}; 66 // Already set, nothing to do.
67 return false;
68 }
52 69
53 if (m_stop_requested.load()) { 70 // Mark stop requested.
54 // Already set, nothing to do 71 m_stop_requested = true;
55 return false;
56 }
57 72
58 // Set as requested 73 while (!m_callbacks.empty()) {
59 m_stop_requested = true; 74 // Get an iterator to the first element.
75 const auto it = m_callbacks.begin();
60 76
61 // Copy callback list 77 // Move the callback function out of the map.
62 callbacks = m_callbacks; 78 function<void()> f;
63 } 79 swap(it->second, f);
80
81 // Erase the now-empty map element.
82 m_callbacks.erase(it);
64 83
65 for (auto callback : callbacks) { 84 // Run the callback.
66 callback(); 85 if (f) {
86 f();
87 }
67 } 88 }
68 89
69 return true; 90 return true;
70 } 91 }
71 92
72 bool stop_requested() const { 93 bool stop_requested() const {
73 return m_stop_requested.load(); 94 unique_lock lk{m_lock};
95 return m_stop_requested;
74 } 96 }
75 97
76 stop_state_callbacks::const_iterator insert_callback(function<void()> f) { 98 stop_state_callback insert_callback(function<void()> f) {
77 stop_state_callbacks::const_iterator ret{}; 99 unique_lock lk{m_lock};
78 bool should_run{};
79
80 {
81 scoped_lock lk{m_lock};
82 should_run = m_stop_requested.load();
83 m_callbacks.push_front(f);
84 ret = m_callbacks.begin();
85 }
86 100
87 if (should_run) { 101 if (m_stop_requested) {
88 f(); 102 // Stop already requested. Don't insert anything,
103 // just run the callback synchronously.
104 if (f) {
105 f();
106 }
107 return 0;
89 } 108 }
90 109
110 // Insert the callback.
111 stop_state_callback ret = ++m_next_callback;
112 m_callbacks.emplace(ret, move(f));
91 return ret; 113 return ret;
92 } 114 }
93 115
94 void remove_callback(stop_state_callbacks::const_iterator it) { 116 void remove_callback(stop_state_callback cb) {
95 scoped_lock lk{m_lock}; 117 unique_lock lk{m_lock};
96 m_callbacks.erase(it); 118 m_callbacks.erase(cb);
97 } 119 }
98 120
99private: 121private:
100 mutex m_lock; 122 mutable recursive_mutex m_lock;
101 atomic<bool> m_stop_requested; 123 map<stop_state_callback, function<void()>> m_callbacks;
102 stop_state_callbacks m_callbacks; 124 stop_state_callback m_next_callback{0};
125 bool m_stop_requested{false};
103}; 126};
104 127
105} // namespace polyfill 128} // namespace polyfill
@@ -190,7 +213,7 @@ public:
190 using callback_type = Callback; 213 using callback_type = Callback;
191 214
192 template <typename C> 215 template <typename C>
193 requires constructible_from<Callback, C> 216 requires constructible_from<Callback, C>
194 explicit stop_callback(const stop_token& st, 217 explicit stop_callback(const stop_token& st,
195 C&& cb) noexcept(is_nothrow_constructible_v<Callback, C>) 218 C&& cb) noexcept(is_nothrow_constructible_v<Callback, C>)
196 : m_stop_state(st.m_stop_state) { 219 : m_stop_state(st.m_stop_state) {
@@ -199,7 +222,7 @@ public:
199 } 222 }
200 } 223 }
201 template <typename C> 224 template <typename C>
202 requires constructible_from<Callback, C> 225 requires constructible_from<Callback, C>
203 explicit stop_callback(stop_token&& st, 226 explicit stop_callback(stop_token&& st,
204 C&& cb) noexcept(is_nothrow_constructible_v<Callback, C>) 227 C&& cb) noexcept(is_nothrow_constructible_v<Callback, C>)
205 : m_stop_state(move(st.m_stop_state)) { 228 : m_stop_state(move(st.m_stop_state)) {
@@ -209,7 +232,7 @@ public:
209 } 232 }
210 ~stop_callback() { 233 ~stop_callback() {
211 if (m_stop_state && m_callback) { 234 if (m_stop_state && m_callback) {
212 m_stop_state->remove_callback(*m_callback); 235 m_stop_state->remove_callback(m_callback);
213 } 236 }
214 } 237 }
215 238
@@ -220,7 +243,7 @@ public:
220 243
221private: 244private:
222 shared_ptr<polyfill::stop_state> m_stop_state; 245 shared_ptr<polyfill::stop_state> m_stop_state;
223 optional<polyfill::stop_state_callbacks::const_iterator> m_callback; 246 polyfill::stop_state_callback m_callback;
224}; 247};
225 248
226template <typename Callback> 249template <typename Callback>
@@ -318,6 +341,28 @@ void CondvarWait(Condvar& cv, Lock& lock, std::stop_token token, Pred pred) {
318 cv.wait(lock, [&] { return pred() || token.stop_requested(); }); 341 cv.wait(lock, [&] { return pred() || token.stop_requested(); });
319} 342}
320 343
344template <typename Rep, typename Period>
345bool StoppableTimedWait(std::stop_token token, const std::chrono::duration<Rep, Period>& rel_time) {
346 if (token.stop_requested()) {
347 return false;
348 }
349
350 bool stop_requested = false;
351 std::condition_variable cv;
352 std::mutex m;
353
354 std::stop_callback cb(token, [&] {
355 // Wake up the waiting thread.
356 std::unique_lock lk{m};
357 stop_requested = true;
358 cv.notify_one();
359 });
360
361 // Perform the timed wait.
362 std::unique_lock lk{m};
363 return !cv.wait_for(lk, rel_time, [&] { return stop_requested; });
364}
365
321} // namespace Common 366} // namespace Common
322 367
323#endif 368#endif
diff --git a/src/common/settings.h b/src/common/settings.h
index 80b2eeabc..64db66f37 100644
--- a/src/common/settings.h
+++ b/src/common/settings.h
@@ -131,7 +131,8 @@ public:
131 * @param default_val Intial value of the setting, and default value of the setting 131 * @param default_val Intial value of the setting, and default value of the setting
132 * @param name Label for the setting 132 * @param name Label for the setting
133 */ 133 */
134 explicit Setting(const Type& default_val, const std::string& name) requires(!ranged) 134 explicit Setting(const Type& default_val, const std::string& name)
135 requires(!ranged)
135 : value{default_val}, default_value{default_val}, label{name} {} 136 : value{default_val}, default_value{default_val}, label{name} {}
136 virtual ~Setting() = default; 137 virtual ~Setting() = default;
137 138
@@ -144,7 +145,8 @@ public:
144 * @param name Label for the setting 145 * @param name Label for the setting
145 */ 146 */
146 explicit Setting(const Type& default_val, const Type& min_val, const Type& max_val, 147 explicit Setting(const Type& default_val, const Type& min_val, const Type& max_val,
147 const std::string& name) requires(ranged) 148 const std::string& name)
149 requires(ranged)
148 : value{default_val}, 150 : value{default_val},
149 default_value{default_val}, maximum{max_val}, minimum{min_val}, label{name} {} 151 default_value{default_val}, maximum{max_val}, minimum{min_val}, label{name} {}
150 152
@@ -232,7 +234,8 @@ public:
232 * @param default_val Intial value of the setting, and default value of the setting 234 * @param default_val Intial value of the setting, and default value of the setting
233 * @param name Label for the setting 235 * @param name Label for the setting
234 */ 236 */
235 explicit SwitchableSetting(const Type& default_val, const std::string& name) requires(!ranged) 237 explicit SwitchableSetting(const Type& default_val, const std::string& name)
238 requires(!ranged)
236 : Setting<Type>{default_val, name} {} 239 : Setting<Type>{default_val, name} {}
237 virtual ~SwitchableSetting() = default; 240 virtual ~SwitchableSetting() = default;
238 241
@@ -245,7 +248,8 @@ public:
245 * @param name Label for the setting 248 * @param name Label for the setting
246 */ 249 */
247 explicit SwitchableSetting(const Type& default_val, const Type& min_val, const Type& max_val, 250 explicit SwitchableSetting(const Type& default_val, const Type& min_val, const Type& max_val,
248 const std::string& name) requires(ranged) 251 const std::string& name)
252 requires(ranged)
249 : Setting<Type, true>{default_val, min_val, max_val, name} {} 253 : Setting<Type, true>{default_val, min_val, max_val, name} {}
250 254
251 /** 255 /**
@@ -483,6 +487,7 @@ struct Values {
483 487
484 Setting<bool> enable_raw_input{false, "enable_raw_input"}; 488 Setting<bool> enable_raw_input{false, "enable_raw_input"};
485 Setting<bool> controller_navigation{true, "controller_navigation"}; 489 Setting<bool> controller_navigation{true, "controller_navigation"};
490 Setting<bool> enable_joycon_driver{true, "enable_joycon_driver"};
486 491
487 SwitchableSetting<bool> vibration_enabled{true, "vibration_enabled"}; 492 SwitchableSetting<bool> vibration_enabled{true, "vibration_enabled"};
488 SwitchableSetting<bool> enable_accurate_vibrations{false, "enable_accurate_vibrations"}; 493 SwitchableSetting<bool> enable_accurate_vibrations{false, "enable_accurate_vibrations"};
diff --git a/src/common/string_util.cpp b/src/common/string_util.cpp
index b26db4796..e0b6180c5 100644
--- a/src/common/string_util.cpp
+++ b/src/common/string_util.cpp
@@ -30,7 +30,7 @@ std::string ToUpper(std::string str) {
30 return str; 30 return str;
31} 31}
32 32
33std::string StringFromBuffer(const std::vector<u8>& data) { 33std::string StringFromBuffer(std::span<const u8> data) {
34 return std::string(data.begin(), std::find(data.begin(), data.end(), '\0')); 34 return std::string(data.begin(), std::find(data.begin(), data.end(), '\0'));
35} 35}
36 36
diff --git a/src/common/string_util.h b/src/common/string_util.h
index ce18a33cf..f8aecc875 100644
--- a/src/common/string_util.h
+++ b/src/common/string_util.h
@@ -5,6 +5,7 @@
5#pragma once 5#pragma once
6 6
7#include <cstddef> 7#include <cstddef>
8#include <span>
8#include <string> 9#include <string>
9#include <vector> 10#include <vector>
10#include "common/common_types.h" 11#include "common/common_types.h"
@@ -17,7 +18,7 @@ namespace Common {
17/// Make a string uppercase 18/// Make a string uppercase
18[[nodiscard]] std::string ToUpper(std::string str); 19[[nodiscard]] std::string ToUpper(std::string str);
19 20
20[[nodiscard]] std::string StringFromBuffer(const std::vector<u8>& data); 21[[nodiscard]] std::string StringFromBuffer(std::span<const u8> data);
21 22
22[[nodiscard]] std::string StripSpaces(const std::string& s); 23[[nodiscard]] std::string StripSpaces(const std::string& s);
23[[nodiscard]] std::string StripQuotes(const std::string& s); 24[[nodiscard]] std::string StripQuotes(const std::string& s);
diff --git a/src/common/tree.h b/src/common/tree.h
index f77859209..f4fc43de3 100644
--- a/src/common/tree.h
+++ b/src/common/tree.h
@@ -103,12 +103,12 @@ concept IsRBEntry = CheckRBEntry<T>::value;
103 103
104template <typename T> 104template <typename T>
105concept HasRBEntry = requires(T& t, const T& ct) { 105concept HasRBEntry = requires(T& t, const T& ct) {
106 { t.GetRBEntry() } -> std::same_as<RBEntry<T>&>; 106 { t.GetRBEntry() } -> std::same_as<RBEntry<T>&>;
107 { ct.GetRBEntry() } -> std::same_as<const RBEntry<T>&>; 107 { ct.GetRBEntry() } -> std::same_as<const RBEntry<T>&>;
108}; 108 };
109 109
110template <typename T> 110template <typename T>
111requires HasRBEntry<T> 111 requires HasRBEntry<T>
112class RBHead { 112class RBHead {
113private: 113private:
114 T* m_rbh_root = nullptr; 114 T* m_rbh_root = nullptr;
@@ -130,90 +130,90 @@ public:
130}; 130};
131 131
132template <typename T> 132template <typename T>
133requires HasRBEntry<T> 133 requires HasRBEntry<T>
134[[nodiscard]] constexpr RBEntry<T>& RB_ENTRY(T* t) { 134[[nodiscard]] constexpr RBEntry<T>& RB_ENTRY(T* t) {
135 return t->GetRBEntry(); 135 return t->GetRBEntry();
136} 136}
137template <typename T> 137template <typename T>
138requires HasRBEntry<T> 138 requires HasRBEntry<T>
139[[nodiscard]] constexpr const RBEntry<T>& RB_ENTRY(const T* t) { 139[[nodiscard]] constexpr const RBEntry<T>& RB_ENTRY(const T* t) {
140 return t->GetRBEntry(); 140 return t->GetRBEntry();
141} 141}
142 142
143template <typename T> 143template <typename T>
144requires HasRBEntry<T> 144 requires HasRBEntry<T>
145[[nodiscard]] constexpr T* RB_LEFT(T* t) { 145[[nodiscard]] constexpr T* RB_LEFT(T* t) {
146 return RB_ENTRY(t).Left(); 146 return RB_ENTRY(t).Left();
147} 147}
148template <typename T> 148template <typename T>
149requires HasRBEntry<T> 149 requires HasRBEntry<T>
150[[nodiscard]] constexpr const T* RB_LEFT(const T* t) { 150[[nodiscard]] constexpr const T* RB_LEFT(const T* t) {
151 return RB_ENTRY(t).Left(); 151 return RB_ENTRY(t).Left();
152} 152}
153 153
154template <typename T> 154template <typename T>
155requires HasRBEntry<T> 155 requires HasRBEntry<T>
156[[nodiscard]] constexpr T* RB_RIGHT(T* t) { 156[[nodiscard]] constexpr T* RB_RIGHT(T* t) {
157 return RB_ENTRY(t).Right(); 157 return RB_ENTRY(t).Right();
158} 158}
159template <typename T> 159template <typename T>
160requires HasRBEntry<T> 160 requires HasRBEntry<T>
161[[nodiscard]] constexpr const T* RB_RIGHT(const T* t) { 161[[nodiscard]] constexpr const T* RB_RIGHT(const T* t) {
162 return RB_ENTRY(t).Right(); 162 return RB_ENTRY(t).Right();
163} 163}
164 164
165template <typename T> 165template <typename T>
166requires HasRBEntry<T> 166 requires HasRBEntry<T>
167[[nodiscard]] constexpr T* RB_PARENT(T* t) { 167[[nodiscard]] constexpr T* RB_PARENT(T* t) {
168 return RB_ENTRY(t).Parent(); 168 return RB_ENTRY(t).Parent();
169} 169}
170template <typename T> 170template <typename T>
171requires HasRBEntry<T> 171 requires HasRBEntry<T>
172[[nodiscard]] constexpr const T* RB_PARENT(const T* t) { 172[[nodiscard]] constexpr const T* RB_PARENT(const T* t) {
173 return RB_ENTRY(t).Parent(); 173 return RB_ENTRY(t).Parent();
174} 174}
175 175
176template <typename T> 176template <typename T>
177requires HasRBEntry<T> 177 requires HasRBEntry<T>
178constexpr void RB_SET_LEFT(T* t, T* e) { 178constexpr void RB_SET_LEFT(T* t, T* e) {
179 RB_ENTRY(t).SetLeft(e); 179 RB_ENTRY(t).SetLeft(e);
180} 180}
181template <typename T> 181template <typename T>
182requires HasRBEntry<T> 182 requires HasRBEntry<T>
183constexpr void RB_SET_RIGHT(T* t, T* e) { 183constexpr void RB_SET_RIGHT(T* t, T* e) {
184 RB_ENTRY(t).SetRight(e); 184 RB_ENTRY(t).SetRight(e);
185} 185}
186template <typename T> 186template <typename T>
187requires HasRBEntry<T> 187 requires HasRBEntry<T>
188constexpr void RB_SET_PARENT(T* t, T* e) { 188constexpr void RB_SET_PARENT(T* t, T* e) {
189 RB_ENTRY(t).SetParent(e); 189 RB_ENTRY(t).SetParent(e);
190} 190}
191 191
192template <typename T> 192template <typename T>
193requires HasRBEntry<T> 193 requires HasRBEntry<T>
194[[nodiscard]] constexpr bool RB_IS_BLACK(const T* t) { 194[[nodiscard]] constexpr bool RB_IS_BLACK(const T* t) {
195 return RB_ENTRY(t).IsBlack(); 195 return RB_ENTRY(t).IsBlack();
196} 196}
197template <typename T> 197template <typename T>
198requires HasRBEntry<T> 198 requires HasRBEntry<T>
199[[nodiscard]] constexpr bool RB_IS_RED(const T* t) { 199[[nodiscard]] constexpr bool RB_IS_RED(const T* t) {
200 return RB_ENTRY(t).IsRed(); 200 return RB_ENTRY(t).IsRed();
201} 201}
202 202
203template <typename T> 203template <typename T>
204requires HasRBEntry<T> 204 requires HasRBEntry<T>
205[[nodiscard]] constexpr RBColor RB_COLOR(const T* t) { 205[[nodiscard]] constexpr RBColor RB_COLOR(const T* t) {
206 return RB_ENTRY(t).Color(); 206 return RB_ENTRY(t).Color();
207} 207}
208 208
209template <typename T> 209template <typename T>
210requires HasRBEntry<T> 210 requires HasRBEntry<T>
211constexpr void RB_SET_COLOR(T* t, RBColor c) { 211constexpr void RB_SET_COLOR(T* t, RBColor c) {
212 RB_ENTRY(t).SetColor(c); 212 RB_ENTRY(t).SetColor(c);
213} 213}
214 214
215template <typename T> 215template <typename T>
216requires HasRBEntry<T> 216 requires HasRBEntry<T>
217constexpr void RB_SET(T* elm, T* parent) { 217constexpr void RB_SET(T* elm, T* parent) {
218 auto& rb_entry = RB_ENTRY(elm); 218 auto& rb_entry = RB_ENTRY(elm);
219 rb_entry.SetParent(parent); 219 rb_entry.SetParent(parent);
@@ -223,14 +223,14 @@ constexpr void RB_SET(T* elm, T* parent) {
223} 223}
224 224
225template <typename T> 225template <typename T>
226requires HasRBEntry<T> 226 requires HasRBEntry<T>
227constexpr void RB_SET_BLACKRED(T* black, T* red) { 227constexpr void RB_SET_BLACKRED(T* black, T* red) {
228 RB_SET_COLOR(black, RBColor::RB_BLACK); 228 RB_SET_COLOR(black, RBColor::RB_BLACK);
229 RB_SET_COLOR(red, RBColor::RB_RED); 229 RB_SET_COLOR(red, RBColor::RB_RED);
230} 230}
231 231
232template <typename T> 232template <typename T>
233requires HasRBEntry<T> 233 requires HasRBEntry<T>
234constexpr void RB_ROTATE_LEFT(RBHead<T>& head, T* elm, T*& tmp) { 234constexpr void RB_ROTATE_LEFT(RBHead<T>& head, T* elm, T*& tmp) {
235 tmp = RB_RIGHT(elm); 235 tmp = RB_RIGHT(elm);
236 if (RB_SET_RIGHT(elm, RB_LEFT(tmp)); RB_RIGHT(elm) != nullptr) { 236 if (RB_SET_RIGHT(elm, RB_LEFT(tmp)); RB_RIGHT(elm) != nullptr) {
@@ -252,7 +252,7 @@ constexpr void RB_ROTATE_LEFT(RBHead<T>& head, T* elm, T*& tmp) {
252} 252}
253 253
254template <typename T> 254template <typename T>
255requires HasRBEntry<T> 255 requires HasRBEntry<T>
256constexpr void RB_ROTATE_RIGHT(RBHead<T>& head, T* elm, T*& tmp) { 256constexpr void RB_ROTATE_RIGHT(RBHead<T>& head, T* elm, T*& tmp) {
257 tmp = RB_LEFT(elm); 257 tmp = RB_LEFT(elm);
258 if (RB_SET_LEFT(elm, RB_RIGHT(tmp)); RB_LEFT(elm) != nullptr) { 258 if (RB_SET_LEFT(elm, RB_RIGHT(tmp)); RB_LEFT(elm) != nullptr) {
@@ -274,7 +274,7 @@ constexpr void RB_ROTATE_RIGHT(RBHead<T>& head, T* elm, T*& tmp) {
274} 274}
275 275
276template <typename T> 276template <typename T>
277requires HasRBEntry<T> 277 requires HasRBEntry<T>
278constexpr void RB_REMOVE_COLOR(RBHead<T>& head, T* parent, T* elm) { 278constexpr void RB_REMOVE_COLOR(RBHead<T>& head, T* parent, T* elm) {
279 T* tmp; 279 T* tmp;
280 while ((elm == nullptr || RB_IS_BLACK(elm)) && elm != head.Root()) { 280 while ((elm == nullptr || RB_IS_BLACK(elm)) && elm != head.Root()) {
@@ -358,7 +358,7 @@ constexpr void RB_REMOVE_COLOR(RBHead<T>& head, T* parent, T* elm) {
358} 358}
359 359
360template <typename T> 360template <typename T>
361requires HasRBEntry<T> 361 requires HasRBEntry<T>
362constexpr T* RB_REMOVE(RBHead<T>& head, T* elm) { 362constexpr T* RB_REMOVE(RBHead<T>& head, T* elm) {
363 T* child = nullptr; 363 T* child = nullptr;
364 T* parent = nullptr; 364 T* parent = nullptr;
@@ -451,7 +451,7 @@ constexpr T* RB_REMOVE(RBHead<T>& head, T* elm) {
451} 451}
452 452
453template <typename T> 453template <typename T>
454requires HasRBEntry<T> 454 requires HasRBEntry<T>
455constexpr void RB_INSERT_COLOR(RBHead<T>& head, T* elm) { 455constexpr void RB_INSERT_COLOR(RBHead<T>& head, T* elm) {
456 T *parent = nullptr, *tmp = nullptr; 456 T *parent = nullptr, *tmp = nullptr;
457 while ((parent = RB_PARENT(elm)) != nullptr && RB_IS_RED(parent)) { 457 while ((parent = RB_PARENT(elm)) != nullptr && RB_IS_RED(parent)) {
@@ -499,7 +499,7 @@ constexpr void RB_INSERT_COLOR(RBHead<T>& head, T* elm) {
499} 499}
500 500
501template <typename T, typename Compare> 501template <typename T, typename Compare>
502requires HasRBEntry<T> 502 requires HasRBEntry<T>
503constexpr T* RB_INSERT(RBHead<T>& head, T* elm, Compare cmp) { 503constexpr T* RB_INSERT(RBHead<T>& head, T* elm, Compare cmp) {
504 T* parent = nullptr; 504 T* parent = nullptr;
505 T* tmp = head.Root(); 505 T* tmp = head.Root();
@@ -534,7 +534,7 @@ constexpr T* RB_INSERT(RBHead<T>& head, T* elm, Compare cmp) {
534} 534}
535 535
536template <typename T, typename Compare> 536template <typename T, typename Compare>
537requires HasRBEntry<T> 537 requires HasRBEntry<T>
538constexpr T* RB_FIND(RBHead<T>& head, T* elm, Compare cmp) { 538constexpr T* RB_FIND(RBHead<T>& head, T* elm, Compare cmp) {
539 T* tmp = head.Root(); 539 T* tmp = head.Root();
540 540
@@ -553,7 +553,7 @@ constexpr T* RB_FIND(RBHead<T>& head, T* elm, Compare cmp) {
553} 553}
554 554
555template <typename T, typename Compare> 555template <typename T, typename Compare>
556requires HasRBEntry<T> 556 requires HasRBEntry<T>
557constexpr T* RB_NFIND(RBHead<T>& head, T* elm, Compare cmp) { 557constexpr T* RB_NFIND(RBHead<T>& head, T* elm, Compare cmp) {
558 T* tmp = head.Root(); 558 T* tmp = head.Root();
559 T* res = nullptr; 559 T* res = nullptr;
@@ -574,7 +574,7 @@ constexpr T* RB_NFIND(RBHead<T>& head, T* elm, Compare cmp) {
574} 574}
575 575
576template <typename T, typename U, typename Compare> 576template <typename T, typename U, typename Compare>
577requires HasRBEntry<T> 577 requires HasRBEntry<T>
578constexpr T* RB_FIND_KEY(RBHead<T>& head, const U& key, Compare cmp) { 578constexpr T* RB_FIND_KEY(RBHead<T>& head, const U& key, Compare cmp) {
579 T* tmp = head.Root(); 579 T* tmp = head.Root();
580 580
@@ -593,7 +593,7 @@ constexpr T* RB_FIND_KEY(RBHead<T>& head, const U& key, Compare cmp) {
593} 593}
594 594
595template <typename T, typename U, typename Compare> 595template <typename T, typename U, typename Compare>
596requires HasRBEntry<T> 596 requires HasRBEntry<T>
597constexpr T* RB_NFIND_KEY(RBHead<T>& head, const U& key, Compare cmp) { 597constexpr T* RB_NFIND_KEY(RBHead<T>& head, const U& key, Compare cmp) {
598 T* tmp = head.Root(); 598 T* tmp = head.Root();
599 T* res = nullptr; 599 T* res = nullptr;
@@ -614,7 +614,7 @@ constexpr T* RB_NFIND_KEY(RBHead<T>& head, const U& key, Compare cmp) {
614} 614}
615 615
616template <typename T, typename Compare> 616template <typename T, typename Compare>
617requires HasRBEntry<T> 617 requires HasRBEntry<T>
618constexpr T* RB_FIND_EXISTING(RBHead<T>& head, T* elm, Compare cmp) { 618constexpr T* RB_FIND_EXISTING(RBHead<T>& head, T* elm, Compare cmp) {
619 T* tmp = head.Root(); 619 T* tmp = head.Root();
620 620
@@ -631,7 +631,7 @@ constexpr T* RB_FIND_EXISTING(RBHead<T>& head, T* elm, Compare cmp) {
631} 631}
632 632
633template <typename T, typename U, typename Compare> 633template <typename T, typename U, typename Compare>
634requires HasRBEntry<T> 634 requires HasRBEntry<T>
635constexpr T* RB_FIND_EXISTING_KEY(RBHead<T>& head, const U& key, Compare cmp) { 635constexpr T* RB_FIND_EXISTING_KEY(RBHead<T>& head, const U& key, Compare cmp) {
636 T* tmp = head.Root(); 636 T* tmp = head.Root();
637 637
@@ -648,7 +648,7 @@ constexpr T* RB_FIND_EXISTING_KEY(RBHead<T>& head, const U& key, Compare cmp) {
648} 648}
649 649
650template <typename T> 650template <typename T>
651requires HasRBEntry<T> 651 requires HasRBEntry<T>
652constexpr T* RB_NEXT(T* elm) { 652constexpr T* RB_NEXT(T* elm) {
653 if (RB_RIGHT(elm)) { 653 if (RB_RIGHT(elm)) {
654 elm = RB_RIGHT(elm); 654 elm = RB_RIGHT(elm);
@@ -669,7 +669,7 @@ constexpr T* RB_NEXT(T* elm) {
669} 669}
670 670
671template <typename T> 671template <typename T>
672requires HasRBEntry<T> 672 requires HasRBEntry<T>
673constexpr T* RB_PREV(T* elm) { 673constexpr T* RB_PREV(T* elm) {
674 if (RB_LEFT(elm)) { 674 if (RB_LEFT(elm)) {
675 elm = RB_LEFT(elm); 675 elm = RB_LEFT(elm);
@@ -690,7 +690,7 @@ constexpr T* RB_PREV(T* elm) {
690} 690}
691 691
692template <typename T> 692template <typename T>
693requires HasRBEntry<T> 693 requires HasRBEntry<T>
694constexpr T* RB_MIN(RBHead<T>& head) { 694constexpr T* RB_MIN(RBHead<T>& head) {
695 T* tmp = head.Root(); 695 T* tmp = head.Root();
696 T* parent = nullptr; 696 T* parent = nullptr;
@@ -704,7 +704,7 @@ constexpr T* RB_MIN(RBHead<T>& head) {
704} 704}
705 705
706template <typename T> 706template <typename T>
707requires HasRBEntry<T> 707 requires HasRBEntry<T>
708constexpr T* RB_MAX(RBHead<T>& head) { 708constexpr T* RB_MAX(RBHead<T>& head) {
709 T* tmp = head.Root(); 709 T* tmp = head.Root();
710 T* parent = nullptr; 710 T* parent = nullptr;
diff --git a/src/common/vector_math.h b/src/common/vector_math.h
index e62eeea2e..0e2095c45 100644
--- a/src/common/vector_math.h
+++ b/src/common/vector_math.h
@@ -348,9 +348,7 @@ public:
348// _DEFINE_SWIZZLER2 defines a single such function, DEFINE_SWIZZLER2 defines all of them for all 348// _DEFINE_SWIZZLER2 defines a single such function, DEFINE_SWIZZLER2 defines all of them for all
349// component names (x<->r) and permutations (xy<->yx) 349// component names (x<->r) and permutations (xy<->yx)
350#define _DEFINE_SWIZZLER2(a, b, name) \ 350#define _DEFINE_SWIZZLER2(a, b, name) \
351 [[nodiscard]] constexpr Vec2<T> name() const { \ 351 [[nodiscard]] constexpr Vec2<T> name() const { return Vec2<T>(a, b); }
352 return Vec2<T>(a, b); \
353 }
354#define DEFINE_SWIZZLER2(a, b, a2, b2, a3, b3, a4, b4) \ 352#define DEFINE_SWIZZLER2(a, b, a2, b2, a3, b3, a4, b4) \
355 _DEFINE_SWIZZLER2(a, b, a##b); \ 353 _DEFINE_SWIZZLER2(a, b, a##b); \
356 _DEFINE_SWIZZLER2(a, b, a2##b2); \ 354 _DEFINE_SWIZZLER2(a, b, a2##b2); \
@@ -543,9 +541,7 @@ public:
543// DEFINE_SWIZZLER2_COMP2 defines two component functions for all component names (x<->r) and 541// DEFINE_SWIZZLER2_COMP2 defines two component functions for all component names (x<->r) and
544// permutations (xy<->yx) 542// permutations (xy<->yx)
545#define _DEFINE_SWIZZLER2(a, b, name) \ 543#define _DEFINE_SWIZZLER2(a, b, name) \
546 [[nodiscard]] constexpr Vec2<T> name() const { \ 544 [[nodiscard]] constexpr Vec2<T> name() const { return Vec2<T>(a, b); }
547 return Vec2<T>(a, b); \
548 }
549#define DEFINE_SWIZZLER2_COMP1(a, a2) \ 545#define DEFINE_SWIZZLER2_COMP1(a, a2) \
550 _DEFINE_SWIZZLER2(a, a, a##a); \ 546 _DEFINE_SWIZZLER2(a, a, a##a); \
551 _DEFINE_SWIZZLER2(a, a, a2##a2) 547 _DEFINE_SWIZZLER2(a, a, a2##a2)
@@ -570,9 +566,7 @@ public:
570#undef _DEFINE_SWIZZLER2 566#undef _DEFINE_SWIZZLER2
571 567
572#define _DEFINE_SWIZZLER3(a, b, c, name) \ 568#define _DEFINE_SWIZZLER3(a, b, c, name) \
573 [[nodiscard]] constexpr Vec3<T> name() const { \ 569 [[nodiscard]] constexpr Vec3<T> name() const { return Vec3<T>(a, b, c); }
574 return Vec3<T>(a, b, c); \
575 }
576#define DEFINE_SWIZZLER3_COMP1(a, a2) \ 570#define DEFINE_SWIZZLER3_COMP1(a, a2) \
577 _DEFINE_SWIZZLER3(a, a, a, a##a##a); \ 571 _DEFINE_SWIZZLER3(a, a, a, a##a##a); \
578 _DEFINE_SWIZZLER3(a, a, a, a2##a2##a2) 572 _DEFINE_SWIZZLER3(a, a, a, a2##a2##a2)
@@ -641,8 +635,8 @@ template <typename T>
641 635
642// linear interpolation via float: 0.0=begin, 1.0=end 636// linear interpolation via float: 0.0=begin, 1.0=end
643template <typename X> 637template <typename X>
644[[nodiscard]] constexpr decltype(X{} * float{} + X{} * float{}) 638[[nodiscard]] constexpr decltype(X{} * float{} + X{} * float{}) Lerp(const X& begin, const X& end,
645 Lerp(const X& begin, const X& end, const float t) { 639 const float t) {
646 return begin * (1.f - t) + end * t; 640 return begin * (1.f - t) + end * t;
647} 641}
648 642
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt
index 5afdeb5ff..f16072e6c 100644
--- a/src/core/CMakeLists.txt
+++ b/src/core/CMakeLists.txt
@@ -182,6 +182,8 @@ add_library(core STATIC
182 hle/kernel/k_auto_object_container.cpp 182 hle/kernel/k_auto_object_container.cpp
183 hle/kernel/k_auto_object_container.h 183 hle/kernel/k_auto_object_container.h
184 hle/kernel/k_affinity_mask.h 184 hle/kernel/k_affinity_mask.h
185 hle/kernel/k_capabilities.cpp
186 hle/kernel/k_capabilities.h
185 hle/kernel/k_class_token.cpp 187 hle/kernel/k_class_token.cpp
186 hle/kernel/k_class_token.h 188 hle/kernel/k_class_token.h
187 hle/kernel/k_client_port.cpp 189 hle/kernel/k_client_port.cpp
@@ -193,6 +195,8 @@ add_library(core STATIC
193 hle/kernel/k_condition_variable.cpp 195 hle/kernel/k_condition_variable.cpp
194 hle/kernel/k_condition_variable.h 196 hle/kernel/k_condition_variable.h
195 hle/kernel/k_debug.h 197 hle/kernel/k_debug.h
198 hle/kernel/k_device_address_space.cpp
199 hle/kernel/k_device_address_space.h
196 hle/kernel/k_dynamic_page_manager.h 200 hle/kernel/k_dynamic_page_manager.h
197 hle/kernel/k_dynamic_resource_manager.h 201 hle/kernel/k_dynamic_resource_manager.h
198 hle/kernel/k_dynamic_slab_heap.h 202 hle/kernel/k_dynamic_slab_heap.h
@@ -294,7 +298,42 @@ add_library(core STATIC
294 hle/kernel/svc.h 298 hle/kernel/svc.h
295 hle/kernel/svc_common.h 299 hle/kernel/svc_common.h
296 hle/kernel/svc_types.h 300 hle/kernel/svc_types.h
297 hle/kernel/svc_wrap.h 301 hle/kernel/svc/svc_activity.cpp
302 hle/kernel/svc/svc_address_arbiter.cpp
303 hle/kernel/svc/svc_address_translation.cpp
304 hle/kernel/svc/svc_cache.cpp
305 hle/kernel/svc/svc_code_memory.cpp
306 hle/kernel/svc/svc_condition_variable.cpp
307 hle/kernel/svc/svc_debug.cpp
308 hle/kernel/svc/svc_debug_string.cpp
309 hle/kernel/svc/svc_device_address_space.cpp
310 hle/kernel/svc/svc_event.cpp
311 hle/kernel/svc/svc_exception.cpp
312 hle/kernel/svc/svc_info.cpp
313 hle/kernel/svc/svc_interrupt_event.cpp
314 hle/kernel/svc/svc_io_pool.cpp
315 hle/kernel/svc/svc_ipc.cpp
316 hle/kernel/svc/svc_kernel_debug.cpp
317 hle/kernel/svc/svc_light_ipc.cpp
318 hle/kernel/svc/svc_lock.cpp
319 hle/kernel/svc/svc_memory.cpp
320 hle/kernel/svc/svc_physical_memory.cpp
321 hle/kernel/svc/svc_port.cpp
322 hle/kernel/svc/svc_power_management.cpp
323 hle/kernel/svc/svc_process.cpp
324 hle/kernel/svc/svc_process_memory.cpp
325 hle/kernel/svc/svc_processor.cpp
326 hle/kernel/svc/svc_query_memory.cpp
327 hle/kernel/svc/svc_register.cpp
328 hle/kernel/svc/svc_resource_limit.cpp
329 hle/kernel/svc/svc_secure_monitor_call.cpp
330 hle/kernel/svc/svc_session.cpp
331 hle/kernel/svc/svc_shared_memory.cpp
332 hle/kernel/svc/svc_synchronization.cpp
333 hle/kernel/svc/svc_thread.cpp
334 hle/kernel/svc/svc_thread_profiler.cpp
335 hle/kernel/svc/svc_tick.cpp
336 hle/kernel/svc/svc_transfer_memory.cpp
298 hle/result.h 337 hle/result.h
299 hle/service/acc/acc.cpp 338 hle/service/acc/acc.cpp
300 hle/service/acc/acc.h 339 hle/service/acc/acc.h
diff --git a/src/core/arm/arm_interface.cpp b/src/core/arm/arm_interface.cpp
index 2df7b0ee8..8aa7b9641 100644
--- a/src/core/arm/arm_interface.cpp
+++ b/src/core/arm/arm_interface.cpp
@@ -1,14 +1,12 @@
1// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project 1// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later 2// SPDX-License-Identifier: GPL-2.0-or-later
3 3
4#ifndef _MSC_VER
5#include <cxxabi.h>
6#endif
7
8#include <map> 4#include <map>
9#include <optional> 5#include <optional>
6
10#include "common/bit_field.h" 7#include "common/bit_field.h"
11#include "common/common_types.h" 8#include "common/common_types.h"
9#include "common/demangle.h"
12#include "common/logging/log.h" 10#include "common/logging/log.h"
13#include "core/arm/arm_interface.h" 11#include "core/arm/arm_interface.h"
14#include "core/arm/symbols.h" 12#include "core/arm/symbols.h"
@@ -71,20 +69,8 @@ void ARM_Interface::SymbolicateBacktrace(Core::System& system, std::vector<Backt
71 const auto symbol_set = symbols.find(entry.module); 69 const auto symbol_set = symbols.find(entry.module);
72 if (symbol_set != symbols.end()) { 70 if (symbol_set != symbols.end()) {
73 const auto symbol = Symbols::GetSymbolName(symbol_set->second, entry.offset); 71 const auto symbol = Symbols::GetSymbolName(symbol_set->second, entry.offset);
74 if (symbol.has_value()) { 72 if (symbol) {
75#ifdef _MSC_VER 73 entry.name = Common::DemangleSymbol(*symbol);
76 // TODO(DarkLordZach): Add demangling of symbol names.
77 entry.name = *symbol;
78#else
79 int status{-1};
80 char* demangled{abi::__cxa_demangle(symbol->c_str(), nullptr, nullptr, &status)};
81 if (status == 0 && demangled != nullptr) {
82 entry.name = demangled;
83 std::free(demangled);
84 } else {
85 entry.name = *symbol;
86 }
87#endif
88 } 74 }
89 } 75 }
90 } 76 }
diff --git a/src/core/debugger/gdbstub.cpp b/src/core/debugger/gdbstub.cpp
index a64a9ac64..9c02b7b31 100644
--- a/src/core/debugger/gdbstub.cpp
+++ b/src/core/debugger/gdbstub.cpp
@@ -11,6 +11,7 @@
11#include "common/hex_util.h" 11#include "common/hex_util.h"
12#include "common/logging/log.h" 12#include "common/logging/log.h"
13#include "common/scope_exit.h" 13#include "common/scope_exit.h"
14#include "common/settings.h"
14#include "core/arm/arm_interface.h" 15#include "core/arm/arm_interface.h"
15#include "core/core.h" 16#include "core/core.h"
16#include "core/debugger/gdbstub.h" 17#include "core/debugger/gdbstub.h"
@@ -731,7 +732,25 @@ void GDBStub::HandleRcmd(const std::vector<u8>& command) {
731 auto* process = system.CurrentProcess(); 732 auto* process = system.CurrentProcess();
732 auto& page_table = process->PageTable(); 733 auto& page_table = process->PageTable();
733 734
734 if (command_str == "get info") { 735 const char* commands = "Commands:\n"
736 " get fastmem\n"
737 " get info\n"
738 " get mappings\n";
739
740 if (command_str == "get fastmem") {
741 if (Settings::IsFastmemEnabled()) {
742 const auto& impl = page_table.PageTableImpl();
743 const auto region = reinterpret_cast<uintptr_t>(impl.fastmem_arena);
744 const auto region_bits = impl.current_address_space_width_in_bits;
745 const auto region_size = 1ULL << region_bits;
746
747 reply = fmt::format("Region bits: {}\n"
748 "Host address: {:#x} - {:#x}\n",
749 region_bits, region, region + region_size - 1);
750 } else {
751 reply = "Fastmem is not enabled.\n";
752 }
753 } else if (command_str == "get info") {
735 Loader::AppLoader::Modules modules; 754 Loader::AppLoader::Modules modules;
736 system.GetAppLoader().ReadNSOModules(modules); 755 system.GetAppLoader().ReadNSOModules(modules);
737 756
@@ -787,9 +806,10 @@ void GDBStub::HandleRcmd(const std::vector<u8>& command) {
787 cur_addr = next_address; 806 cur_addr = next_address;
788 } 807 }
789 } else if (command_str == "help") { 808 } else if (command_str == "help") {
790 reply = "Commands:\n get info\n get mappings\n"; 809 reply = commands;
791 } else { 810 } else {
792 reply = "Unknown command.\nCommands:\n get info\n get mappings\n"; 811 reply = "Unknown command.\n";
812 reply += commands;
793 } 813 }
794 814
795 std::span<const u8> reply_span{reinterpret_cast<u8*>(&reply.front()), reply.size()}; 815 std::span<const u8> reply_span{reinterpret_cast<u8*>(&reply.front()), reply.size()};
diff --git a/src/core/hardware_properties.h b/src/core/hardware_properties.h
index 13cbdb734..45567b840 100644
--- a/src/core/hardware_properties.h
+++ b/src/core/hardware_properties.h
@@ -25,6 +25,26 @@ constexpr std::array<s32, Common::BitSize<u64>()> VirtualToPhysicalCoreMap{
25 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 25 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3,
26}; 26};
27 27
28static constexpr inline size_t NumVirtualCores = Common::BitSize<u64>();
29
30static constexpr inline u64 VirtualCoreMask = [] {
31 u64 mask = 0;
32 for (size_t i = 0; i < NumVirtualCores; ++i) {
33 mask |= (UINT64_C(1) << i);
34 }
35 return mask;
36}();
37
38static constexpr inline u64 ConvertVirtualCoreMaskToPhysical(u64 v_core_mask) {
39 u64 p_core_mask = 0;
40 while (v_core_mask != 0) {
41 const u64 next = std::countr_zero(v_core_mask);
42 v_core_mask &= ~(static_cast<u64>(1) << next);
43 p_core_mask |= (static_cast<u64>(1) << VirtualToPhysicalCoreMap[next]);
44 }
45 return p_core_mask;
46}
47
28// Cortex-A57 supports 4 memory watchpoints 48// Cortex-A57 supports 4 memory watchpoints
29constexpr u64 NUM_WATCHPOINTS = 4; 49constexpr u64 NUM_WATCHPOINTS = 4;
30 50
diff --git a/src/core/hid/emulated_controller.cpp b/src/core/hid/emulated_controller.cpp
index 7a01f3f4c..631aa6ad2 100644
--- a/src/core/hid/emulated_controller.cpp
+++ b/src/core/hid/emulated_controller.cpp
@@ -2,6 +2,7 @@
2// SPDX-License-Identifier: GPL-2.0-or-later 2// SPDX-License-Identifier: GPL-2.0-or-later
3 3
4#include <algorithm> 4#include <algorithm>
5#include <common/scope_exit.h>
5 6
6#include "common/polyfill_ranges.h" 7#include "common/polyfill_ranges.h"
7#include "common/thread.h" 8#include "common/thread.h"
@@ -11,6 +12,7 @@
11namespace Core::HID { 12namespace Core::HID {
12constexpr s32 HID_JOYSTICK_MAX = 0x7fff; 13constexpr s32 HID_JOYSTICK_MAX = 0x7fff;
13constexpr s32 HID_TRIGGER_MAX = 0x7fff; 14constexpr s32 HID_TRIGGER_MAX = 0x7fff;
15constexpr u32 TURBO_BUTTON_DELAY = 4;
14// Use a common UUID for TAS and Virtual Gamepad 16// Use a common UUID for TAS and Virtual Gamepad
15constexpr Common::UUID TAS_UUID = 17constexpr Common::UUID TAS_UUID =
16 Common::UUID{{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x7, 0xA5, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}}; 18 Common::UUID{{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x7, 0xA5, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}};
@@ -93,6 +95,7 @@ void EmulatedController::ReloadFromSettings() {
93 motion_params[index] = Common::ParamPackage(player.motions[index]); 95 motion_params[index] = Common::ParamPackage(player.motions[index]);
94 } 96 }
95 97
98 controller.color_values = {};
96 controller.colors_state.fullkey = { 99 controller.colors_state.fullkey = {
97 .body = GetNpadColor(player.body_color_left), 100 .body = GetNpadColor(player.body_color_left),
98 .button = GetNpadColor(player.button_color_left), 101 .button = GetNpadColor(player.button_color_left),
@@ -106,6 +109,8 @@ void EmulatedController::ReloadFromSettings() {
106 .button = GetNpadColor(player.button_color_right), 109 .button = GetNpadColor(player.button_color_right),
107 }; 110 };
108 111
112 ring_params[0] = Common::ParamPackage(Settings::values.ringcon_analogs);
113
109 // Other or debug controller should always be a pro controller 114 // Other or debug controller should always be a pro controller
110 if (npad_id_type != NpadIdType::Other) { 115 if (npad_id_type != NpadIdType::Other) {
111 SetNpadStyleIndex(MapSettingsTypeToNPad(player.controller_type)); 116 SetNpadStyleIndex(MapSettingsTypeToNPad(player.controller_type));
@@ -132,18 +137,28 @@ void EmulatedController::LoadDevices() {
132 trigger_params[LeftIndex] = button_params[Settings::NativeButton::ZL]; 137 trigger_params[LeftIndex] = button_params[Settings::NativeButton::ZL];
133 trigger_params[RightIndex] = button_params[Settings::NativeButton::ZR]; 138 trigger_params[RightIndex] = button_params[Settings::NativeButton::ZR];
134 139
140 color_params[LeftIndex] = left_joycon;
141 color_params[RightIndex] = right_joycon;
142 color_params[LeftIndex].Set("color", true);
143 color_params[RightIndex].Set("color", true);
144
135 battery_params[LeftIndex] = left_joycon; 145 battery_params[LeftIndex] = left_joycon;
136 battery_params[RightIndex] = right_joycon; 146 battery_params[RightIndex] = right_joycon;
137 battery_params[LeftIndex].Set("battery", true); 147 battery_params[LeftIndex].Set("battery", true);
138 battery_params[RightIndex].Set("battery", true); 148 battery_params[RightIndex].Set("battery", true);
139 149
140 camera_params = Common::ParamPackage{"engine:camera,camera:1"}; 150 camera_params[0] = right_joycon;
141 nfc_params = Common::ParamPackage{"engine:virtual_amiibo,nfc:1"}; 151 camera_params[0].Set("camera", true);
152 camera_params[1] = Common::ParamPackage{"engine:camera,camera:1"};
153 ring_params[1] = Common::ParamPackage{"engine:joycon,axis_x:100,axis_y:101"};
154 nfc_params[0] = Common::ParamPackage{"engine:virtual_amiibo,nfc:1"};
155 nfc_params[1] = right_joycon;
156 nfc_params[1].Set("nfc", true);
142 157
143 output_params[LeftIndex] = left_joycon; 158 output_params[LeftIndex] = left_joycon;
144 output_params[RightIndex] = right_joycon; 159 output_params[RightIndex] = right_joycon;
145 output_params[2] = camera_params; 160 output_params[2] = camera_params[1];
146 output_params[3] = nfc_params; 161 output_params[3] = nfc_params[0];
147 output_params[LeftIndex].Set("output", true); 162 output_params[LeftIndex].Set("output", true);
148 output_params[RightIndex].Set("output", true); 163 output_params[RightIndex].Set("output", true);
149 output_params[2].Set("output", true); 164 output_params[2].Set("output", true);
@@ -159,8 +174,11 @@ void EmulatedController::LoadDevices() {
159 Common::Input::CreateInputDevice); 174 Common::Input::CreateInputDevice);
160 std::ranges::transform(battery_params, battery_devices.begin(), 175 std::ranges::transform(battery_params, battery_devices.begin(),
161 Common::Input::CreateInputDevice); 176 Common::Input::CreateInputDevice);
162 camera_devices = Common::Input::CreateInputDevice(camera_params); 177 std::ranges::transform(color_params, color_devices.begin(), Common::Input::CreateInputDevice);
163 nfc_devices = Common::Input::CreateInputDevice(nfc_params); 178 std::ranges::transform(camera_params, camera_devices.begin(), Common::Input::CreateInputDevice);
179 std::ranges::transform(ring_params, ring_analog_devices.begin(),
180 Common::Input::CreateInputDevice);
181 std::ranges::transform(nfc_params, nfc_devices.begin(), Common::Input::CreateInputDevice);
164 std::ranges::transform(output_params, output_devices.begin(), 182 std::ranges::transform(output_params, output_devices.begin(),
165 Common::Input::CreateOutputDevice); 183 Common::Input::CreateOutputDevice);
166 184
@@ -322,6 +340,19 @@ void EmulatedController::ReloadInput() {
322 battery_devices[index]->ForceUpdate(); 340 battery_devices[index]->ForceUpdate();
323 } 341 }
324 342
343 for (std::size_t index = 0; index < color_devices.size(); ++index) {
344 if (!color_devices[index]) {
345 continue;
346 }
347 color_devices[index]->SetCallback({
348 .on_change =
349 [this, index](const Common::Input::CallbackStatus& callback) {
350 SetColors(callback, index);
351 },
352 });
353 color_devices[index]->ForceUpdate();
354 }
355
325 for (std::size_t index = 0; index < motion_devices.size(); ++index) { 356 for (std::size_t index = 0; index < motion_devices.size(); ++index) {
326 if (!motion_devices[index]) { 357 if (!motion_devices[index]) {
327 continue; 358 continue;
@@ -335,22 +366,37 @@ void EmulatedController::ReloadInput() {
335 motion_devices[index]->ForceUpdate(); 366 motion_devices[index]->ForceUpdate();
336 } 367 }
337 368
338 if (camera_devices) { 369 for (std::size_t index = 0; index < camera_devices.size(); ++index) {
339 camera_devices->SetCallback({ 370 if (!camera_devices[index]) {
371 continue;
372 }
373 camera_devices[index]->SetCallback({
340 .on_change = 374 .on_change =
341 [this](const Common::Input::CallbackStatus& callback) { SetCamera(callback); }, 375 [this](const Common::Input::CallbackStatus& callback) { SetCamera(callback); },
342 }); 376 });
343 camera_devices->ForceUpdate(); 377 camera_devices[index]->ForceUpdate();
344 } 378 }
345 379
346 if (nfc_devices) { 380 for (std::size_t index = 0; index < ring_analog_devices.size(); ++index) {
347 if (npad_id_type == NpadIdType::Handheld || npad_id_type == NpadIdType::Player1) { 381 if (!ring_analog_devices[index]) {
348 nfc_devices->SetCallback({ 382 continue;
349 .on_change = 383 }
350 [this](const Common::Input::CallbackStatus& callback) { SetNfc(callback); }, 384 ring_analog_devices[index]->SetCallback({
351 }); 385 .on_change =
352 nfc_devices->ForceUpdate(); 386 [this](const Common::Input::CallbackStatus& callback) { SetRingAnalog(callback); },
387 });
388 ring_analog_devices[index]->ForceUpdate();
389 }
390
391 for (std::size_t index = 0; index < nfc_devices.size(); ++index) {
392 if (!nfc_devices[index]) {
393 continue;
353 } 394 }
395 nfc_devices[index]->SetCallback({
396 .on_change =
397 [this](const Common::Input::CallbackStatus& callback) { SetNfc(callback); },
398 });
399 nfc_devices[index]->ForceUpdate();
354 } 400 }
355 401
356 // Register TAS devices. No need to force update 402 // Register TAS devices. No need to force update
@@ -402,6 +448,7 @@ void EmulatedController::ReloadInput() {
402 }, 448 },
403 }); 449 });
404 } 450 }
451 turbo_button_state = 0;
405} 452}
406 453
407void EmulatedController::UnloadInput() { 454void EmulatedController::UnloadInput() {
@@ -420,6 +467,9 @@ void EmulatedController::UnloadInput() {
420 for (auto& battery : battery_devices) { 467 for (auto& battery : battery_devices) {
421 battery.reset(); 468 battery.reset();
422 } 469 }
470 for (auto& color : color_devices) {
471 color.reset();
472 }
423 for (auto& output : output_devices) { 473 for (auto& output : output_devices) {
424 output.reset(); 474 output.reset();
425 } 475 }
@@ -435,8 +485,15 @@ void EmulatedController::UnloadInput() {
435 for (auto& stick : virtual_stick_devices) { 485 for (auto& stick : virtual_stick_devices) {
436 stick.reset(); 486 stick.reset();
437 } 487 }
438 camera_devices.reset(); 488 for (auto& camera : camera_devices) {
439 nfc_devices.reset(); 489 camera.reset();
490 }
491 for (auto& ring : ring_analog_devices) {
492 ring.reset();
493 }
494 for (auto& nfc : nfc_devices) {
495 nfc.reset();
496 }
440} 497}
441 498
442void EmulatedController::EnableConfiguration() { 499void EmulatedController::EnableConfiguration() {
@@ -448,6 +505,11 @@ void EmulatedController::EnableConfiguration() {
448void EmulatedController::DisableConfiguration() { 505void EmulatedController::DisableConfiguration() {
449 is_configuring = false; 506 is_configuring = false;
450 507
508 // Get Joycon colors before turning on the controller
509 for (const auto& color_device : color_devices) {
510 color_device->ForceUpdate();
511 }
512
451 // Apply temporary npad type to the real controller 513 // Apply temporary npad type to the real controller
452 if (tmp_npad_type != npad_type) { 514 if (tmp_npad_type != npad_type) {
453 if (is_connected) { 515 if (is_connected) {
@@ -501,6 +563,9 @@ void EmulatedController::SaveCurrentConfig() {
501 for (std::size_t index = 0; index < player.motions.size(); ++index) { 563 for (std::size_t index = 0; index < player.motions.size(); ++index) {
502 player.motions[index] = motion_params[index].Serialize(); 564 player.motions[index] = motion_params[index].Serialize();
503 } 565 }
566 if (npad_id_type == NpadIdType::Player1) {
567 Settings::values.ringcon_analogs = ring_params[0].Serialize();
568 }
504} 569}
505 570
506void EmulatedController::RestoreConfig() { 571void EmulatedController::RestoreConfig() {
@@ -624,6 +689,7 @@ void EmulatedController::SetButton(const Common::Input::CallbackStatus& callback
624 } 689 }
625 690
626 current_status.toggle = new_status.toggle; 691 current_status.toggle = new_status.toggle;
692 current_status.turbo = new_status.turbo;
627 current_status.uuid = uuid; 693 current_status.uuid = uuid;
628 694
629 // Update button status with current 695 // Update button status with current
@@ -772,17 +838,21 @@ void EmulatedController::SetStick(const Common::Input::CallbackStatus& callback,
772 if (index >= controller.stick_values.size()) { 838 if (index >= controller.stick_values.size()) {
773 return; 839 return;
774 } 840 }
775 std::unique_lock lock{mutex}; 841 auto trigger_guard =
842 SCOPE_GUARD({ TriggerOnChange(ControllerTriggerType::Stick, !is_configuring); });
843 std::scoped_lock lock{mutex};
776 const auto stick_value = TransformToStick(callback); 844 const auto stick_value = TransformToStick(callback);
777 845
778 // Only read stick values that have the same uuid or are over the threshold to avoid flapping 846 // Only read stick values that have the same uuid or are over the threshold to avoid flapping
779 if (controller.stick_values[index].uuid != uuid) { 847 if (controller.stick_values[index].uuid != uuid) {
780 const bool is_tas = uuid == TAS_UUID; 848 const bool is_tas = uuid == TAS_UUID;
781 if (is_tas && stick_value.x.value == 0 && stick_value.y.value == 0) { 849 if (is_tas && stick_value.x.value == 0 && stick_value.y.value == 0) {
850 trigger_guard.Cancel();
782 return; 851 return;
783 } 852 }
784 if (!is_tas && !stick_value.down && !stick_value.up && !stick_value.left && 853 if (!is_tas && !stick_value.down && !stick_value.up && !stick_value.left &&
785 !stick_value.right) { 854 !stick_value.right) {
855 trigger_guard.Cancel();
786 return; 856 return;
787 } 857 }
788 } 858 }
@@ -793,8 +863,6 @@ void EmulatedController::SetStick(const Common::Input::CallbackStatus& callback,
793 if (is_configuring) { 863 if (is_configuring) {
794 controller.analog_stick_state.left = {}; 864 controller.analog_stick_state.left = {};
795 controller.analog_stick_state.right = {}; 865 controller.analog_stick_state.right = {};
796 lock.unlock();
797 TriggerOnChange(ControllerTriggerType::Stick, false);
798 return; 866 return;
799 } 867 }
800 868
@@ -819,9 +887,6 @@ void EmulatedController::SetStick(const Common::Input::CallbackStatus& callback,
819 controller.npad_button_state.stick_r_down.Assign(controller.stick_values[index].down); 887 controller.npad_button_state.stick_r_down.Assign(controller.stick_values[index].down);
820 break; 888 break;
821 } 889 }
822
823 lock.unlock();
824 TriggerOnChange(ControllerTriggerType::Stick, true);
825} 890}
826 891
827void EmulatedController::SetTrigger(const Common::Input::CallbackStatus& callback, 892void EmulatedController::SetTrigger(const Common::Input::CallbackStatus& callback,
@@ -829,7 +894,9 @@ void EmulatedController::SetTrigger(const Common::Input::CallbackStatus& callbac
829 if (index >= controller.trigger_values.size()) { 894 if (index >= controller.trigger_values.size()) {
830 return; 895 return;
831 } 896 }
832 std::unique_lock lock{mutex}; 897 auto trigger_guard =
898 SCOPE_GUARD({ TriggerOnChange(ControllerTriggerType::Trigger, !is_configuring); });
899 std::scoped_lock lock{mutex};
833 const auto trigger_value = TransformToTrigger(callback); 900 const auto trigger_value = TransformToTrigger(callback);
834 901
835 // Only read trigger values that have the same uuid or are pressed once 902 // Only read trigger values that have the same uuid or are pressed once
@@ -845,13 +912,12 @@ void EmulatedController::SetTrigger(const Common::Input::CallbackStatus& callbac
845 if (is_configuring) { 912 if (is_configuring) {
846 controller.gc_trigger_state.left = 0; 913 controller.gc_trigger_state.left = 0;
847 controller.gc_trigger_state.right = 0; 914 controller.gc_trigger_state.right = 0;
848 lock.unlock();
849 TriggerOnChange(ControllerTriggerType::Trigger, false);
850 return; 915 return;
851 } 916 }
852 917
853 // Only GC controllers have analog triggers 918 // Only GC controllers have analog triggers
854 if (npad_type != NpadStyleIndex::GameCube) { 919 if (npad_type != NpadStyleIndex::GameCube) {
920 trigger_guard.Cancel();
855 return; 921 return;
856 } 922 }
857 923
@@ -868,9 +934,6 @@ void EmulatedController::SetTrigger(const Common::Input::CallbackStatus& callbac
868 controller.npad_button_state.zr.Assign(trigger.pressed.value); 934 controller.npad_button_state.zr.Assign(trigger.pressed.value);
869 break; 935 break;
870 } 936 }
871
872 lock.unlock();
873 TriggerOnChange(ControllerTriggerType::Trigger, true);
874} 937}
875 938
876void EmulatedController::SetMotion(const Common::Input::CallbackStatus& callback, 939void EmulatedController::SetMotion(const Common::Input::CallbackStatus& callback,
@@ -878,7 +941,8 @@ void EmulatedController::SetMotion(const Common::Input::CallbackStatus& callback
878 if (index >= controller.motion_values.size()) { 941 if (index >= controller.motion_values.size()) {
879 return; 942 return;
880 } 943 }
881 std::unique_lock lock{mutex}; 944 SCOPE_EXIT({ TriggerOnChange(ControllerTriggerType::Motion, !is_configuring); });
945 std::scoped_lock lock{mutex};
882 auto& raw_status = controller.motion_values[index].raw_status; 946 auto& raw_status = controller.motion_values[index].raw_status;
883 auto& emulated = controller.motion_values[index].emulated; 947 auto& emulated = controller.motion_values[index].emulated;
884 948
@@ -899,8 +963,6 @@ void EmulatedController::SetMotion(const Common::Input::CallbackStatus& callback
899 force_update_motion = raw_status.force_update; 963 force_update_motion = raw_status.force_update;
900 964
901 if (is_configuring) { 965 if (is_configuring) {
902 lock.unlock();
903 TriggerOnChange(ControllerTriggerType::Motion, false);
904 return; 966 return;
905 } 967 }
906 968
@@ -910,9 +972,56 @@ void EmulatedController::SetMotion(const Common::Input::CallbackStatus& callback
910 motion.rotation = emulated.GetRotations(); 972 motion.rotation = emulated.GetRotations();
911 motion.orientation = emulated.GetOrientation(); 973 motion.orientation = emulated.GetOrientation();
912 motion.is_at_rest = !emulated.IsMoving(motion_sensitivity); 974 motion.is_at_rest = !emulated.IsMoving(motion_sensitivity);
975}
913 976
914 lock.unlock(); 977void EmulatedController::SetColors(const Common::Input::CallbackStatus& callback,
915 TriggerOnChange(ControllerTriggerType::Motion, true); 978 std::size_t index) {
979 if (index >= controller.color_values.size()) {
980 return;
981 }
982 auto trigger_guard =
983 SCOPE_GUARD({ TriggerOnChange(ControllerTriggerType::Color, !is_configuring); });
984 std::scoped_lock lock{mutex};
985 controller.color_values[index] = TransformToColor(callback);
986
987 if (is_configuring) {
988 return;
989 }
990
991 if (controller.color_values[index].body == 0) {
992 trigger_guard.Cancel();
993 return;
994 }
995
996 controller.colors_state.fullkey = {
997 .body = GetNpadColor(controller.color_values[index].body),
998 .button = GetNpadColor(controller.color_values[index].buttons),
999 };
1000 if (npad_type == NpadStyleIndex::ProController) {
1001 controller.colors_state.left = {
1002 .body = GetNpadColor(controller.color_values[index].left_grip),
1003 .button = GetNpadColor(controller.color_values[index].buttons),
1004 };
1005 controller.colors_state.right = {
1006 .body = GetNpadColor(controller.color_values[index].right_grip),
1007 .button = GetNpadColor(controller.color_values[index].buttons),
1008 };
1009 } else {
1010 switch (index) {
1011 case LeftIndex:
1012 controller.colors_state.left = {
1013 .body = GetNpadColor(controller.color_values[index].body),
1014 .button = GetNpadColor(controller.color_values[index].buttons),
1015 };
1016 break;
1017 case RightIndex:
1018 controller.colors_state.right = {
1019 .body = GetNpadColor(controller.color_values[index].body),
1020 .button = GetNpadColor(controller.color_values[index].buttons),
1021 };
1022 break;
1023 }
1024 }
916} 1025}
917 1026
918void EmulatedController::SetBattery(const Common::Input::CallbackStatus& callback, 1027void EmulatedController::SetBattery(const Common::Input::CallbackStatus& callback,
@@ -920,12 +1029,11 @@ void EmulatedController::SetBattery(const Common::Input::CallbackStatus& callbac
920 if (index >= controller.battery_values.size()) { 1029 if (index >= controller.battery_values.size()) {
921 return; 1030 return;
922 } 1031 }
923 std::unique_lock lock{mutex}; 1032 SCOPE_EXIT({ TriggerOnChange(ControllerTriggerType::Battery, !is_configuring); });
1033 std::scoped_lock lock{mutex};
924 controller.battery_values[index] = TransformToBattery(callback); 1034 controller.battery_values[index] = TransformToBattery(callback);
925 1035
926 if (is_configuring) { 1036 if (is_configuring) {
927 lock.unlock();
928 TriggerOnChange(ControllerTriggerType::Battery, false);
929 return; 1037 return;
930 } 1038 }
931 1039
@@ -981,18 +1089,14 @@ void EmulatedController::SetBattery(const Common::Input::CallbackStatus& callbac
981 }; 1089 };
982 break; 1090 break;
983 } 1091 }
984
985 lock.unlock();
986 TriggerOnChange(ControllerTriggerType::Battery, true);
987} 1092}
988 1093
989void EmulatedController::SetCamera(const Common::Input::CallbackStatus& callback) { 1094void EmulatedController::SetCamera(const Common::Input::CallbackStatus& callback) {
990 std::unique_lock lock{mutex}; 1095 SCOPE_EXIT({ TriggerOnChange(ControllerTriggerType::IrSensor, !is_configuring); });
1096 std::scoped_lock lock{mutex};
991 controller.camera_values = TransformToCamera(callback); 1097 controller.camera_values = TransformToCamera(callback);
992 1098
993 if (is_configuring) { 1099 if (is_configuring) {
994 lock.unlock();
995 TriggerOnChange(ControllerTriggerType::IrSensor, false);
996 return; 1100 return;
997 } 1101 }
998 1102
@@ -1000,18 +1104,28 @@ void EmulatedController::SetCamera(const Common::Input::CallbackStatus& callback
1000 controller.camera_state.format = 1104 controller.camera_state.format =
1001 static_cast<Core::IrSensor::ImageTransferProcessorFormat>(controller.camera_values.format); 1105 static_cast<Core::IrSensor::ImageTransferProcessorFormat>(controller.camera_values.format);
1002 controller.camera_state.data = controller.camera_values.data; 1106 controller.camera_state.data = controller.camera_values.data;
1107}
1003 1108
1004 lock.unlock(); 1109void EmulatedController::SetRingAnalog(const Common::Input::CallbackStatus& callback) {
1005 TriggerOnChange(ControllerTriggerType::IrSensor, true); 1110 SCOPE_EXIT({ TriggerOnChange(ControllerTriggerType::RingController, !is_configuring); });
1111 std::scoped_lock lock{mutex};
1112 const auto force_value = TransformToStick(callback);
1113
1114 controller.ring_analog_value = force_value.x;
1115
1116 if (is_configuring) {
1117 return;
1118 }
1119
1120 controller.ring_analog_state.force = force_value.x.value;
1006} 1121}
1007 1122
1008void EmulatedController::SetNfc(const Common::Input::CallbackStatus& callback) { 1123void EmulatedController::SetNfc(const Common::Input::CallbackStatus& callback) {
1009 std::unique_lock lock{mutex}; 1124 SCOPE_EXIT({ TriggerOnChange(ControllerTriggerType::Nfc, !is_configuring); });
1125 std::scoped_lock lock{mutex};
1010 controller.nfc_values = TransformToNfc(callback); 1126 controller.nfc_values = TransformToNfc(callback);
1011 1127
1012 if (is_configuring) { 1128 if (is_configuring) {
1013 lock.unlock();
1014 TriggerOnChange(ControllerTriggerType::Nfc, false);
1015 return; 1129 return;
1016 } 1130 }
1017 1131
@@ -1019,9 +1133,6 @@ void EmulatedController::SetNfc(const Common::Input::CallbackStatus& callback) {
1019 controller.nfc_values.state, 1133 controller.nfc_values.state,
1020 controller.nfc_values.data, 1134 controller.nfc_values.data,
1021 }; 1135 };
1022
1023 lock.unlock();
1024 TriggerOnChange(ControllerTriggerType::Nfc, true);
1025} 1136}
1026 1137
1027bool EmulatedController::SetVibration(std::size_t device_index, VibrationValue vibration) { 1138bool EmulatedController::SetVibration(std::size_t device_index, VibrationValue vibration) {
@@ -1053,7 +1164,7 @@ bool EmulatedController::SetVibration(std::size_t device_index, VibrationValue v
1053 .type = type, 1164 .type = type,
1054 }; 1165 };
1055 return output_devices[device_index]->SetVibration(status) == 1166 return output_devices[device_index]->SetVibration(status) ==
1056 Common::Input::VibrationError::None; 1167 Common::Input::DriverResult::Success;
1057} 1168}
1058 1169
1059bool EmulatedController::IsVibrationEnabled(std::size_t device_index) { 1170bool EmulatedController::IsVibrationEnabled(std::size_t device_index) {
@@ -1075,16 +1186,32 @@ bool EmulatedController::IsVibrationEnabled(std::size_t device_index) {
1075 return output_devices[device_index]->IsVibrationEnabled(); 1186 return output_devices[device_index]->IsVibrationEnabled();
1076} 1187}
1077 1188
1078bool EmulatedController::SetPollingMode(Common::Input::PollingMode polling_mode) { 1189Common::Input::DriverResult EmulatedController::SetPollingMode(
1079 LOG_INFO(Service_HID, "Set polling mode {}", polling_mode); 1190 EmulatedDeviceIndex device_index, Common::Input::PollingMode polling_mode) {
1080 auto& output_device = output_devices[static_cast<std::size_t>(DeviceIndex::Right)]; 1191 LOG_INFO(Service_HID, "Set polling mode {}, device_index={}", polling_mode, device_index);
1192
1193 auto& left_output_device = output_devices[static_cast<std::size_t>(DeviceIndex::Left)];
1194 auto& right_output_device = output_devices[static_cast<std::size_t>(DeviceIndex::Right)];
1081 auto& nfc_output_device = output_devices[3]; 1195 auto& nfc_output_device = output_devices[3];
1082 1196
1083 const auto virtual_nfc_result = nfc_output_device->SetPollingMode(polling_mode); 1197 if (device_index == EmulatedDeviceIndex::LeftIndex) {
1084 const auto mapped_nfc_result = output_device->SetPollingMode(polling_mode); 1198 return left_output_device->SetPollingMode(polling_mode);
1199 }
1200
1201 if (device_index == EmulatedDeviceIndex::RightIndex) {
1202 const auto virtual_nfc_result = nfc_output_device->SetPollingMode(polling_mode);
1203 const auto mapped_nfc_result = right_output_device->SetPollingMode(polling_mode);
1085 1204
1086 return virtual_nfc_result == Common::Input::PollingError::None || 1205 if (virtual_nfc_result == Common::Input::DriverResult::Success) {
1087 mapped_nfc_result == Common::Input::PollingError::None; 1206 return virtual_nfc_result;
1207 }
1208 return mapped_nfc_result;
1209 }
1210
1211 left_output_device->SetPollingMode(polling_mode);
1212 right_output_device->SetPollingMode(polling_mode);
1213 nfc_output_device->SetPollingMode(polling_mode);
1214 return Common::Input::DriverResult::Success;
1088} 1215}
1089 1216
1090bool EmulatedController::SetCameraFormat( 1217bool EmulatedController::SetCameraFormat(
@@ -1095,13 +1222,22 @@ bool EmulatedController::SetCameraFormat(
1095 auto& camera_output_device = output_devices[2]; 1222 auto& camera_output_device = output_devices[2];
1096 1223
1097 if (right_output_device->SetCameraFormat(static_cast<Common::Input::CameraFormat>( 1224 if (right_output_device->SetCameraFormat(static_cast<Common::Input::CameraFormat>(
1098 camera_format)) == Common::Input::CameraError::None) { 1225 camera_format)) == Common::Input::DriverResult::Success) {
1099 return true; 1226 return true;
1100 } 1227 }
1101 1228
1102 // Fallback to Qt camera if native device doesn't have support 1229 // Fallback to Qt camera if native device doesn't have support
1103 return camera_output_device->SetCameraFormat(static_cast<Common::Input::CameraFormat>( 1230 return camera_output_device->SetCameraFormat(static_cast<Common::Input::CameraFormat>(
1104 camera_format)) == Common::Input::CameraError::None; 1231 camera_format)) == Common::Input::DriverResult::Success;
1232}
1233
1234Common::ParamPackage EmulatedController::GetRingParam() const {
1235 return ring_params[0];
1236}
1237
1238void EmulatedController::SetRingParam(Common::ParamPackage param) {
1239 ring_params[0] = std::move(param);
1240 ReloadInput();
1105} 1241}
1106 1242
1107bool EmulatedController::HasNfc() const { 1243bool EmulatedController::HasNfc() const {
@@ -1255,39 +1391,35 @@ void EmulatedController::Connect(bool use_temporary_value) {
1255 return; 1391 return;
1256 } 1392 }
1257 1393
1258 std::unique_lock lock{mutex}; 1394 auto trigger_guard =
1395 SCOPE_GUARD({ TriggerOnChange(ControllerTriggerType::Connected, !is_configuring); });
1396 std::scoped_lock lock{mutex};
1259 if (is_configuring) { 1397 if (is_configuring) {
1260 tmp_is_connected = true; 1398 tmp_is_connected = true;
1261 lock.unlock();
1262 TriggerOnChange(ControllerTriggerType::Connected, false);
1263 return; 1399 return;
1264 } 1400 }
1265 1401
1266 if (is_connected) { 1402 if (is_connected) {
1403 trigger_guard.Cancel();
1267 return; 1404 return;
1268 } 1405 }
1269 is_connected = true; 1406 is_connected = true;
1270
1271 lock.unlock();
1272 TriggerOnChange(ControllerTriggerType::Connected, true);
1273} 1407}
1274 1408
1275void EmulatedController::Disconnect() { 1409void EmulatedController::Disconnect() {
1276 std::unique_lock lock{mutex}; 1410 auto trigger_guard =
1411 SCOPE_GUARD({ TriggerOnChange(ControllerTriggerType::Disconnected, !is_configuring); });
1412 std::scoped_lock lock{mutex};
1277 if (is_configuring) { 1413 if (is_configuring) {
1278 tmp_is_connected = false; 1414 tmp_is_connected = false;
1279 lock.unlock();
1280 TriggerOnChange(ControllerTriggerType::Disconnected, false);
1281 return; 1415 return;
1282 } 1416 }
1283 1417
1284 if (!is_connected) { 1418 if (!is_connected) {
1419 trigger_guard.Cancel();
1285 return; 1420 return;
1286 } 1421 }
1287 is_connected = false; 1422 is_connected = false;
1288
1289 lock.unlock();
1290 TriggerOnChange(ControllerTriggerType::Disconnected, true);
1291} 1423}
1292 1424
1293bool EmulatedController::IsConnected(bool get_temporary_value) const { 1425bool EmulatedController::IsConnected(bool get_temporary_value) const {
@@ -1312,19 +1444,21 @@ NpadStyleIndex EmulatedController::GetNpadStyleIndex(bool get_temporary_value) c
1312} 1444}
1313 1445
1314void EmulatedController::SetNpadStyleIndex(NpadStyleIndex npad_type_) { 1446void EmulatedController::SetNpadStyleIndex(NpadStyleIndex npad_type_) {
1315 std::unique_lock lock{mutex}; 1447 auto trigger_guard =
1448 SCOPE_GUARD({ TriggerOnChange(ControllerTriggerType::Type, !is_configuring); });
1449 std::scoped_lock lock{mutex};
1316 1450
1317 if (is_configuring) { 1451 if (is_configuring) {
1318 if (tmp_npad_type == npad_type_) { 1452 if (tmp_npad_type == npad_type_) {
1453 trigger_guard.Cancel();
1319 return; 1454 return;
1320 } 1455 }
1321 tmp_npad_type = npad_type_; 1456 tmp_npad_type = npad_type_;
1322 lock.unlock();
1323 TriggerOnChange(ControllerTriggerType::Type, false);
1324 return; 1457 return;
1325 } 1458 }
1326 1459
1327 if (npad_type == npad_type_) { 1460 if (npad_type == npad_type_) {
1461 trigger_guard.Cancel();
1328 return; 1462 return;
1329 } 1463 }
1330 if (is_connected) { 1464 if (is_connected) {
@@ -1332,9 +1466,6 @@ void EmulatedController::SetNpadStyleIndex(NpadStyleIndex npad_type_) {
1332 NpadIdTypeToIndex(npad_id_type)); 1466 NpadIdTypeToIndex(npad_id_type));
1333 } 1467 }
1334 npad_type = npad_type_; 1468 npad_type = npad_type_;
1335
1336 lock.unlock();
1337 TriggerOnChange(ControllerTriggerType::Type, true);
1338} 1469}
1339 1470
1340LedPattern EmulatedController::GetLedPattern() const { 1471LedPattern EmulatedController::GetLedPattern() const {
@@ -1395,6 +1526,10 @@ CameraValues EmulatedController::GetCameraValues() const {
1395 return controller.camera_values; 1526 return controller.camera_values;
1396} 1527}
1397 1528
1529RingAnalogValue EmulatedController::GetRingSensorValues() const {
1530 return controller.ring_analog_value;
1531}
1532
1398HomeButtonState EmulatedController::GetHomeButtons() const { 1533HomeButtonState EmulatedController::GetHomeButtons() const {
1399 std::scoped_lock lock{mutex}; 1534 std::scoped_lock lock{mutex};
1400 if (is_configuring) { 1535 if (is_configuring) {
@@ -1416,7 +1551,7 @@ NpadButtonState EmulatedController::GetNpadButtons() const {
1416 if (is_configuring) { 1551 if (is_configuring) {
1417 return {}; 1552 return {};
1418 } 1553 }
1419 return controller.npad_button_state; 1554 return {controller.npad_button_state.raw & GetTurboButtonMask()};
1420} 1555}
1421 1556
1422DebugPadButton EmulatedController::GetDebugPadButtons() const { 1557DebugPadButton EmulatedController::GetDebugPadButtons() const {
@@ -1428,7 +1563,7 @@ DebugPadButton EmulatedController::GetDebugPadButtons() const {
1428} 1563}
1429 1564
1430AnalogSticks EmulatedController::GetSticks() const { 1565AnalogSticks EmulatedController::GetSticks() const {
1431 std::unique_lock lock{mutex}; 1566 std::scoped_lock lock{mutex};
1432 1567
1433 if (is_configuring) { 1568 if (is_configuring) {
1434 return {}; 1569 return {};
@@ -1478,6 +1613,10 @@ const CameraState& EmulatedController::GetCamera() const {
1478 return controller.camera_state; 1613 return controller.camera_state;
1479} 1614}
1480 1615
1616RingSensorForce EmulatedController::GetRingSensorForce() const {
1617 return controller.ring_analog_state;
1618}
1619
1481const NfcState& EmulatedController::GetNfc() const { 1620const NfcState& EmulatedController::GetNfc() const {
1482 std::scoped_lock lock{mutex}; 1621 std::scoped_lock lock{mutex};
1483 return controller.nfc_state; 1622 return controller.nfc_state;
@@ -1520,4 +1659,74 @@ void EmulatedController::DeleteCallback(int key) {
1520 } 1659 }
1521 callback_list.erase(iterator); 1660 callback_list.erase(iterator);
1522} 1661}
1662
1663void EmulatedController::TurboButtonUpdate() {
1664 turbo_button_state = (turbo_button_state + 1) % (TURBO_BUTTON_DELAY * 2);
1665}
1666
1667NpadButton EmulatedController::GetTurboButtonMask() const {
1668 // Apply no mask when disabled
1669 if (turbo_button_state < TURBO_BUTTON_DELAY) {
1670 return {NpadButton::All};
1671 }
1672
1673 NpadButtonState button_mask{};
1674 for (std::size_t index = 0; index < controller.button_values.size(); ++index) {
1675 if (!controller.button_values[index].turbo) {
1676 continue;
1677 }
1678
1679 switch (index) {
1680 case Settings::NativeButton::A:
1681 button_mask.a.Assign(1);
1682 break;
1683 case Settings::NativeButton::B:
1684 button_mask.b.Assign(1);
1685 break;
1686 case Settings::NativeButton::X:
1687 button_mask.x.Assign(1);
1688 break;
1689 case Settings::NativeButton::Y:
1690 button_mask.y.Assign(1);
1691 break;
1692 case Settings::NativeButton::L:
1693 button_mask.l.Assign(1);
1694 break;
1695 case Settings::NativeButton::R:
1696 button_mask.r.Assign(1);
1697 break;
1698 case Settings::NativeButton::ZL:
1699 button_mask.zl.Assign(1);
1700 break;
1701 case Settings::NativeButton::ZR:
1702 button_mask.zr.Assign(1);
1703 break;
1704 case Settings::NativeButton::DLeft:
1705 button_mask.left.Assign(1);
1706 break;
1707 case Settings::NativeButton::DUp:
1708 button_mask.up.Assign(1);
1709 break;
1710 case Settings::NativeButton::DRight:
1711 button_mask.right.Assign(1);
1712 break;
1713 case Settings::NativeButton::DDown:
1714 button_mask.down.Assign(1);
1715 break;
1716 case Settings::NativeButton::SL:
1717 button_mask.left_sl.Assign(1);
1718 button_mask.right_sl.Assign(1);
1719 break;
1720 case Settings::NativeButton::SR:
1721 button_mask.left_sr.Assign(1);
1722 button_mask.right_sr.Assign(1);
1723 break;
1724 default:
1725 break;
1726 }
1727 }
1728
1729 return static_cast<NpadButton>(~button_mask.raw);
1730}
1731
1523} // namespace Core::HID 1732} // namespace Core::HID
diff --git a/src/core/hid/emulated_controller.h b/src/core/hid/emulated_controller.h
index a398543a6..b02bf35c4 100644
--- a/src/core/hid/emulated_controller.h
+++ b/src/core/hid/emulated_controller.h
@@ -35,19 +35,27 @@ using ControllerMotionDevices =
35 std::array<std::unique_ptr<Common::Input::InputDevice>, Settings::NativeMotion::NumMotions>; 35 std::array<std::unique_ptr<Common::Input::InputDevice>, Settings::NativeMotion::NumMotions>;
36using TriggerDevices = 36using TriggerDevices =
37 std::array<std::unique_ptr<Common::Input::InputDevice>, Settings::NativeTrigger::NumTriggers>; 37 std::array<std::unique_ptr<Common::Input::InputDevice>, Settings::NativeTrigger::NumTriggers>;
38using ColorDevices =
39 std::array<std::unique_ptr<Common::Input::InputDevice>, max_emulated_controllers>;
38using BatteryDevices = 40using BatteryDevices =
39 std::array<std::unique_ptr<Common::Input::InputDevice>, max_emulated_controllers>; 41 std::array<std::unique_ptr<Common::Input::InputDevice>, max_emulated_controllers>;
40using CameraDevices = std::unique_ptr<Common::Input::InputDevice>; 42using CameraDevices =
41using NfcDevices = std::unique_ptr<Common::Input::InputDevice>; 43 std::array<std::unique_ptr<Common::Input::InputDevice>, max_emulated_controllers>;
44using RingAnalogDevices =
45 std::array<std::unique_ptr<Common::Input::InputDevice>, max_emulated_controllers>;
46using NfcDevices =
47 std::array<std::unique_ptr<Common::Input::InputDevice>, max_emulated_controllers>;
42using OutputDevices = std::array<std::unique_ptr<Common::Input::OutputDevice>, output_devices_size>; 48using OutputDevices = std::array<std::unique_ptr<Common::Input::OutputDevice>, output_devices_size>;
43 49
44using ButtonParams = std::array<Common::ParamPackage, Settings::NativeButton::NumButtons>; 50using ButtonParams = std::array<Common::ParamPackage, Settings::NativeButton::NumButtons>;
45using StickParams = std::array<Common::ParamPackage, Settings::NativeAnalog::NumAnalogs>; 51using StickParams = std::array<Common::ParamPackage, Settings::NativeAnalog::NumAnalogs>;
46using ControllerMotionParams = std::array<Common::ParamPackage, Settings::NativeMotion::NumMotions>; 52using ControllerMotionParams = std::array<Common::ParamPackage, Settings::NativeMotion::NumMotions>;
47using TriggerParams = std::array<Common::ParamPackage, Settings::NativeTrigger::NumTriggers>; 53using TriggerParams = std::array<Common::ParamPackage, Settings::NativeTrigger::NumTriggers>;
54using ColorParams = std::array<Common::ParamPackage, max_emulated_controllers>;
48using BatteryParams = std::array<Common::ParamPackage, max_emulated_controllers>; 55using BatteryParams = std::array<Common::ParamPackage, max_emulated_controllers>;
49using CameraParams = Common::ParamPackage; 56using CameraParams = std::array<Common::ParamPackage, max_emulated_controllers>;
50using NfcParams = Common::ParamPackage; 57using RingAnalogParams = std::array<Common::ParamPackage, max_emulated_controllers>;
58using NfcParams = std::array<Common::ParamPackage, max_emulated_controllers>;
51using OutputParams = std::array<Common::ParamPackage, output_devices_size>; 59using OutputParams = std::array<Common::ParamPackage, output_devices_size>;
52 60
53using ButtonValues = std::array<Common::Input::ButtonStatus, Settings::NativeButton::NumButtons>; 61using ButtonValues = std::array<Common::Input::ButtonStatus, Settings::NativeButton::NumButtons>;
@@ -58,6 +66,7 @@ using ControllerMotionValues = std::array<ControllerMotionInfo, Settings::Native
58using ColorValues = std::array<Common::Input::BodyColorStatus, max_emulated_controllers>; 66using ColorValues = std::array<Common::Input::BodyColorStatus, max_emulated_controllers>;
59using BatteryValues = std::array<Common::Input::BatteryStatus, max_emulated_controllers>; 67using BatteryValues = std::array<Common::Input::BatteryStatus, max_emulated_controllers>;
60using CameraValues = Common::Input::CameraStatus; 68using CameraValues = Common::Input::CameraStatus;
69using RingAnalogValue = Common::Input::AnalogStatus;
61using NfcValues = Common::Input::NfcStatus; 70using NfcValues = Common::Input::NfcStatus;
62using VibrationValues = std::array<Common::Input::VibrationStatus, max_emulated_controllers>; 71using VibrationValues = std::array<Common::Input::VibrationStatus, max_emulated_controllers>;
63 72
@@ -84,6 +93,10 @@ struct CameraState {
84 std::size_t sample{}; 93 std::size_t sample{};
85}; 94};
86 95
96struct RingSensorForce {
97 f32 force;
98};
99
87struct NfcState { 100struct NfcState {
88 Common::Input::NfcState state{}; 101 Common::Input::NfcState state{};
89 std::vector<u8> data{}; 102 std::vector<u8> data{};
@@ -116,6 +129,7 @@ struct ControllerStatus {
116 BatteryValues battery_values{}; 129 BatteryValues battery_values{};
117 VibrationValues vibration_values{}; 130 VibrationValues vibration_values{};
118 CameraValues camera_values{}; 131 CameraValues camera_values{};
132 RingAnalogValue ring_analog_value{};
119 NfcValues nfc_values{}; 133 NfcValues nfc_values{};
120 134
121 // Data for HID serices 135 // Data for HID serices
@@ -129,6 +143,7 @@ struct ControllerStatus {
129 ControllerColors colors_state{}; 143 ControllerColors colors_state{};
130 BatteryLevelState battery_state{}; 144 BatteryLevelState battery_state{};
131 CameraState camera_state{}; 145 CameraState camera_state{};
146 RingSensorForce ring_analog_state{};
132 NfcState nfc_state{}; 147 NfcState nfc_state{};
133}; 148};
134 149
@@ -141,6 +156,7 @@ enum class ControllerTriggerType {
141 Battery, 156 Battery,
142 Vibration, 157 Vibration,
143 IrSensor, 158 IrSensor,
159 RingController,
144 Nfc, 160 Nfc,
145 Connected, 161 Connected,
146 Disconnected, 162 Disconnected,
@@ -294,6 +310,9 @@ public:
294 /// Returns the latest camera status from the controller with parameters 310 /// Returns the latest camera status from the controller with parameters
295 CameraValues GetCameraValues() const; 311 CameraValues GetCameraValues() const;
296 312
313 /// Returns the latest status of analog input from the ring sensor with parameters
314 RingAnalogValue GetRingSensorValues() const;
315
297 /// Returns the latest status of button input for the hid::HomeButton service 316 /// Returns the latest status of button input for the hid::HomeButton service
298 HomeButtonState GetHomeButtons() const; 317 HomeButtonState GetHomeButtons() const;
299 318
@@ -324,6 +343,9 @@ public:
324 /// Returns the latest camera status from the controller 343 /// Returns the latest camera status from the controller
325 const CameraState& GetCamera() const; 344 const CameraState& GetCamera() const;
326 345
346 /// Returns the latest ringcon force sensor value
347 RingSensorForce GetRingSensorForce() const;
348
327 /// Returns the latest ntag status from the controller 349 /// Returns the latest ntag status from the controller
328 const NfcState& GetNfc() const; 350 const NfcState& GetNfc() const;
329 351
@@ -341,10 +363,12 @@ public:
341 363
342 /** 364 /**
343 * Sets the desired data to be polled from a controller 365 * Sets the desired data to be polled from a controller
366 * @param device_index index of the controller to set the polling mode
344 * @param polling_mode type of input desired buttons, gyro, nfc, ir, etc. 367 * @param polling_mode type of input desired buttons, gyro, nfc, ir, etc.
345 * @return true if SetPollingMode was successfull 368 * @return driver result from this command
346 */ 369 */
347 bool SetPollingMode(Common::Input::PollingMode polling_mode); 370 Common::Input::DriverResult SetPollingMode(EmulatedDeviceIndex device_index,
371 Common::Input::PollingMode polling_mode);
348 372
349 /** 373 /**
350 * Sets the desired camera format to be polled from a controller 374 * Sets the desired camera format to be polled from a controller
@@ -353,6 +377,15 @@ public:
353 */ 377 */
354 bool SetCameraFormat(Core::IrSensor::ImageTransferProcessorFormat camera_format); 378 bool SetCameraFormat(Core::IrSensor::ImageTransferProcessorFormat camera_format);
355 379
380 // Returns the current mapped ring device
381 Common::ParamPackage GetRingParam() const;
382
383 /**
384 * Updates the current mapped ring device
385 * @param param ParamPackage with ring sensor data to be mapped
386 */
387 void SetRingParam(Common::ParamPackage param);
388
356 /// Returns true if the device has nfc support 389 /// Returns true if the device has nfc support
357 bool HasNfc() const; 390 bool HasNfc() const;
358 391
@@ -378,6 +411,9 @@ public:
378 */ 411 */
379 void DeleteCallback(int key); 412 void DeleteCallback(int key);
380 413
414 /// Swaps the state of the turbo buttons
415 void TurboButtonUpdate();
416
381private: 417private:
382 /// creates input devices from params 418 /// creates input devices from params
383 void LoadDevices(); 419 void LoadDevices();
@@ -433,9 +469,16 @@ private:
433 void SetMotion(const Common::Input::CallbackStatus& callback, std::size_t index); 469 void SetMotion(const Common::Input::CallbackStatus& callback, std::size_t index);
434 470
435 /** 471 /**
472 * Updates the color status of the controller
473 * @param callback A CallbackStatus containing the color status
474 * @param index color ID of the to be updated
475 */
476 void SetColors(const Common::Input::CallbackStatus& callback, std::size_t index);
477
478 /**
436 * Updates the battery status of the controller 479 * Updates the battery status of the controller
437 * @param callback A CallbackStatus containing the battery status 480 * @param callback A CallbackStatus containing the battery status
438 * @param index Button ID of the to be updated 481 * @param index battery ID of the to be updated
439 */ 482 */
440 void SetBattery(const Common::Input::CallbackStatus& callback, std::size_t index); 483 void SetBattery(const Common::Input::CallbackStatus& callback, std::size_t index);
441 484
@@ -446,6 +489,12 @@ private:
446 void SetCamera(const Common::Input::CallbackStatus& callback); 489 void SetCamera(const Common::Input::CallbackStatus& callback);
447 490
448 /** 491 /**
492 * Updates the ring analog sensor status of the ring controller
493 * @param callback A CallbackStatus containing the force status
494 */
495 void SetRingAnalog(const Common::Input::CallbackStatus& callback);
496
497 /**
449 * Updates the nfc status of the controller 498 * Updates the nfc status of the controller
450 * @param callback A CallbackStatus containing the nfc status 499 * @param callback A CallbackStatus containing the nfc status
451 */ 500 */
@@ -465,6 +514,8 @@ private:
465 */ 514 */
466 void TriggerOnChange(ControllerTriggerType type, bool is_service_update); 515 void TriggerOnChange(ControllerTriggerType type, bool is_service_update);
467 516
517 NpadButton GetTurboButtonMask() const;
518
468 const NpadIdType npad_id_type; 519 const NpadIdType npad_id_type;
469 NpadStyleIndex npad_type{NpadStyleIndex::None}; 520 NpadStyleIndex npad_type{NpadStyleIndex::None};
470 NpadStyleIndex original_npad_type{NpadStyleIndex::None}; 521 NpadStyleIndex original_npad_type{NpadStyleIndex::None};
@@ -474,6 +525,7 @@ private:
474 bool system_buttons_enabled{true}; 525 bool system_buttons_enabled{true};
475 f32 motion_sensitivity{0.01f}; 526 f32 motion_sensitivity{0.01f};
476 bool force_update_motion{false}; 527 bool force_update_motion{false};
528 u32 turbo_button_state{0};
477 529
478 // Temporary values to avoid doing changes while the controller is in configuring mode 530 // Temporary values to avoid doing changes while the controller is in configuring mode
479 NpadStyleIndex tmp_npad_type{NpadStyleIndex::None}; 531 NpadStyleIndex tmp_npad_type{NpadStyleIndex::None};
@@ -484,7 +536,9 @@ private:
484 ControllerMotionParams motion_params; 536 ControllerMotionParams motion_params;
485 TriggerParams trigger_params; 537 TriggerParams trigger_params;
486 BatteryParams battery_params; 538 BatteryParams battery_params;
539 ColorParams color_params;
487 CameraParams camera_params; 540 CameraParams camera_params;
541 RingAnalogParams ring_params;
488 NfcParams nfc_params; 542 NfcParams nfc_params;
489 OutputParams output_params; 543 OutputParams output_params;
490 544
@@ -493,7 +547,9 @@ private:
493 ControllerMotionDevices motion_devices; 547 ControllerMotionDevices motion_devices;
494 TriggerDevices trigger_devices; 548 TriggerDevices trigger_devices;
495 BatteryDevices battery_devices; 549 BatteryDevices battery_devices;
550 ColorDevices color_devices;
496 CameraDevices camera_devices; 551 CameraDevices camera_devices;
552 RingAnalogDevices ring_analog_devices;
497 NfcDevices nfc_devices; 553 NfcDevices nfc_devices;
498 OutputDevices output_devices; 554 OutputDevices output_devices;
499 555
diff --git a/src/core/hid/emulated_devices.cpp b/src/core/hid/emulated_devices.cpp
index e421828d2..836f32c0f 100644
--- a/src/core/hid/emulated_devices.cpp
+++ b/src/core/hid/emulated_devices.cpp
@@ -14,7 +14,6 @@ EmulatedDevices::EmulatedDevices() = default;
14EmulatedDevices::~EmulatedDevices() = default; 14EmulatedDevices::~EmulatedDevices() = default;
15 15
16void EmulatedDevices::ReloadFromSettings() { 16void EmulatedDevices::ReloadFromSettings() {
17 ring_params = Common::ParamPackage(Settings::values.ringcon_analogs);
18 ReloadInput(); 17 ReloadInput();
19} 18}
20 19
@@ -66,8 +65,6 @@ void EmulatedDevices::ReloadInput() {
66 key_index++; 65 key_index++;
67 } 66 }
68 67
69 ring_analog_device = Common::Input::CreateInputDevice(ring_params);
70
71 for (std::size_t index = 0; index < mouse_button_devices.size(); ++index) { 68 for (std::size_t index = 0; index < mouse_button_devices.size(); ++index) {
72 if (!mouse_button_devices[index]) { 69 if (!mouse_button_devices[index]) {
73 continue; 70 continue;
@@ -122,13 +119,6 @@ void EmulatedDevices::ReloadInput() {
122 }, 119 },
123 }); 120 });
124 } 121 }
125
126 if (ring_analog_device) {
127 ring_analog_device->SetCallback({
128 .on_change =
129 [this](const Common::Input::CallbackStatus& callback) { SetRingAnalog(callback); },
130 });
131 }
132} 122}
133 123
134void EmulatedDevices::UnloadInput() { 124void EmulatedDevices::UnloadInput() {
@@ -145,7 +135,6 @@ void EmulatedDevices::UnloadInput() {
145 for (auto& button : keyboard_modifier_devices) { 135 for (auto& button : keyboard_modifier_devices) {
146 button.reset(); 136 button.reset();
147 } 137 }
148 ring_analog_device.reset();
149} 138}
150 139
151void EmulatedDevices::EnableConfiguration() { 140void EmulatedDevices::EnableConfiguration() {
@@ -165,7 +154,6 @@ void EmulatedDevices::SaveCurrentConfig() {
165 if (!is_configuring) { 154 if (!is_configuring) {
166 return; 155 return;
167 } 156 }
168 Settings::values.ringcon_analogs = ring_params.Serialize();
169} 157}
170 158
171void EmulatedDevices::RestoreConfig() { 159void EmulatedDevices::RestoreConfig() {
@@ -175,15 +163,6 @@ void EmulatedDevices::RestoreConfig() {
175 ReloadFromSettings(); 163 ReloadFromSettings();
176} 164}
177 165
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
187void EmulatedDevices::SetKeyboardButton(const Common::Input::CallbackStatus& callback, 166void EmulatedDevices::SetKeyboardButton(const Common::Input::CallbackStatus& callback,
188 std::size_t index) { 167 std::size_t index) {
189 if (index >= device_status.keyboard_values.size()) { 168 if (index >= device_status.keyboard_values.size()) {
@@ -430,23 +409,6 @@ void EmulatedDevices::SetMouseStick(const Common::Input::CallbackStatus& callbac
430 TriggerOnChange(DeviceTriggerType::Mouse); 409 TriggerOnChange(DeviceTriggerType::Mouse);
431} 410}
432 411
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
450KeyboardValues EmulatedDevices::GetKeyboardValues() const { 412KeyboardValues EmulatedDevices::GetKeyboardValues() const {
451 std::scoped_lock lock{mutex}; 413 std::scoped_lock lock{mutex};
452 return device_status.keyboard_values; 414 return device_status.keyboard_values;
@@ -462,10 +424,6 @@ MouseButtonValues EmulatedDevices::GetMouseButtonsValues() const {
462 return device_status.mouse_button_values; 424 return device_status.mouse_button_values;
463} 425}
464 426
465RingAnalogValue EmulatedDevices::GetRingSensorValues() const {
466 return device_status.ring_analog_value;
467}
468
469KeyboardKey EmulatedDevices::GetKeyboard() const { 427KeyboardKey EmulatedDevices::GetKeyboard() const {
470 std::scoped_lock lock{mutex}; 428 std::scoped_lock lock{mutex};
471 return device_status.keyboard_state; 429 return device_status.keyboard_state;
@@ -491,10 +449,6 @@ AnalogStickState EmulatedDevices::GetMouseWheel() const {
491 return device_status.mouse_wheel_state; 449 return device_status.mouse_wheel_state;
492} 450}
493 451
494RingSensorForce EmulatedDevices::GetRingSensorForce() const {
495 return device_status.ring_analog_state;
496}
497
498void EmulatedDevices::TriggerOnChange(DeviceTriggerType type) { 452void EmulatedDevices::TriggerOnChange(DeviceTriggerType type) {
499 std::scoped_lock lock{callback_mutex}; 453 std::scoped_lock lock{callback_mutex};
500 for (const auto& poller_pair : callback_list) { 454 for (const auto& poller_pair : callback_list) {
diff --git a/src/core/hid/emulated_devices.h b/src/core/hid/emulated_devices.h
index 4cdbf9dc6..76f9150df 100644
--- a/src/core/hid/emulated_devices.h
+++ b/src/core/hid/emulated_devices.h
@@ -26,11 +26,9 @@ 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>;
30 29
31using MouseButtonParams = 30using MouseButtonParams =
32 std::array<Common::ParamPackage, Settings::NativeMouseButton::NumMouseButtons>; 31 std::array<Common::ParamPackage, Settings::NativeMouseButton::NumMouseButtons>;
33using RingAnalogParams = Common::ParamPackage;
34 32
35using KeyboardValues = 33using KeyboardValues =
36 std::array<Common::Input::ButtonStatus, Settings::NativeKeyboard::NumKeyboardKeys>; 34 std::array<Common::Input::ButtonStatus, Settings::NativeKeyboard::NumKeyboardKeys>;
@@ -41,17 +39,12 @@ using MouseButtonValues =
41using MouseAnalogValues = 39using MouseAnalogValues =
42 std::array<Common::Input::AnalogStatus, Settings::NativeMouseWheel::NumMouseWheels>; 40 std::array<Common::Input::AnalogStatus, Settings::NativeMouseWheel::NumMouseWheels>;
43using MouseStickValue = Common::Input::TouchStatus; 41using MouseStickValue = Common::Input::TouchStatus;
44using RingAnalogValue = Common::Input::AnalogStatus;
45 42
46struct MousePosition { 43struct MousePosition {
47 f32 x; 44 f32 x;
48 f32 y; 45 f32 y;
49}; 46};
50 47
51struct RingSensorForce {
52 f32 force;
53};
54
55struct DeviceStatus { 48struct DeviceStatus {
56 // Data from input_common 49 // Data from input_common
57 KeyboardValues keyboard_values{}; 50 KeyboardValues keyboard_values{};
@@ -59,7 +52,6 @@ struct DeviceStatus {
59 MouseButtonValues mouse_button_values{}; 52 MouseButtonValues mouse_button_values{};
60 MouseAnalogValues mouse_analog_values{}; 53 MouseAnalogValues mouse_analog_values{};
61 MouseStickValue mouse_stick_value{}; 54 MouseStickValue mouse_stick_value{};
62 RingAnalogValue ring_analog_value{};
63 55
64 // Data for HID serices 56 // Data for HID serices
65 KeyboardKey keyboard_state{}; 57 KeyboardKey keyboard_state{};
@@ -67,7 +59,6 @@ struct DeviceStatus {
67 MouseButton mouse_button_state{}; 59 MouseButton mouse_button_state{};
68 MousePosition mouse_position_state{}; 60 MousePosition mouse_position_state{};
69 AnalogStickState mouse_wheel_state{}; 61 AnalogStickState mouse_wheel_state{};
70 RingSensorForce ring_analog_state{};
71}; 62};
72 63
73enum class DeviceTriggerType { 64enum class DeviceTriggerType {
@@ -138,9 +129,6 @@ public:
138 /// Returns the latest status of button input from the mouse with parameters 129 /// Returns the latest status of button input from the mouse with parameters
139 MouseButtonValues GetMouseButtonsValues() const; 130 MouseButtonValues GetMouseButtonsValues() const;
140 131
141 /// Returns the latest status of analog input from the ring sensor with parameters
142 RingAnalogValue GetRingSensorValues() const;
143
144 /// Returns the latest status of button input from the keyboard 132 /// Returns the latest status of button input from the keyboard
145 KeyboardKey GetKeyboard() const; 133 KeyboardKey GetKeyboard() const;
146 134
@@ -156,9 +144,6 @@ public:
156 /// Returns the latest mouse wheel change 144 /// Returns the latest mouse wheel change
157 AnalogStickState GetMouseWheel() const; 145 AnalogStickState GetMouseWheel() const;
158 146
159 /// Returns the latest ringcon force sensor value
160 RingSensorForce GetRingSensorForce() const;
161
162 /** 147 /**
163 * Adds a callback to the list of events 148 * Adds a callback to the list of events
164 * @param update_callback InterfaceUpdateCallback that will be triggered 149 * @param update_callback InterfaceUpdateCallback that will be triggered
@@ -224,14 +209,11 @@ private:
224 209
225 bool is_configuring{false}; 210 bool is_configuring{false};
226 211
227 RingAnalogParams ring_params;
228
229 KeyboardDevices keyboard_devices; 212 KeyboardDevices keyboard_devices;
230 KeyboardModifierDevices keyboard_modifier_devices; 213 KeyboardModifierDevices keyboard_modifier_devices;
231 MouseButtonDevices mouse_button_devices; 214 MouseButtonDevices mouse_button_devices;
232 MouseAnalogDevices mouse_analog_devices; 215 MouseAnalogDevices mouse_analog_devices;
233 MouseStickDevice mouse_stick_device; 216 MouseStickDevice mouse_stick_device;
234 RingAnalogDevice ring_analog_device;
235 217
236 mutable std::mutex mutex; 218 mutable std::mutex mutex;
237 mutable std::mutex callback_mutex; 219 mutable std::mutex callback_mutex;
diff --git a/src/core/hid/input_converter.cpp b/src/core/hid/input_converter.cpp
index 502692875..3f7b8c090 100644
--- a/src/core/hid/input_converter.cpp
+++ b/src/core/hid/input_converter.cpp
@@ -304,6 +304,18 @@ Common::Input::NfcStatus TransformToNfc(const Common::Input::CallbackStatus& cal
304 return nfc; 304 return nfc;
305} 305}
306 306
307Common::Input::BodyColorStatus TransformToColor(const Common::Input::CallbackStatus& callback) {
308 switch (callback.type) {
309 case Common::Input::InputType::Color:
310 return callback.color_status;
311 break;
312 default:
313 LOG_ERROR(Input, "Conversion from type {} to color not implemented", callback.type);
314 return {};
315 break;
316 }
317}
318
307void SanitizeAnalog(Common::Input::AnalogStatus& analog, bool clamp_value) { 319void SanitizeAnalog(Common::Input::AnalogStatus& analog, bool clamp_value) {
308 const auto& properties = analog.properties; 320 const auto& properties = analog.properties;
309 float& raw_value = analog.raw_value; 321 float& raw_value = analog.raw_value;
diff --git a/src/core/hid/input_converter.h b/src/core/hid/input_converter.h
index b7eb6e660..c51c03e57 100644
--- a/src/core/hid/input_converter.h
+++ b/src/core/hid/input_converter.h
@@ -88,11 +88,19 @@ Common::Input::CameraStatus TransformToCamera(const Common::Input::CallbackStatu
88 * Converts raw input data into a valid nfc status. 88 * Converts raw input data into a valid nfc status.
89 * 89 *
90 * @param callback Supported callbacks: Nfc. 90 * @param callback Supported callbacks: Nfc.
91 * @return A valid CameraObject object. 91 * @return A valid data tag vector.
92 */ 92 */
93Common::Input::NfcStatus TransformToNfc(const Common::Input::CallbackStatus& callback); 93Common::Input::NfcStatus TransformToNfc(const Common::Input::CallbackStatus& callback);
94 94
95/** 95/**
96 * Converts raw input data into a valid color status.
97 *
98 * @param callback Supported callbacks: Color.
99 * @return A valid Color object.
100 */
101Common::Input::BodyColorStatus TransformToColor(const Common::Input::CallbackStatus& callback);
102
103/**
96 * Converts raw analog data into a valid analog value 104 * Converts raw analog data into a valid analog value
97 * @param analog An analog object containing raw data and properties 105 * @param analog An analog object containing raw data and properties
98 * @param clamp_value determines if the value needs to be clamped between -1.0f and 1.0f. 106 * @param clamp_value determines if the value needs to be clamped between -1.0f and 1.0f.
diff --git a/src/core/hle/kernel/hle_ipc.cpp b/src/core/hle/kernel/hle_ipc.cpp
index 738b6d0f1..494151eef 100644
--- a/src/core/hle/kernel/hle_ipc.cpp
+++ b/src/core/hle/kernel/hle_ipc.cpp
@@ -11,6 +11,7 @@
11#include "common/common_funcs.h" 11#include "common/common_funcs.h"
12#include "common/common_types.h" 12#include "common/common_types.h"
13#include "common/logging/log.h" 13#include "common/logging/log.h"
14#include "common/scratch_buffer.h"
14#include "core/hle/ipc_helpers.h" 15#include "core/hle/ipc_helpers.h"
15#include "core/hle/kernel/hle_ipc.h" 16#include "core/hle/kernel/hle_ipc.h"
16#include "core/hle/kernel/k_auto_object.h" 17#include "core/hle/kernel/k_auto_object.h"
@@ -325,7 +326,7 @@ Result HLERequestContext::WriteToOutgoingCommandBuffer(KThread& requesting_threa
325 return ResultSuccess; 326 return ResultSuccess;
326} 327}
327 328
328std::vector<u8> HLERequestContext::ReadBuffer(std::size_t buffer_index) const { 329std::vector<u8> HLERequestContext::ReadBufferCopy(std::size_t buffer_index) const {
329 const bool is_buffer_a{BufferDescriptorA().size() > buffer_index && 330 const bool is_buffer_a{BufferDescriptorA().size() > buffer_index &&
330 BufferDescriptorA()[buffer_index].Size()}; 331 BufferDescriptorA()[buffer_index].Size()};
331 if (is_buffer_a) { 332 if (is_buffer_a) {
@@ -345,6 +346,33 @@ std::vector<u8> HLERequestContext::ReadBuffer(std::size_t buffer_index) const {
345 } 346 }
346} 347}
347 348
349std::span<const u8> HLERequestContext::ReadBuffer(std::size_t buffer_index) const {
350 static thread_local std::array<Common::ScratchBuffer<u8>, 2> read_buffer_a;
351 static thread_local std::array<Common::ScratchBuffer<u8>, 2> read_buffer_x;
352
353 const bool is_buffer_a{BufferDescriptorA().size() > buffer_index &&
354 BufferDescriptorA()[buffer_index].Size()};
355 if (is_buffer_a) {
356 ASSERT_OR_EXECUTE_MSG(
357 BufferDescriptorA().size() > buffer_index, { return {}; },
358 "BufferDescriptorA invalid buffer_index {}", buffer_index);
359 auto& read_buffer = read_buffer_a[buffer_index];
360 read_buffer.resize_destructive(BufferDescriptorA()[buffer_index].Size());
361 memory.ReadBlock(BufferDescriptorA()[buffer_index].Address(), read_buffer.data(),
362 read_buffer.size());
363 return read_buffer;
364 } else {
365 ASSERT_OR_EXECUTE_MSG(
366 BufferDescriptorX().size() > buffer_index, { return {}; },
367 "BufferDescriptorX invalid buffer_index {}", buffer_index);
368 auto& read_buffer = read_buffer_x[buffer_index];
369 read_buffer.resize_destructive(BufferDescriptorX()[buffer_index].Size());
370 memory.ReadBlock(BufferDescriptorX()[buffer_index].Address(), read_buffer.data(),
371 read_buffer.size());
372 return read_buffer;
373 }
374}
375
348std::size_t HLERequestContext::WriteBuffer(const void* buffer, std::size_t size, 376std::size_t HLERequestContext::WriteBuffer(const void* buffer, std::size_t size,
349 std::size_t buffer_index) const { 377 std::size_t buffer_index) const {
350 if (size == 0) { 378 if (size == 0) {
diff --git a/src/core/hle/kernel/hle_ipc.h b/src/core/hle/kernel/hle_ipc.h
index e252b5f4b..5bf4f171b 100644
--- a/src/core/hle/kernel/hle_ipc.h
+++ b/src/core/hle/kernel/hle_ipc.h
@@ -7,6 +7,7 @@
7#include <functional> 7#include <functional>
8#include <memory> 8#include <memory>
9#include <optional> 9#include <optional>
10#include <span>
10#include <string> 11#include <string>
11#include <type_traits> 12#include <type_traits>
12#include <vector> 13#include <vector>
@@ -270,8 +271,11 @@ public:
270 return domain_message_header.has_value(); 271 return domain_message_header.has_value();
271 } 272 }
272 273
273 /// Helper function to read a buffer using the appropriate buffer descriptor 274 /// Helper function to get a span of a buffer using the appropriate buffer descriptor
274 [[nodiscard]] std::vector<u8> ReadBuffer(std::size_t buffer_index = 0) const; 275 [[nodiscard]] std::span<const u8> ReadBuffer(std::size_t buffer_index = 0) const;
276
277 /// Helper function to read a copy of a buffer using the appropriate buffer descriptor
278 [[nodiscard]] std::vector<u8> ReadBufferCopy(std::size_t buffer_index = 0) const;
275 279
276 /// Helper function to write a buffer using the appropriate buffer descriptor 280 /// Helper function to write a buffer using the appropriate buffer descriptor
277 std::size_t WriteBuffer(const void* buffer, std::size_t size, 281 std::size_t WriteBuffer(const void* buffer, std::size_t size,
diff --git a/src/core/hle/kernel/init/init_slab_setup.cpp b/src/core/hle/kernel/init/init_slab_setup.cpp
index 7b363eb1e..571acf4b2 100644
--- a/src/core/hle/kernel/init/init_slab_setup.cpp
+++ b/src/core/hle/kernel/init/init_slab_setup.cpp
@@ -11,6 +11,7 @@
11#include "core/hle/kernel/init/init_slab_setup.h" 11#include "core/hle/kernel/init/init_slab_setup.h"
12#include "core/hle/kernel/k_code_memory.h" 12#include "core/hle/kernel/k_code_memory.h"
13#include "core/hle/kernel/k_debug.h" 13#include "core/hle/kernel/k_debug.h"
14#include "core/hle/kernel/k_device_address_space.h"
14#include "core/hle/kernel/k_event.h" 15#include "core/hle/kernel/k_event.h"
15#include "core/hle/kernel/k_event_info.h" 16#include "core/hle/kernel/k_event_info.h"
16#include "core/hle/kernel/k_memory_layout.h" 17#include "core/hle/kernel/k_memory_layout.h"
@@ -43,6 +44,7 @@ namespace Kernel::Init {
43 HANDLER(KSharedMemoryInfo, (SLAB_COUNT(KSharedMemory) * 8), ##__VA_ARGS__) \ 44 HANDLER(KSharedMemoryInfo, (SLAB_COUNT(KSharedMemory) * 8), ##__VA_ARGS__) \
44 HANDLER(KTransferMemory, (SLAB_COUNT(KTransferMemory)), ##__VA_ARGS__) \ 45 HANDLER(KTransferMemory, (SLAB_COUNT(KTransferMemory)), ##__VA_ARGS__) \
45 HANDLER(KCodeMemory, (SLAB_COUNT(KCodeMemory)), ##__VA_ARGS__) \ 46 HANDLER(KCodeMemory, (SLAB_COUNT(KCodeMemory)), ##__VA_ARGS__) \
47 HANDLER(KDeviceAddressSpace, (SLAB_COUNT(KDeviceAddressSpace)), ##__VA_ARGS__) \
46 HANDLER(KSession, (SLAB_COUNT(KSession)), ##__VA_ARGS__) \ 48 HANDLER(KSession, (SLAB_COUNT(KSession)), ##__VA_ARGS__) \
47 HANDLER(KThreadLocalPage, \ 49 HANDLER(KThreadLocalPage, \
48 (SLAB_COUNT(KProcess) + (SLAB_COUNT(KProcess) + SLAB_COUNT(KThread)) / 8), \ 50 (SLAB_COUNT(KProcess) + (SLAB_COUNT(KProcess) + SLAB_COUNT(KThread)) / 8), \
diff --git a/src/core/hle/kernel/k_auto_object.h b/src/core/hle/kernel/k_auto_object.h
index 2827763d5..e8118c2b8 100644
--- a/src/core/hle/kernel/k_auto_object.h
+++ b/src/core/hle/kernel/k_auto_object.h
@@ -24,9 +24,7 @@ private:
24 friend class ::Kernel::KClassTokenGenerator; \ 24 friend class ::Kernel::KClassTokenGenerator; \
25 static constexpr inline auto ObjectType = ::Kernel::KClassTokenGenerator::ObjectType::CLASS; \ 25 static constexpr inline auto ObjectType = ::Kernel::KClassTokenGenerator::ObjectType::CLASS; \
26 static constexpr inline const char* const TypeName = #CLASS; \ 26 static constexpr inline const char* const TypeName = #CLASS; \
27 static constexpr inline ClassTokenType ClassToken() { \ 27 static constexpr inline ClassTokenType ClassToken() { return ::Kernel::ClassToken<CLASS>; } \
28 return ::Kernel::ClassToken<CLASS>; \
29 } \
30 \ 28 \
31public: \ 29public: \
32 YUZU_NON_COPYABLE(CLASS); \ 30 YUZU_NON_COPYABLE(CLASS); \
@@ -37,15 +35,9 @@ public:
37 constexpr ClassTokenType Token = ClassToken(); \ 35 constexpr ClassTokenType Token = ClassToken(); \
38 return TypeObj(TypeName, Token); \ 36 return TypeObj(TypeName, Token); \
39 } \ 37 } \
40 static constexpr const char* GetStaticTypeName() { \ 38 static constexpr const char* GetStaticTypeName() { return TypeName; } \
41 return TypeName; \ 39 virtual TypeObj GetTypeObj() ATTRIBUTE { return GetStaticTypeObj(); } \
42 } \ 40 virtual const char* GetTypeName() ATTRIBUTE { return GetStaticTypeName(); } \
43 virtual TypeObj GetTypeObj() ATTRIBUTE { \
44 return GetStaticTypeObj(); \
45 } \
46 virtual const char* GetTypeName() ATTRIBUTE { \
47 return GetStaticTypeName(); \
48 } \
49 \ 41 \
50private: \ 42private: \
51 constexpr bool operator!=(const TypeObj& rhs) 43 constexpr bool operator!=(const TypeObj& rhs)
@@ -245,8 +237,8 @@ public:
245 } 237 }
246 238
247 template <typename U> 239 template <typename U>
248 requires(std::derived_from<T, U> || 240 requires(std::derived_from<T, U> || std::derived_from<U, T>)
249 std::derived_from<U, T>) constexpr KScopedAutoObject(KScopedAutoObject<U>&& rhs) { 241 constexpr KScopedAutoObject(KScopedAutoObject<U>&& rhs) {
250 if constexpr (std::derived_from<U, T>) { 242 if constexpr (std::derived_from<U, T>) {
251 // Upcast. 243 // Upcast.
252 m_obj = rhs.m_obj; 244 m_obj = rhs.m_obj;
diff --git a/src/core/hle/kernel/k_capabilities.cpp b/src/core/hle/kernel/k_capabilities.cpp
new file mode 100644
index 000000000..64f1d7371
--- /dev/null
+++ b/src/core/hle/kernel/k_capabilities.cpp
@@ -0,0 +1,358 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "core/hardware_properties.h"
5#include "core/hle/kernel/k_capabilities.h"
6#include "core/hle/kernel/k_memory_layout.h"
7#include "core/hle/kernel/k_page_table.h"
8#include "core/hle/kernel/kernel.h"
9#include "core/hle/kernel/svc_results.h"
10#include "core/hle/kernel/svc_version.h"
11
12namespace Kernel {
13
14Result KCapabilities::InitializeForKIP(std::span<const u32> kern_caps, KPageTable* page_table) {
15 // We're initializing an initial process.
16 m_svc_access_flags.reset();
17 m_irq_access_flags.reset();
18 m_debug_capabilities = 0;
19 m_handle_table_size = 0;
20 m_intended_kernel_version = 0;
21 m_program_type = 0;
22
23 // Initial processes may run on all cores.
24 constexpr u64 VirtMask = Core::Hardware::VirtualCoreMask;
25 constexpr u64 PhysMask = Core::Hardware::ConvertVirtualCoreMaskToPhysical(VirtMask);
26
27 m_core_mask = VirtMask;
28 m_phys_core_mask = PhysMask;
29
30 // Initial processes may use any user priority they like.
31 m_priority_mask = ~0xFULL;
32
33 // Here, Nintendo sets the kernel version to the current kernel version.
34 // We will follow suit and set the version to the highest supported kernel version.
35 KernelVersion intended_kernel_version{};
36 intended_kernel_version.major_version.Assign(Svc::SupportedKernelMajorVersion);
37 intended_kernel_version.minor_version.Assign(Svc::SupportedKernelMinorVersion);
38 m_intended_kernel_version = intended_kernel_version.raw;
39
40 // Parse the capabilities array.
41 R_RETURN(this->SetCapabilities(kern_caps, page_table));
42}
43
44Result KCapabilities::InitializeForUser(std::span<const u32> user_caps, KPageTable* page_table) {
45 // We're initializing a user process.
46 m_svc_access_flags.reset();
47 m_irq_access_flags.reset();
48 m_debug_capabilities = 0;
49 m_handle_table_size = 0;
50 m_intended_kernel_version = 0;
51 m_program_type = 0;
52
53 // User processes must specify what cores/priorities they can use.
54 m_core_mask = 0;
55 m_priority_mask = 0;
56
57 // Parse the user capabilities array.
58 R_RETURN(this->SetCapabilities(user_caps, page_table));
59}
60
61Result KCapabilities::SetCorePriorityCapability(const u32 cap) {
62 // We can't set core/priority if we've already set them.
63 R_UNLESS(m_core_mask == 0, ResultInvalidArgument);
64 R_UNLESS(m_priority_mask == 0, ResultInvalidArgument);
65
66 // Validate the core/priority.
67 CorePriority pack{cap};
68 const u32 min_core = pack.minimum_core_id;
69 const u32 max_core = pack.maximum_core_id;
70 const u32 max_prio = pack.lowest_thread_priority;
71 const u32 min_prio = pack.highest_thread_priority;
72
73 R_UNLESS(min_core <= max_core, ResultInvalidCombination);
74 R_UNLESS(min_prio <= max_prio, ResultInvalidCombination);
75 R_UNLESS(max_core < Core::Hardware::NumVirtualCores, ResultInvalidCoreId);
76
77 ASSERT(max_prio < Common::BitSize<u64>());
78
79 // Set core mask.
80 for (auto core_id = min_core; core_id <= max_core; core_id++) {
81 m_core_mask |= (1ULL << core_id);
82 }
83 ASSERT((m_core_mask & Core::Hardware::VirtualCoreMask) == m_core_mask);
84
85 // Set physical core mask.
86 m_phys_core_mask = Core::Hardware::ConvertVirtualCoreMaskToPhysical(m_core_mask);
87
88 // Set priority mask.
89 for (auto prio = min_prio; prio <= max_prio; prio++) {
90 m_priority_mask |= (1ULL << prio);
91 }
92
93 // We must have some core/priority we can use.
94 R_UNLESS(m_core_mask != 0, ResultInvalidArgument);
95 R_UNLESS(m_priority_mask != 0, ResultInvalidArgument);
96
97 // Processes must not have access to kernel thread priorities.
98 R_UNLESS((m_priority_mask & 0xF) == 0, ResultInvalidArgument);
99
100 R_SUCCEED();
101}
102
103Result KCapabilities::SetSyscallMaskCapability(const u32 cap, u32& set_svc) {
104 // Validate the index.
105 SyscallMask pack{cap};
106 const u32 mask = pack.mask;
107 const u32 index = pack.index;
108
109 const u32 index_flag = (1U << index);
110 R_UNLESS((set_svc & index_flag) == 0, ResultInvalidCombination);
111 set_svc |= index_flag;
112
113 // Set SVCs.
114 for (size_t i = 0; i < decltype(SyscallMask::mask)::bits; i++) {
115 const u32 svc_id = static_cast<u32>(decltype(SyscallMask::mask)::bits * index + i);
116 if (mask & (1U << i)) {
117 R_UNLESS(this->SetSvcAllowed(svc_id), ResultOutOfRange);
118 }
119 }
120
121 R_SUCCEED();
122}
123
124Result KCapabilities::MapRange_(const u32 cap, const u32 size_cap, KPageTable* page_table) {
125 const auto range_pack = MapRange{cap};
126 const auto size_pack = MapRangeSize{size_cap};
127
128 // Get/validate address/size
129 const u64 phys_addr = range_pack.address.Value() * PageSize;
130
131 // Validate reserved bits are unused.
132 R_UNLESS(size_pack.reserved.Value() == 0, ResultOutOfRange);
133
134 const size_t num_pages = size_pack.pages;
135 const size_t size = num_pages * PageSize;
136 R_UNLESS(num_pages != 0, ResultInvalidSize);
137 R_UNLESS(phys_addr < phys_addr + size, ResultInvalidAddress);
138 R_UNLESS(((phys_addr + size - 1) & ~PhysicalMapAllowedMask) == 0, ResultInvalidAddress);
139
140 // Do the mapping.
141 [[maybe_unused]] const KMemoryPermission perm = range_pack.read_only.Value()
142 ? KMemoryPermission::UserRead
143 : KMemoryPermission::UserReadWrite;
144 if (MapRangeSize{size_cap}.normal) {
145 // R_RETURN(page_table->MapStatic(phys_addr, size, perm));
146 } else {
147 // R_RETURN(page_table->MapIo(phys_addr, size, perm));
148 }
149
150 UNIMPLEMENTED();
151 R_SUCCEED();
152}
153
154Result KCapabilities::MapIoPage_(const u32 cap, KPageTable* page_table) {
155 // Get/validate address/size
156 const u64 phys_addr = MapIoPage{cap}.address.Value() * PageSize;
157 const size_t num_pages = 1;
158 const size_t size = num_pages * PageSize;
159 R_UNLESS(num_pages != 0, ResultInvalidSize);
160 R_UNLESS(phys_addr < phys_addr + size, ResultInvalidAddress);
161 R_UNLESS(((phys_addr + size - 1) & ~PhysicalMapAllowedMask) == 0, ResultInvalidAddress);
162
163 // Do the mapping.
164 // R_RETURN(page_table->MapIo(phys_addr, size, KMemoryPermission_UserReadWrite));
165
166 UNIMPLEMENTED();
167 R_SUCCEED();
168}
169
170template <typename F>
171Result KCapabilities::ProcessMapRegionCapability(const u32 cap, F f) {
172 // Define the allowed memory regions.
173 constexpr std::array<KMemoryRegionType, 4> MemoryRegions{
174 KMemoryRegionType_None,
175 KMemoryRegionType_KernelTraceBuffer,
176 KMemoryRegionType_OnMemoryBootImage,
177 KMemoryRegionType_DTB,
178 };
179
180 // Extract regions/read only.
181 const MapRegion pack{cap};
182 const std::array<RegionType, 3> types{pack.region0, pack.region1, pack.region2};
183 const std::array<u32, 3> ro{pack.read_only0, pack.read_only1, pack.read_only2};
184
185 for (size_t i = 0; i < types.size(); i++) {
186 const auto type = types[i];
187 const auto perm = ro[i] ? KMemoryPermission::UserRead : KMemoryPermission::UserReadWrite;
188 switch (type) {
189 case RegionType::NoMapping:
190 break;
191 case RegionType::KernelTraceBuffer:
192 case RegionType::OnMemoryBootImage:
193 case RegionType::DTB:
194 R_TRY(f(MemoryRegions[static_cast<u32>(type)], perm));
195 break;
196 default:
197 R_THROW(ResultNotFound);
198 }
199 }
200
201 R_SUCCEED();
202}
203
204Result KCapabilities::MapRegion_(const u32 cap, KPageTable* page_table) {
205 // Map each region into the process's page table.
206 R_RETURN(ProcessMapRegionCapability(
207 cap, [](KMemoryRegionType region_type, KMemoryPermission perm) -> Result {
208 // R_RETURN(page_table->MapRegion(region_type, perm));
209 UNIMPLEMENTED();
210 R_SUCCEED();
211 }));
212}
213
214Result KCapabilities::CheckMapRegion(KernelCore& kernel, const u32 cap) {
215 // Check that each region has a physical backing store.
216 R_RETURN(ProcessMapRegionCapability(
217 cap, [&](KMemoryRegionType region_type, KMemoryPermission perm) -> Result {
218 R_UNLESS(kernel.MemoryLayout().GetPhysicalMemoryRegionTree().FindFirstDerived(
219 region_type) != nullptr,
220 ResultOutOfRange);
221 R_SUCCEED();
222 }));
223}
224
225Result KCapabilities::SetInterruptPairCapability(const u32 cap) {
226 // Extract interrupts.
227 const InterruptPair pack{cap};
228 const std::array<u32, 2> ids{pack.interrupt_id0, pack.interrupt_id1};
229
230 for (size_t i = 0; i < ids.size(); i++) {
231 if (ids[i] != PaddingInterruptId) {
232 UNIMPLEMENTED();
233 // R_UNLESS(Kernel::GetInterruptManager().IsInterruptDefined(ids[i]), ResultOutOfRange);
234 // R_UNLESS(this->SetInterruptPermitted(ids[i]), ResultOutOfRange);
235 }
236 }
237
238 R_SUCCEED();
239}
240
241Result KCapabilities::SetProgramTypeCapability(const u32 cap) {
242 // Validate.
243 const ProgramType pack{cap};
244 R_UNLESS(pack.reserved == 0, ResultReservedUsed);
245
246 m_program_type = pack.type;
247 R_SUCCEED();
248}
249
250Result KCapabilities::SetKernelVersionCapability(const u32 cap) {
251 // Ensure we haven't set our version before.
252 R_UNLESS(KernelVersion{m_intended_kernel_version}.major_version == 0, ResultInvalidArgument);
253
254 // Set, ensure that we set a valid version.
255 m_intended_kernel_version = cap;
256 R_UNLESS(KernelVersion{m_intended_kernel_version}.major_version != 0, ResultInvalidArgument);
257
258 R_SUCCEED();
259}
260
261Result KCapabilities::SetHandleTableCapability(const u32 cap) {
262 // Validate.
263 const HandleTable pack{cap};
264 R_UNLESS(pack.reserved == 0, ResultReservedUsed);
265
266 m_handle_table_size = pack.size;
267 R_SUCCEED();
268}
269
270Result KCapabilities::SetDebugFlagsCapability(const u32 cap) {
271 // Validate.
272 const DebugFlags pack{cap};
273 R_UNLESS(pack.reserved == 0, ResultReservedUsed);
274
275 DebugFlags debug_capabilities{m_debug_capabilities};
276 debug_capabilities.allow_debug.Assign(pack.allow_debug);
277 debug_capabilities.force_debug.Assign(pack.force_debug);
278 m_debug_capabilities = debug_capabilities.raw;
279
280 R_SUCCEED();
281}
282
283Result KCapabilities::SetCapability(const u32 cap, u32& set_flags, u32& set_svc,
284 KPageTable* page_table) {
285 // Validate this is a capability we can act on.
286 const auto type = GetCapabilityType(cap);
287 R_UNLESS(type != CapabilityType::Invalid, ResultInvalidArgument);
288
289 // If the type is padding, we have no work to do.
290 R_SUCCEED_IF(type == CapabilityType::Padding);
291
292 // Check that we haven't already processed this capability.
293 const auto flag = GetCapabilityFlag(type);
294 R_UNLESS(((set_flags & InitializeOnceFlags) & flag) == 0, ResultInvalidCombination);
295 set_flags |= flag;
296
297 // Process the capability.
298 switch (type) {
299 case CapabilityType::CorePriority:
300 R_RETURN(this->SetCorePriorityCapability(cap));
301 case CapabilityType::SyscallMask:
302 R_RETURN(this->SetSyscallMaskCapability(cap, set_svc));
303 case CapabilityType::MapIoPage:
304 R_RETURN(this->MapIoPage_(cap, page_table));
305 case CapabilityType::MapRegion:
306 R_RETURN(this->MapRegion_(cap, page_table));
307 case CapabilityType::InterruptPair:
308 R_RETURN(this->SetInterruptPairCapability(cap));
309 case CapabilityType::ProgramType:
310 R_RETURN(this->SetProgramTypeCapability(cap));
311 case CapabilityType::KernelVersion:
312 R_RETURN(this->SetKernelVersionCapability(cap));
313 case CapabilityType::HandleTable:
314 R_RETURN(this->SetHandleTableCapability(cap));
315 case CapabilityType::DebugFlags:
316 R_RETURN(this->SetDebugFlagsCapability(cap));
317 default:
318 R_THROW(ResultInvalidArgument);
319 }
320}
321
322Result KCapabilities::SetCapabilities(std::span<const u32> caps, KPageTable* page_table) {
323 u32 set_flags = 0, set_svc = 0;
324
325 for (size_t i = 0; i < caps.size(); i++) {
326 const u32 cap{caps[i]};
327
328 if (GetCapabilityType(cap) == CapabilityType::MapRange) {
329 // Check that the pair cap exists.
330 R_UNLESS((++i) < caps.size(), ResultInvalidCombination);
331
332 // Check the pair cap is a map range cap.
333 const u32 size_cap{caps[i]};
334 R_UNLESS(GetCapabilityType(size_cap) == CapabilityType::MapRange,
335 ResultInvalidCombination);
336
337 // Map the range.
338 R_TRY(this->MapRange_(cap, size_cap, page_table));
339 } else {
340 R_TRY(this->SetCapability(cap, set_flags, set_svc, page_table));
341 }
342 }
343
344 R_SUCCEED();
345}
346
347Result KCapabilities::CheckCapabilities(KernelCore& kernel, std::span<const u32> caps) {
348 for (auto cap : caps) {
349 // Check the capability refers to a valid region.
350 if (GetCapabilityType(cap) == CapabilityType::MapRegion) {
351 R_TRY(CheckMapRegion(kernel, cap));
352 }
353 }
354
355 R_SUCCEED();
356}
357
358} // namespace Kernel
diff --git a/src/core/hle/kernel/k_capabilities.h b/src/core/hle/kernel/k_capabilities.h
new file mode 100644
index 000000000..cd96f8d23
--- /dev/null
+++ b/src/core/hle/kernel/k_capabilities.h
@@ -0,0 +1,295 @@
1
2// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
3// SPDX-License-Identifier: GPL-2.0-or-later
4
5#pragma once
6
7#include <bitset>
8#include <span>
9
10#include "common/bit_field.h"
11#include "common/common_types.h"
12
13#include "core/hle/kernel/svc_types.h"
14#include "core/hle/result.h"
15
16namespace Kernel {
17
18class KPageTable;
19class KernelCore;
20
21class KCapabilities {
22public:
23 constexpr explicit KCapabilities() = default;
24
25 Result InitializeForKIP(std::span<const u32> kern_caps, KPageTable* page_table);
26 Result InitializeForUser(std::span<const u32> user_caps, KPageTable* page_table);
27
28 static Result CheckCapabilities(KernelCore& kernel, std::span<const u32> user_caps);
29
30 constexpr u64 GetCoreMask() const {
31 return m_core_mask;
32 }
33
34 constexpr u64 GetPhysicalCoreMask() const {
35 return m_phys_core_mask;
36 }
37
38 constexpr u64 GetPriorityMask() const {
39 return m_priority_mask;
40 }
41
42 constexpr s32 GetHandleTableSize() const {
43 return m_handle_table_size;
44 }
45
46 constexpr const Svc::SvcAccessFlagSet& GetSvcPermissions() const {
47 return m_svc_access_flags;
48 }
49
50 constexpr bool IsPermittedSvc(u32 id) const {
51 return (id < m_svc_access_flags.size()) && m_svc_access_flags[id];
52 }
53
54 constexpr bool IsPermittedInterrupt(u32 id) const {
55 return (id < m_irq_access_flags.size()) && m_irq_access_flags[id];
56 }
57
58 constexpr bool IsPermittedDebug() const {
59 return DebugFlags{m_debug_capabilities}.allow_debug.Value() != 0;
60 }
61
62 constexpr bool CanForceDebug() const {
63 return DebugFlags{m_debug_capabilities}.force_debug.Value() != 0;
64 }
65
66 constexpr u32 GetIntendedKernelMajorVersion() const {
67 return KernelVersion{m_intended_kernel_version}.major_version;
68 }
69
70 constexpr u32 GetIntendedKernelMinorVersion() const {
71 return KernelVersion{m_intended_kernel_version}.minor_version;
72 }
73
74private:
75 static constexpr size_t InterruptIdCount = 0x400;
76 using InterruptFlagSet = std::bitset<InterruptIdCount>;
77
78 enum class CapabilityType : u32 {
79 CorePriority = (1U << 3) - 1,
80 SyscallMask = (1U << 4) - 1,
81 MapRange = (1U << 6) - 1,
82 MapIoPage = (1U << 7) - 1,
83 MapRegion = (1U << 10) - 1,
84 InterruptPair = (1U << 11) - 1,
85 ProgramType = (1U << 13) - 1,
86 KernelVersion = (1U << 14) - 1,
87 HandleTable = (1U << 15) - 1,
88 DebugFlags = (1U << 16) - 1,
89
90 Invalid = 0U,
91 Padding = ~0U,
92 };
93
94 using RawCapabilityValue = u32;
95
96 static constexpr CapabilityType GetCapabilityType(const RawCapabilityValue value) {
97 return static_cast<CapabilityType>((~value & (value + 1)) - 1);
98 }
99
100 static constexpr u32 GetCapabilityFlag(CapabilityType type) {
101 return static_cast<u32>(type) + 1;
102 }
103
104 template <CapabilityType Type>
105 static constexpr inline u32 CapabilityFlag = static_cast<u32>(Type) + 1;
106
107 template <CapabilityType Type>
108 static constexpr inline u32 CapabilityId = std::countr_zero(CapabilityFlag<Type>);
109
110 union CorePriority {
111 static_assert(CapabilityId<CapabilityType::CorePriority> + 1 == 4);
112
113 RawCapabilityValue raw;
114 BitField<0, 4, CapabilityType> id;
115 BitField<4, 6, u32> lowest_thread_priority;
116 BitField<10, 6, u32> highest_thread_priority;
117 BitField<16, 8, u32> minimum_core_id;
118 BitField<24, 8, u32> maximum_core_id;
119 };
120
121 union SyscallMask {
122 static_assert(CapabilityId<CapabilityType::SyscallMask> + 1 == 5);
123
124 RawCapabilityValue raw;
125 BitField<0, 5, CapabilityType> id;
126 BitField<5, 24, u32> mask;
127 BitField<29, 3, u32> index;
128 };
129
130 // #undef MESOSPHERE_ENABLE_LARGE_PHYSICAL_ADDRESS_CAPABILITIES
131 static constexpr u64 PhysicalMapAllowedMask = (1ULL << 36) - 1;
132
133 union MapRange {
134 static_assert(CapabilityId<CapabilityType::MapRange> + 1 == 7);
135
136 RawCapabilityValue raw;
137 BitField<0, 7, CapabilityType> id;
138 BitField<7, 24, u32> address;
139 BitField<31, 1, u32> read_only;
140 };
141
142 union MapRangeSize {
143 static_assert(CapabilityId<CapabilityType::MapRange> + 1 == 7);
144
145 RawCapabilityValue raw;
146 BitField<0, 7, CapabilityType> id;
147 BitField<7, 20, u32> pages;
148 BitField<27, 4, u32> reserved;
149 BitField<31, 1, u32> normal;
150 };
151
152 union MapIoPage {
153 static_assert(CapabilityId<CapabilityType::MapIoPage> + 1 == 8);
154
155 RawCapabilityValue raw;
156 BitField<0, 8, CapabilityType> id;
157 BitField<8, 24, u32> address;
158 };
159
160 enum class RegionType : u32 {
161 NoMapping = 0,
162 KernelTraceBuffer = 1,
163 OnMemoryBootImage = 2,
164 DTB = 3,
165 };
166
167 union MapRegion {
168 static_assert(CapabilityId<CapabilityType::MapRegion> + 1 == 11);
169
170 RawCapabilityValue raw;
171 BitField<0, 11, CapabilityType> id;
172 BitField<11, 6, RegionType> region0;
173 BitField<17, 1, u32> read_only0;
174 BitField<18, 6, RegionType> region1;
175 BitField<24, 1, u32> read_only1;
176 BitField<25, 6, RegionType> region2;
177 BitField<31, 1, u32> read_only2;
178 };
179
180 union InterruptPair {
181 static_assert(CapabilityId<CapabilityType::InterruptPair> + 1 == 12);
182
183 RawCapabilityValue raw;
184 BitField<0, 12, CapabilityType> id;
185 BitField<12, 10, u32> interrupt_id0;
186 BitField<22, 10, u32> interrupt_id1;
187 };
188
189 union ProgramType {
190 static_assert(CapabilityId<CapabilityType::ProgramType> + 1 == 14);
191
192 RawCapabilityValue raw;
193 BitField<0, 14, CapabilityType> id;
194 BitField<14, 3, u32> type;
195 BitField<17, 15, u32> reserved;
196 };
197
198 union KernelVersion {
199 static_assert(CapabilityId<CapabilityType::KernelVersion> + 1 == 15);
200
201 RawCapabilityValue raw;
202 BitField<0, 15, CapabilityType> id;
203 BitField<15, 4, u32> major_version;
204 BitField<19, 13, u32> minor_version;
205 };
206
207 union HandleTable {
208 static_assert(CapabilityId<CapabilityType::HandleTable> + 1 == 16);
209
210 RawCapabilityValue raw;
211 BitField<0, 16, CapabilityType> id;
212 BitField<16, 10, u32> size;
213 BitField<26, 6, u32> reserved;
214 };
215
216 union DebugFlags {
217 static_assert(CapabilityId<CapabilityType::DebugFlags> + 1 == 17);
218
219 RawCapabilityValue raw;
220 BitField<0, 17, CapabilityType> id;
221 BitField<17, 1, u32> allow_debug;
222 BitField<18, 1, u32> force_debug;
223 BitField<19, 13, u32> reserved;
224 };
225
226 static_assert(sizeof(CorePriority) == 4);
227 static_assert(sizeof(SyscallMask) == 4);
228 static_assert(sizeof(MapRange) == 4);
229 static_assert(sizeof(MapRangeSize) == 4);
230 static_assert(sizeof(MapIoPage) == 4);
231 static_assert(sizeof(MapRegion) == 4);
232 static_assert(sizeof(InterruptPair) == 4);
233 static_assert(sizeof(ProgramType) == 4);
234 static_assert(sizeof(KernelVersion) == 4);
235 static_assert(sizeof(HandleTable) == 4);
236 static_assert(sizeof(DebugFlags) == 4);
237
238 static constexpr u32 InitializeOnceFlags =
239 CapabilityFlag<CapabilityType::CorePriority> | CapabilityFlag<CapabilityType::ProgramType> |
240 CapabilityFlag<CapabilityType::KernelVersion> |
241 CapabilityFlag<CapabilityType::HandleTable> | CapabilityFlag<CapabilityType::DebugFlags>;
242
243 static const u32 PaddingInterruptId = 0x3FF;
244 static_assert(PaddingInterruptId < InterruptIdCount);
245
246private:
247 constexpr bool SetSvcAllowed(u32 id) {
248 if (id < m_svc_access_flags.size()) [[likely]] {
249 m_svc_access_flags[id] = true;
250 return true;
251 } else {
252 return false;
253 }
254 }
255
256 constexpr bool SetInterruptPermitted(u32 id) {
257 if (id < m_irq_access_flags.size()) [[likely]] {
258 m_irq_access_flags[id] = true;
259 return true;
260 } else {
261 return false;
262 }
263 }
264
265 Result SetCorePriorityCapability(const u32 cap);
266 Result SetSyscallMaskCapability(const u32 cap, u32& set_svc);
267 Result MapRange_(const u32 cap, const u32 size_cap, KPageTable* page_table);
268 Result MapIoPage_(const u32 cap, KPageTable* page_table);
269 Result MapRegion_(const u32 cap, KPageTable* page_table);
270 Result SetInterruptPairCapability(const u32 cap);
271 Result SetProgramTypeCapability(const u32 cap);
272 Result SetKernelVersionCapability(const u32 cap);
273 Result SetHandleTableCapability(const u32 cap);
274 Result SetDebugFlagsCapability(const u32 cap);
275
276 template <typename F>
277 static Result ProcessMapRegionCapability(const u32 cap, F f);
278 static Result CheckMapRegion(KernelCore& kernel, const u32 cap);
279
280 Result SetCapability(const u32 cap, u32& set_flags, u32& set_svc, KPageTable* page_table);
281 Result SetCapabilities(std::span<const u32> caps, KPageTable* page_table);
282
283private:
284 Svc::SvcAccessFlagSet m_svc_access_flags{};
285 InterruptFlagSet m_irq_access_flags{};
286 u64 m_core_mask{};
287 u64 m_phys_core_mask{};
288 u64 m_priority_mask{};
289 u32 m_debug_capabilities{};
290 s32 m_handle_table_size{};
291 u32 m_intended_kernel_version{};
292 u32 m_program_type{};
293};
294
295} // namespace Kernel
diff --git a/src/core/hle/kernel/k_code_memory.cpp b/src/core/hle/kernel/k_code_memory.cpp
index d9da1e600..884eba001 100644
--- a/src/core/hle/kernel/k_code_memory.cpp
+++ b/src/core/hle/kernel/k_code_memory.cpp
@@ -74,7 +74,7 @@ Result KCodeMemory::Map(VAddr address, size_t size) {
74 R_UNLESS(!m_is_mapped, ResultInvalidState); 74 R_UNLESS(!m_is_mapped, ResultInvalidState);
75 75
76 // Map the memory. 76 // Map the memory.
77 R_TRY(kernel.CurrentProcess()->PageTable().MapPages( 77 R_TRY(kernel.CurrentProcess()->PageTable().MapPageGroup(
78 address, *m_page_group, KMemoryState::CodeOut, KMemoryPermission::UserReadWrite)); 78 address, *m_page_group, KMemoryState::CodeOut, KMemoryPermission::UserReadWrite));
79 79
80 // Mark ourselves as mapped. 80 // Mark ourselves as mapped.
@@ -91,8 +91,8 @@ Result KCodeMemory::Unmap(VAddr address, size_t size) {
91 KScopedLightLock lk(m_lock); 91 KScopedLightLock lk(m_lock);
92 92
93 // Unmap the memory. 93 // Unmap the memory.
94 R_TRY(kernel.CurrentProcess()->PageTable().UnmapPages(address, *m_page_group, 94 R_TRY(kernel.CurrentProcess()->PageTable().UnmapPageGroup(address, *m_page_group,
95 KMemoryState::CodeOut)); 95 KMemoryState::CodeOut));
96 96
97 // Mark ourselves as unmapped. 97 // Mark ourselves as unmapped.
98 m_is_mapped = false; 98 m_is_mapped = false;
@@ -125,8 +125,8 @@ Result KCodeMemory::MapToOwner(VAddr address, size_t size, Svc::MemoryPermission
125 } 125 }
126 126
127 // Map the memory. 127 // Map the memory.
128 R_TRY( 128 R_TRY(m_owner->PageTable().MapPageGroup(address, *m_page_group, KMemoryState::GeneratedCode,
129 m_owner->PageTable().MapPages(address, *m_page_group, KMemoryState::GeneratedCode, k_perm)); 129 k_perm));
130 130
131 // Mark ourselves as mapped. 131 // Mark ourselves as mapped.
132 m_is_owner_mapped = true; 132 m_is_owner_mapped = true;
@@ -142,7 +142,7 @@ Result KCodeMemory::UnmapFromOwner(VAddr address, size_t size) {
142 KScopedLightLock lk(m_lock); 142 KScopedLightLock lk(m_lock);
143 143
144 // Unmap the memory. 144 // Unmap the memory.
145 R_TRY(m_owner->PageTable().UnmapPages(address, *m_page_group, KMemoryState::GeneratedCode)); 145 R_TRY(m_owner->PageTable().UnmapPageGroup(address, *m_page_group, KMemoryState::GeneratedCode));
146 146
147 // Mark ourselves as unmapped. 147 // Mark ourselves as unmapped.
148 m_is_owner_mapped = false; 148 m_is_owner_mapped = false;
diff --git a/src/core/hle/kernel/k_condition_variable.cpp b/src/core/hle/kernel/k_condition_variable.cpp
index 124149697..0c6b20db3 100644
--- a/src/core/hle/kernel/k_condition_variable.cpp
+++ b/src/core/hle/kernel/k_condition_variable.cpp
@@ -171,7 +171,7 @@ Result KConditionVariable::WaitForAddress(Handle handle, VAddr addr, u32 value)
171 R_UNLESS(owner_thread != nullptr, ResultInvalidHandle); 171 R_UNLESS(owner_thread != nullptr, ResultInvalidHandle);
172 172
173 // Update the lock. 173 // Update the lock.
174 cur_thread->SetAddressKey(addr, value); 174 cur_thread->SetUserAddressKey(addr, value);
175 owner_thread->AddWaiter(cur_thread); 175 owner_thread->AddWaiter(cur_thread);
176 176
177 // Begin waiting. 177 // Begin waiting.
diff --git a/src/core/hle/kernel/k_device_address_space.cpp b/src/core/hle/kernel/k_device_address_space.cpp
new file mode 100644
index 000000000..27659ea3b
--- /dev/null
+++ b/src/core/hle/kernel/k_device_address_space.cpp
@@ -0,0 +1,150 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "common/assert.h"
5#include "core/core.h"
6#include "core/hle/kernel/k_device_address_space.h"
7#include "core/hle/kernel/kernel.h"
8#include "core/hle/kernel/svc_results.h"
9
10namespace Kernel {
11
12KDeviceAddressSpace::KDeviceAddressSpace(KernelCore& kernel_)
13 : KAutoObjectWithSlabHeapAndContainer(kernel_), m_lock(kernel_), m_is_initialized(false) {}
14KDeviceAddressSpace::~KDeviceAddressSpace() = default;
15
16void KDeviceAddressSpace::Initialize() {
17 // This just forwards to the device page table manager.
18 // KDevicePageTable::Initialize();
19}
20
21// Member functions.
22Result KDeviceAddressSpace::Initialize(u64 address, u64 size) {
23 // Initialize the device page table.
24 // R_TRY(m_table.Initialize(address, size));
25
26 // Set member variables.
27 m_space_address = address;
28 m_space_size = size;
29 m_is_initialized = true;
30
31 R_SUCCEED();
32}
33
34void KDeviceAddressSpace::Finalize() {
35 // Finalize the table.
36 // m_table.Finalize();
37}
38
39Result KDeviceAddressSpace::Attach(Svc::DeviceName device_name) {
40 // Lock the address space.
41 KScopedLightLock lk(m_lock);
42
43 // Attach.
44 // R_RETURN(m_table.Attach(device_name, m_space_address, m_space_size));
45 R_SUCCEED();
46}
47
48Result KDeviceAddressSpace::Detach(Svc::DeviceName device_name) {
49 // Lock the address space.
50 KScopedLightLock lk(m_lock);
51
52 // Detach.
53 // R_RETURN(m_table.Detach(device_name));
54 R_SUCCEED();
55}
56
57Result KDeviceAddressSpace::Map(KPageTable* page_table, VAddr process_address, size_t size,
58 u64 device_address, u32 option, bool is_aligned) {
59 // Check that the address falls within the space.
60 R_UNLESS((m_space_address <= device_address &&
61 device_address + size - 1 <= m_space_address + m_space_size - 1),
62 ResultInvalidCurrentMemory);
63
64 // Decode the option.
65 const Svc::MapDeviceAddressSpaceOption option_pack{option};
66 const auto device_perm = option_pack.permission.Value();
67 const auto flags = option_pack.flags.Value();
68 const auto reserved = option_pack.reserved.Value();
69
70 // Validate the option.
71 // TODO: It is likely that this check for flags == none is only on NX board.
72 R_UNLESS(flags == Svc::MapDeviceAddressSpaceFlag::None, ResultInvalidEnumValue);
73 R_UNLESS(reserved == 0, ResultInvalidEnumValue);
74
75 // Lock the address space.
76 KScopedLightLock lk(m_lock);
77
78 // Lock the page table to prevent concurrent device mapping operations.
79 // KScopedLightLock pt_lk = page_table->AcquireDeviceMapLock();
80
81 // Lock the pages.
82 bool is_io{};
83 R_TRY(page_table->LockForMapDeviceAddressSpace(std::addressof(is_io), process_address, size,
84 ConvertToKMemoryPermission(device_perm),
85 is_aligned, true));
86
87 // Ensure that if we fail, we don't keep unmapped pages locked.
88 ON_RESULT_FAILURE {
89 ASSERT(page_table->UnlockForDeviceAddressSpace(process_address, size) == ResultSuccess);
90 };
91
92 // Check that the io status is allowable.
93 if (is_io) {
94 R_UNLESS(static_cast<u32>(flags & Svc::MapDeviceAddressSpaceFlag::NotIoRegister) == 0,
95 ResultInvalidCombination);
96 }
97
98 // Map the pages.
99 {
100 // Perform the mapping.
101 // R_TRY(m_table.Map(page_table, process_address, size, device_address, device_perm,
102 // is_aligned, is_io));
103
104 // Ensure that we unmap the pages if we fail to update the protections.
105 // NOTE: Nintendo does not check the result of this unmap call.
106 // ON_RESULT_FAILURE { m_table.Unmap(device_address, size); };
107
108 // Update the protections in accordance with how much we mapped.
109 // R_TRY(page_table->UnlockForDeviceAddressSpacePartialMap(process_address, size));
110 }
111
112 // We succeeded.
113 R_SUCCEED();
114}
115
116Result KDeviceAddressSpace::Unmap(KPageTable* page_table, VAddr process_address, size_t size,
117 u64 device_address) {
118 // Check that the address falls within the space.
119 R_UNLESS((m_space_address <= device_address &&
120 device_address + size - 1 <= m_space_address + m_space_size - 1),
121 ResultInvalidCurrentMemory);
122
123 // Lock the address space.
124 KScopedLightLock lk(m_lock);
125
126 // Lock the page table to prevent concurrent device mapping operations.
127 // KScopedLightLock pt_lk = page_table->AcquireDeviceMapLock();
128
129 // Lock the pages.
130 R_TRY(page_table->LockForUnmapDeviceAddressSpace(process_address, size, true));
131
132 // Unmap the pages.
133 {
134 // If we fail to unmap, we want to do a partial unlock.
135 // ON_RESULT_FAILURE {
136 // ASSERT(page_table->UnlockForDeviceAddressSpacePartialMap(process_address, size) ==
137 // ResultSuccess);
138 // };
139
140 // Perform the unmap.
141 // R_TRY(m_table.Unmap(page_table, process_address, size, device_address));
142 }
143
144 // Unlock the pages.
145 ASSERT(page_table->UnlockForDeviceAddressSpace(process_address, size) == ResultSuccess);
146
147 R_SUCCEED();
148}
149
150} // namespace Kernel
diff --git a/src/core/hle/kernel/k_device_address_space.h b/src/core/hle/kernel/k_device_address_space.h
new file mode 100644
index 000000000..4709df995
--- /dev/null
+++ b/src/core/hle/kernel/k_device_address_space.h
@@ -0,0 +1,60 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include <string>
7
8#include "common/common_types.h"
9#include "core/hle/kernel/k_page_table.h"
10#include "core/hle/kernel/slab_helpers.h"
11#include "core/hle/result.h"
12
13namespace Kernel {
14
15class KDeviceAddressSpace final
16 : public KAutoObjectWithSlabHeapAndContainer<KDeviceAddressSpace, KAutoObjectWithList> {
17 KERNEL_AUTOOBJECT_TRAITS(KDeviceAddressSpace, KAutoObject);
18
19public:
20 explicit KDeviceAddressSpace(KernelCore& kernel);
21 ~KDeviceAddressSpace();
22
23 Result Initialize(u64 address, u64 size);
24 void Finalize();
25
26 bool IsInitialized() const {
27 return m_is_initialized;
28 }
29 static void PostDestroy(uintptr_t arg) {}
30
31 Result Attach(Svc::DeviceName device_name);
32 Result Detach(Svc::DeviceName device_name);
33
34 Result MapByForce(KPageTable* page_table, VAddr process_address, size_t size,
35 u64 device_address, u32 option) {
36 R_RETURN(this->Map(page_table, process_address, size, device_address, option, false));
37 }
38
39 Result MapAligned(KPageTable* page_table, VAddr process_address, size_t size,
40 u64 device_address, u32 option) {
41 R_RETURN(this->Map(page_table, process_address, size, device_address, option, true));
42 }
43
44 Result Unmap(KPageTable* page_table, VAddr process_address, size_t size, u64 device_address);
45
46 static void Initialize();
47
48private:
49 Result Map(KPageTable* page_table, VAddr process_address, size_t size, u64 device_address,
50 u32 option, bool is_aligned);
51
52private:
53 KLightLock m_lock;
54 // KDevicePageTable m_table;
55 u64 m_space_address{};
56 u64 m_space_size{};
57 bool m_is_initialized{};
58};
59
60} // namespace Kernel
diff --git a/src/core/hle/kernel/k_light_lock.cpp b/src/core/hle/kernel/k_light_lock.cpp
index 43185320d..d791acbe3 100644
--- a/src/core/hle/kernel/k_light_lock.cpp
+++ b/src/core/hle/kernel/k_light_lock.cpp
@@ -68,7 +68,7 @@ bool KLightLock::LockSlowPath(uintptr_t _owner, uintptr_t _cur_thread) {
68 68
69 // Add the current thread as a waiter on the owner. 69 // Add the current thread as a waiter on the owner.
70 KThread* owner_thread = reinterpret_cast<KThread*>(_owner & ~1ULL); 70 KThread* owner_thread = reinterpret_cast<KThread*>(_owner & ~1ULL);
71 cur_thread->SetAddressKey(reinterpret_cast<uintptr_t>(std::addressof(tag))); 71 cur_thread->SetKernelAddressKey(reinterpret_cast<uintptr_t>(std::addressof(tag)));
72 owner_thread->AddWaiter(cur_thread); 72 owner_thread->AddWaiter(cur_thread);
73 73
74 // Begin waiting to hold the lock. 74 // Begin waiting to hold the lock.
diff --git a/src/core/hle/kernel/k_memory_layout.h b/src/core/hle/kernel/k_memory_layout.h
index fd6e1d3e6..17fa1a6ed 100644
--- a/src/core/hle/kernel/k_memory_layout.h
+++ b/src/core/hle/kernel/k_memory_layout.h
@@ -67,9 +67,9 @@ constexpr size_t KernelPageBufferAdditionalSize = 0x33C000;
67constexpr std::size_t KernelResourceSize = KernelPageTableHeapSize + KernelInitialPageHeapSize + 67constexpr std::size_t KernelResourceSize = KernelPageTableHeapSize + KernelInitialPageHeapSize +
68 KernelSlabHeapSize + KernelPageBufferHeapSize; 68 KernelSlabHeapSize + KernelPageBufferHeapSize;
69 69
70constexpr bool IsKernelAddressKey(VAddr key) { 70//! NB: Use KThread::GetAddressKeyIsKernel().
71 return KernelVirtualAddressSpaceBase <= key && key <= KernelVirtualAddressSpaceLast; 71//! See explanation for deviation of GetAddressKey.
72} 72bool IsKernelAddressKey(VAddr key) = delete;
73 73
74constexpr bool IsKernelAddress(VAddr address) { 74constexpr bool IsKernelAddress(VAddr address) {
75 return KernelVirtualAddressSpaceBase <= address && address < KernelVirtualAddressSpaceEnd; 75 return KernelVirtualAddressSpaceBase <= address && address < KernelVirtualAddressSpaceEnd;
diff --git a/src/core/hle/kernel/k_page_table.cpp b/src/core/hle/kernel/k_page_table.cpp
index 9c7ac22dc..2e13d5d0d 100644
--- a/src/core/hle/kernel/k_page_table.cpp
+++ b/src/core/hle/kernel/k_page_table.cpp
@@ -435,6 +435,9 @@ Result KPageTable::MapCodeMemory(VAddr dst_address, VAddr src_address, size_t si
435 KPageGroup pg{m_kernel, m_block_info_manager}; 435 KPageGroup pg{m_kernel, m_block_info_manager};
436 AddRegionToPages(src_address, num_pages, pg); 436 AddRegionToPages(src_address, num_pages, pg);
437 437
438 // We're going to perform an update, so create a helper.
439 KScopedPageTableUpdater updater(this);
440
438 // Reprotect the source as kernel-read/not mapped. 441 // Reprotect the source as kernel-read/not mapped.
439 const auto new_perm = static_cast<KMemoryPermission>(KMemoryPermission::KernelRead | 442 const auto new_perm = static_cast<KMemoryPermission>(KMemoryPermission::KernelRead |
440 KMemoryPermission::NotMapped); 443 KMemoryPermission::NotMapped);
@@ -447,7 +450,10 @@ Result KPageTable::MapCodeMemory(VAddr dst_address, VAddr src_address, size_t si
447 }); 450 });
448 451
449 // Map the alias pages. 452 // Map the alias pages.
450 R_TRY(MapPages(dst_address, pg, new_perm)); 453 const KPageProperties dst_properties = {new_perm, false, false,
454 DisableMergeAttribute::DisableHead};
455 R_TRY(
456 this->MapPageGroupImpl(updater.GetPageList(), dst_address, pg, dst_properties, false));
451 457
452 // We successfully mapped the alias pages, so we don't need to unprotect the src pages on 458 // We successfully mapped the alias pages, so we don't need to unprotect the src pages on
453 // failure. 459 // failure.
@@ -1881,7 +1887,8 @@ Result KPageTable::UnmapPhysicalMemory(VAddr address, size_t size) {
1881 R_SUCCEED(); 1887 R_SUCCEED();
1882} 1888}
1883 1889
1884Result KPageTable::MapMemory(VAddr dst_address, VAddr src_address, size_t size) { 1890Result KPageTable::MapMemory(KProcessAddress dst_address, KProcessAddress src_address,
1891 size_t size) {
1885 // Lock the table. 1892 // Lock the table.
1886 KScopedLightLock lk(m_general_lock); 1893 KScopedLightLock lk(m_general_lock);
1887 1894
@@ -1902,53 +1909,73 @@ Result KPageTable::MapMemory(VAddr dst_address, VAddr src_address, size_t size)
1902 KMemoryAttribute::None)); 1909 KMemoryAttribute::None));
1903 1910
1904 // Create an update allocator for the source. 1911 // Create an update allocator for the source.
1905 Result src_allocator_result{ResultSuccess}; 1912 Result src_allocator_result;
1906 KMemoryBlockManagerUpdateAllocator src_allocator(std::addressof(src_allocator_result), 1913 KMemoryBlockManagerUpdateAllocator src_allocator(std::addressof(src_allocator_result),
1907 m_memory_block_slab_manager, 1914 m_memory_block_slab_manager,
1908 num_src_allocator_blocks); 1915 num_src_allocator_blocks);
1909 R_TRY(src_allocator_result); 1916 R_TRY(src_allocator_result);
1910 1917
1911 // Create an update allocator for the destination. 1918 // Create an update allocator for the destination.
1912 Result dst_allocator_result{ResultSuccess}; 1919 Result dst_allocator_result;
1913 KMemoryBlockManagerUpdateAllocator dst_allocator(std::addressof(dst_allocator_result), 1920 KMemoryBlockManagerUpdateAllocator dst_allocator(std::addressof(dst_allocator_result),
1914 m_memory_block_slab_manager, 1921 m_memory_block_slab_manager,
1915 num_dst_allocator_blocks); 1922 num_dst_allocator_blocks);
1916 R_TRY(dst_allocator_result); 1923 R_TRY(dst_allocator_result);
1917 1924
1918 // Map the memory. 1925 // Map the memory.
1919 KPageGroup page_linked_list{m_kernel, m_block_info_manager};
1920 const size_t num_pages{size / PageSize};
1921 const KMemoryPermission new_src_perm = static_cast<KMemoryPermission>(
1922 KMemoryPermission::KernelRead | KMemoryPermission::NotMapped);
1923 const KMemoryAttribute new_src_attr = KMemoryAttribute::Locked;
1924
1925 AddRegionToPages(src_address, num_pages, page_linked_list);
1926 { 1926 {
1927 // Determine the number of pages being operated on.
1928 const size_t num_pages = size / PageSize;
1929
1930 // Create page groups for the memory being unmapped.
1931 KPageGroup pg{m_kernel, m_block_info_manager};
1932
1933 // Create the page group representing the source.
1934 R_TRY(this->MakePageGroup(pg, src_address, num_pages));
1935
1936 // We're going to perform an update, so create a helper.
1937 KScopedPageTableUpdater updater(this);
1938
1927 // Reprotect the source as kernel-read/not mapped. 1939 // Reprotect the source as kernel-read/not mapped.
1928 auto block_guard = detail::ScopeExit([&] { 1940 const KMemoryPermission new_src_perm = static_cast<KMemoryPermission>(
1929 Operate(src_address, num_pages, KMemoryPermission::UserReadWrite, 1941 KMemoryPermission::KernelRead | KMemoryPermission::NotMapped);
1930 OperationType::ChangePermissions); 1942 const KMemoryAttribute new_src_attr = KMemoryAttribute::Locked;
1931 }); 1943 const KPageProperties src_properties = {new_src_perm, false, false,
1932 R_TRY(Operate(src_address, num_pages, new_src_perm, OperationType::ChangePermissions)); 1944 DisableMergeAttribute::DisableHeadBodyTail};
1933 R_TRY(MapPages(dst_address, page_linked_list, KMemoryPermission::UserReadWrite)); 1945 R_TRY(this->Operate(src_address, num_pages, src_properties.perm,
1946 OperationType::ChangePermissions));
1934 1947
1935 block_guard.Cancel(); 1948 // Ensure that we unprotect the source pages on failure.
1936 } 1949 ON_RESULT_FAILURE {
1950 const KPageProperties unprotect_properties = {
1951 KMemoryPermission::UserReadWrite, false, false,
1952 DisableMergeAttribute::EnableHeadBodyTail};
1953 ASSERT(this->Operate(src_address, num_pages, unprotect_properties.perm,
1954 OperationType::ChangePermissions) == ResultSuccess);
1955 };
1937 1956
1938 // Apply the memory block updates. 1957 // Map the alias pages.
1939 m_memory_block_manager.Update(std::addressof(src_allocator), src_address, num_pages, src_state, 1958 const KPageProperties dst_map_properties = {KMemoryPermission::UserReadWrite, false, false,
1940 new_src_perm, new_src_attr, 1959 DisableMergeAttribute::DisableHead};
1941 KMemoryBlockDisableMergeAttribute::Locked, 1960 R_TRY(this->MapPageGroupImpl(updater.GetPageList(), dst_address, pg, dst_map_properties,
1942 KMemoryBlockDisableMergeAttribute::None); 1961 false));
1943 m_memory_block_manager.Update(std::addressof(dst_allocator), dst_address, num_pages, 1962
1944 KMemoryState::Stack, KMemoryPermission::UserReadWrite, 1963 // Apply the memory block updates.
1945 KMemoryAttribute::None, KMemoryBlockDisableMergeAttribute::Normal, 1964 m_memory_block_manager.Update(std::addressof(src_allocator), src_address, num_pages,
1946 KMemoryBlockDisableMergeAttribute::None); 1965 src_state, new_src_perm, new_src_attr,
1966 KMemoryBlockDisableMergeAttribute::Locked,
1967 KMemoryBlockDisableMergeAttribute::None);
1968 m_memory_block_manager.Update(
1969 std::addressof(dst_allocator), dst_address, num_pages, KMemoryState::Stack,
1970 KMemoryPermission::UserReadWrite, KMemoryAttribute::None,
1971 KMemoryBlockDisableMergeAttribute::Normal, KMemoryBlockDisableMergeAttribute::None);
1972 }
1947 1973
1948 R_SUCCEED(); 1974 R_SUCCEED();
1949} 1975}
1950 1976
1951Result KPageTable::UnmapMemory(VAddr dst_address, VAddr src_address, size_t size) { 1977Result KPageTable::UnmapMemory(KProcessAddress dst_address, KProcessAddress src_address,
1978 size_t size) {
1952 // Lock the table. 1979 // Lock the table.
1953 KScopedLightLock lk(m_general_lock); 1980 KScopedLightLock lk(m_general_lock);
1954 1981
@@ -1970,108 +1997,208 @@ Result KPageTable::UnmapMemory(VAddr dst_address, VAddr src_address, size_t size
1970 KMemoryPermission::None, KMemoryAttribute::All, KMemoryAttribute::None)); 1997 KMemoryPermission::None, KMemoryAttribute::All, KMemoryAttribute::None));
1971 1998
1972 // Create an update allocator for the source. 1999 // Create an update allocator for the source.
1973 Result src_allocator_result{ResultSuccess}; 2000 Result src_allocator_result;
1974 KMemoryBlockManagerUpdateAllocator src_allocator(std::addressof(src_allocator_result), 2001 KMemoryBlockManagerUpdateAllocator src_allocator(std::addressof(src_allocator_result),
1975 m_memory_block_slab_manager, 2002 m_memory_block_slab_manager,
1976 num_src_allocator_blocks); 2003 num_src_allocator_blocks);
1977 R_TRY(src_allocator_result); 2004 R_TRY(src_allocator_result);
1978 2005
1979 // Create an update allocator for the destination. 2006 // Create an update allocator for the destination.
1980 Result dst_allocator_result{ResultSuccess}; 2007 Result dst_allocator_result;
1981 KMemoryBlockManagerUpdateAllocator dst_allocator(std::addressof(dst_allocator_result), 2008 KMemoryBlockManagerUpdateAllocator dst_allocator(std::addressof(dst_allocator_result),
1982 m_memory_block_slab_manager, 2009 m_memory_block_slab_manager,
1983 num_dst_allocator_blocks); 2010 num_dst_allocator_blocks);
1984 R_TRY(dst_allocator_result); 2011 R_TRY(dst_allocator_result);
1985 2012
1986 KPageGroup src_pages{m_kernel, m_block_info_manager}; 2013 // Unmap the memory.
1987 KPageGroup dst_pages{m_kernel, m_block_info_manager}; 2014 {
1988 const size_t num_pages{size / PageSize}; 2015 // Determine the number of pages being operated on.
2016 const size_t num_pages = size / PageSize;
1989 2017
1990 AddRegionToPages(src_address, num_pages, src_pages); 2018 // Create page groups for the memory being unmapped.
1991 AddRegionToPages(dst_address, num_pages, dst_pages); 2019 KPageGroup pg{m_kernel, m_block_info_manager};
1992 2020
1993 R_UNLESS(dst_pages.IsEquivalentTo(src_pages), ResultInvalidMemoryRegion); 2021 // Create the page group representing the destination.
2022 R_TRY(this->MakePageGroup(pg, dst_address, num_pages));
1994 2023
1995 { 2024 // Ensure the page group is the valid for the source.
1996 auto block_guard = detail::ScopeExit([&] { MapPages(dst_address, dst_pages, dst_perm); }); 2025 R_UNLESS(this->IsValidPageGroup(pg, src_address, num_pages), ResultInvalidMemoryRegion);
1997 2026
1998 R_TRY(Operate(dst_address, num_pages, KMemoryPermission::None, OperationType::Unmap)); 2027 // We're going to perform an update, so create a helper.
1999 R_TRY(Operate(src_address, num_pages, KMemoryPermission::UserReadWrite, 2028 KScopedPageTableUpdater updater(this);
2000 OperationType::ChangePermissions));
2001 2029
2002 block_guard.Cancel(); 2030 // Unmap the aliased copy of the pages.
2003 } 2031 const KPageProperties dst_unmap_properties = {KMemoryPermission::None, false, false,
2032 DisableMergeAttribute::None};
2033 R_TRY(
2034 this->Operate(dst_address, num_pages, dst_unmap_properties.perm, OperationType::Unmap));
2035
2036 // Ensure that we re-map the aliased pages on failure.
2037 ON_RESULT_FAILURE {
2038 this->RemapPageGroup(updater.GetPageList(), dst_address, size, pg);
2039 };
2004 2040
2005 // Apply the memory block updates. 2041 // Try to set the permissions for the source pages back to what they should be.
2006 m_memory_block_manager.Update(std::addressof(src_allocator), src_address, num_pages, src_state, 2042 const KPageProperties src_properties = {KMemoryPermission::UserReadWrite, false, false,
2007 KMemoryPermission::UserReadWrite, KMemoryAttribute::None, 2043 DisableMergeAttribute::EnableAndMergeHeadBodyTail};
2008 KMemoryBlockDisableMergeAttribute::None, 2044 R_TRY(this->Operate(src_address, num_pages, src_properties.perm,
2009 KMemoryBlockDisableMergeAttribute::Locked); 2045 OperationType::ChangePermissions));
2010 m_memory_block_manager.Update(std::addressof(dst_allocator), dst_address, num_pages, 2046
2011 KMemoryState::None, KMemoryPermission::None, 2047 // Apply the memory block updates.
2012 KMemoryAttribute::None, KMemoryBlockDisableMergeAttribute::None, 2048 m_memory_block_manager.Update(
2013 KMemoryBlockDisableMergeAttribute::Normal); 2049 std::addressof(src_allocator), src_address, num_pages, src_state,
2050 KMemoryPermission::UserReadWrite, KMemoryAttribute::None,
2051 KMemoryBlockDisableMergeAttribute::None, KMemoryBlockDisableMergeAttribute::Locked);
2052 m_memory_block_manager.Update(
2053 std::addressof(dst_allocator), dst_address, num_pages, KMemoryState::None,
2054 KMemoryPermission::None, KMemoryAttribute::None,
2055 KMemoryBlockDisableMergeAttribute::None, KMemoryBlockDisableMergeAttribute::Normal);
2056 }
2014 2057
2015 R_SUCCEED(); 2058 R_SUCCEED();
2016} 2059}
2017 2060
2018Result KPageTable::MapPages(VAddr addr, const KPageGroup& page_linked_list, 2061Result KPageTable::AllocateAndMapPagesImpl(PageLinkedList* page_list, KProcessAddress address,
2019 KMemoryPermission perm) { 2062 size_t num_pages, KMemoryPermission perm) {
2020 ASSERT(this->IsLockedByCurrentThread()); 2063 ASSERT(this->IsLockedByCurrentThread());
2021 2064
2022 VAddr cur_addr{addr}; 2065 // Create a page group to hold the pages we allocate.
2066 KPageGroup pg{m_kernel, m_block_info_manager};
2023 2067
2024 for (const auto& node : page_linked_list) { 2068 // Allocate the pages.
2025 if (const auto result{ 2069 R_TRY(
2026 Operate(cur_addr, node.GetNumPages(), perm, OperationType::Map, node.GetAddress())}; 2070 m_kernel.MemoryManager().AllocateAndOpen(std::addressof(pg), num_pages, m_allocate_option));
2027 result.IsError()) {
2028 const size_t num_pages{(addr - cur_addr) / PageSize};
2029 2071
2030 ASSERT(Operate(addr, num_pages, KMemoryPermission::None, OperationType::Unmap) 2072 // Ensure that the page group is closed when we're done working with it.
2031 .IsSuccess()); 2073 SCOPE_EXIT({ pg.Close(); });
2032 2074
2033 R_RETURN(result); 2075 // Clear all pages.
2076 for (const auto& it : pg) {
2077 std::memset(m_system.DeviceMemory().GetPointer<void>(it.GetAddress()), m_heap_fill_value,
2078 it.GetSize());
2079 }
2080
2081 // Map the pages.
2082 R_RETURN(this->Operate(address, num_pages, pg, OperationType::MapGroup));
2083}
2084
2085Result KPageTable::MapPageGroupImpl(PageLinkedList* page_list, KProcessAddress address,
2086 const KPageGroup& pg, const KPageProperties properties,
2087 bool reuse_ll) {
2088 ASSERT(this->IsLockedByCurrentThread());
2089
2090 // Note the current address, so that we can iterate.
2091 const KProcessAddress start_address = address;
2092 KProcessAddress cur_address = address;
2093
2094 // Ensure that we clean up on failure.
2095 ON_RESULT_FAILURE {
2096 ASSERT(!reuse_ll);
2097 if (cur_address != start_address) {
2098 const KPageProperties unmap_properties = {KMemoryPermission::None, false, false,
2099 DisableMergeAttribute::None};
2100 ASSERT(this->Operate(start_address, (cur_address - start_address) / PageSize,
2101 unmap_properties.perm, OperationType::Unmap) == ResultSuccess);
2034 } 2102 }
2103 };
2035 2104
2036 cur_addr += node.GetNumPages() * PageSize; 2105 // Iterate, mapping all pages in the group.
2106 for (const auto& block : pg) {
2107 // Map and advance.
2108 const KPageProperties cur_properties =
2109 (cur_address == start_address)
2110 ? properties
2111 : KPageProperties{properties.perm, properties.io, properties.uncached,
2112 DisableMergeAttribute::None};
2113 this->Operate(cur_address, block.GetNumPages(), cur_properties.perm, OperationType::Map,
2114 block.GetAddress());
2115 cur_address += block.GetSize();
2037 } 2116 }
2038 2117
2118 // We succeeded!
2039 R_SUCCEED(); 2119 R_SUCCEED();
2040} 2120}
2041 2121
2042Result KPageTable::MapPages(VAddr address, KPageGroup& page_linked_list, KMemoryState state, 2122void KPageTable::RemapPageGroup(PageLinkedList* page_list, KProcessAddress address, size_t size,
2043 KMemoryPermission perm) { 2123 const KPageGroup& pg) {
2044 // Check that the map is in range. 2124 ASSERT(this->IsLockedByCurrentThread());
2045 const size_t num_pages{page_linked_list.GetNumPages()};
2046 const size_t size{num_pages * PageSize};
2047 R_UNLESS(this->CanContain(address, size, state), ResultInvalidCurrentMemory);
2048 2125
2049 // Lock the table. 2126 // Note the current address, so that we can iterate.
2050 KScopedLightLock lk(m_general_lock); 2127 const KProcessAddress start_address = address;
2128 const KProcessAddress last_address = start_address + size - 1;
2129 const KProcessAddress end_address = last_address + 1;
2051 2130
2052 // Check the memory state. 2131 // Iterate over the memory.
2053 R_TRY(this->CheckMemoryState(address, size, KMemoryState::All, KMemoryState::Free, 2132 auto pg_it = pg.begin();
2054 KMemoryPermission::None, KMemoryPermission::None, 2133 ASSERT(pg_it != pg.end());
2055 KMemoryAttribute::None, KMemoryAttribute::None));
2056 2134
2057 // Create an update allocator. 2135 KPhysicalAddress pg_phys_addr = pg_it->GetAddress();
2058 Result allocator_result{ResultSuccess}; 2136 size_t pg_pages = pg_it->GetNumPages();
2059 KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result),
2060 m_memory_block_slab_manager);
2061 2137
2062 // Map the pages. 2138 auto it = m_memory_block_manager.FindIterator(start_address);
2063 R_TRY(MapPages(address, page_linked_list, perm)); 2139 while (true) {
2140 // Check that the iterator is valid.
2141 ASSERT(it != m_memory_block_manager.end());
2064 2142
2065 // Update the blocks. 2143 // Get the memory info.
2066 m_memory_block_manager.Update(std::addressof(allocator), address, num_pages, state, perm, 2144 const KMemoryInfo info = it->GetMemoryInfo();
2067 KMemoryAttribute::None, KMemoryBlockDisableMergeAttribute::Normal,
2068 KMemoryBlockDisableMergeAttribute::None);
2069 2145
2070 R_SUCCEED(); 2146 // Determine the range to map.
2147 KProcessAddress map_address = std::max<VAddr>(info.GetAddress(), start_address);
2148 const KProcessAddress map_end_address = std::min<VAddr>(info.GetEndAddress(), end_address);
2149 ASSERT(map_end_address != map_address);
2150
2151 // Determine if we should disable head merge.
2152 const bool disable_head_merge =
2153 info.GetAddress() >= start_address &&
2154 True(info.GetDisableMergeAttribute() & KMemoryBlockDisableMergeAttribute::Normal);
2155 const KPageProperties map_properties = {
2156 info.GetPermission(), false, false,
2157 disable_head_merge ? DisableMergeAttribute::DisableHead : DisableMergeAttribute::None};
2158
2159 // While we have pages to map, map them.
2160 size_t map_pages = (map_end_address - map_address) / PageSize;
2161 while (map_pages > 0) {
2162 // Check if we're at the end of the physical block.
2163 if (pg_pages == 0) {
2164 // Ensure there are more pages to map.
2165 ASSERT(pg_it != pg.end());
2166
2167 // Advance our physical block.
2168 ++pg_it;
2169 pg_phys_addr = pg_it->GetAddress();
2170 pg_pages = pg_it->GetNumPages();
2171 }
2172
2173 // Map whatever we can.
2174 const size_t cur_pages = std::min(pg_pages, map_pages);
2175 ASSERT(this->Operate(map_address, map_pages, map_properties.perm, OperationType::Map,
2176 pg_phys_addr) == ResultSuccess);
2177
2178 // Advance.
2179 map_address += cur_pages * PageSize;
2180 map_pages -= cur_pages;
2181
2182 pg_phys_addr += cur_pages * PageSize;
2183 pg_pages -= cur_pages;
2184 }
2185
2186 // Check if we're done.
2187 if (last_address <= info.GetLastAddress()) {
2188 break;
2189 }
2190
2191 // Advance.
2192 ++it;
2193 }
2194
2195 // Check that we re-mapped precisely the page group.
2196 ASSERT((++pg_it) == pg.end());
2071} 2197}
2072 2198
2073Result KPageTable::MapPages(VAddr* out_addr, size_t num_pages, size_t alignment, PAddr phys_addr, 2199Result KPageTable::MapPages(KProcessAddress* out_addr, size_t num_pages, size_t alignment,
2074 bool is_pa_valid, VAddr region_start, size_t region_num_pages, 2200 KPhysicalAddress phys_addr, bool is_pa_valid,
2201 KProcessAddress region_start, size_t region_num_pages,
2075 KMemoryState state, KMemoryPermission perm) { 2202 KMemoryState state, KMemoryPermission perm) {
2076 ASSERT(Common::IsAligned(alignment, PageSize) && alignment >= PageSize); 2203 ASSERT(Common::IsAligned(alignment, PageSize) && alignment >= PageSize);
2077 2204
@@ -2084,26 +2211,30 @@ Result KPageTable::MapPages(VAddr* out_addr, size_t num_pages, size_t alignment,
2084 KScopedLightLock lk(m_general_lock); 2211 KScopedLightLock lk(m_general_lock);
2085 2212
2086 // Find a random address to map at. 2213 // Find a random address to map at.
2087 VAddr addr = this->FindFreeArea(region_start, region_num_pages, num_pages, alignment, 0, 2214 KProcessAddress addr = this->FindFreeArea(region_start, region_num_pages, num_pages, alignment,
2088 this->GetNumGuardPages()); 2215 0, this->GetNumGuardPages());
2089 R_UNLESS(addr != 0, ResultOutOfMemory); 2216 R_UNLESS(addr != 0, ResultOutOfMemory);
2090 ASSERT(Common::IsAligned(addr, alignment)); 2217 ASSERT(Common::IsAligned(addr, alignment));
2091 ASSERT(this->CanContain(addr, num_pages * PageSize, state)); 2218 ASSERT(this->CanContain(addr, num_pages * PageSize, state));
2092 ASSERT(this->CheckMemoryState(addr, num_pages * PageSize, KMemoryState::All, KMemoryState::Free, 2219 ASSERT(this->CheckMemoryState(addr, num_pages * PageSize, KMemoryState::All, KMemoryState::Free,
2093 KMemoryPermission::None, KMemoryPermission::None, 2220 KMemoryPermission::None, KMemoryPermission::None,
2094 KMemoryAttribute::None, KMemoryAttribute::None) 2221 KMemoryAttribute::None, KMemoryAttribute::None) == ResultSuccess);
2095 .IsSuccess());
2096 2222
2097 // Create an update allocator. 2223 // Create an update allocator.
2098 Result allocator_result{ResultSuccess}; 2224 Result allocator_result;
2099 KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result), 2225 KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result),
2100 m_memory_block_slab_manager); 2226 m_memory_block_slab_manager);
2227 R_TRY(allocator_result);
2228
2229 // We're going to perform an update, so create a helper.
2230 KScopedPageTableUpdater updater(this);
2101 2231
2102 // Perform mapping operation. 2232 // Perform mapping operation.
2103 if (is_pa_valid) { 2233 if (is_pa_valid) {
2104 R_TRY(this->Operate(addr, num_pages, perm, OperationType::Map, phys_addr)); 2234 const KPageProperties properties = {perm, false, false, DisableMergeAttribute::DisableHead};
2235 R_TRY(this->Operate(addr, num_pages, properties.perm, OperationType::Map, phys_addr));
2105 } else { 2236 } else {
2106 UNIMPLEMENTED(); 2237 R_TRY(this->AllocateAndMapPagesImpl(updater.GetPageList(), addr, num_pages, perm));
2107 } 2238 }
2108 2239
2109 // Update the blocks. 2240 // Update the blocks.
@@ -2116,28 +2247,45 @@ Result KPageTable::MapPages(VAddr* out_addr, size_t num_pages, size_t alignment,
2116 R_SUCCEED(); 2247 R_SUCCEED();
2117} 2248}
2118 2249
2119Result KPageTable::UnmapPages(VAddr addr, const KPageGroup& page_linked_list) { 2250Result KPageTable::MapPages(KProcessAddress address, size_t num_pages, KMemoryState state,
2120 ASSERT(this->IsLockedByCurrentThread()); 2251 KMemoryPermission perm) {
2252 // Check that the map is in range.
2253 const size_t size = num_pages * PageSize;
2254 R_UNLESS(this->CanContain(address, size, state), ResultInvalidCurrentMemory);
2121 2255
2122 VAddr cur_addr{addr}; 2256 // Lock the table.
2257 KScopedLightLock lk(m_general_lock);
2123 2258
2124 for (const auto& node : page_linked_list) { 2259 // Check the memory state.
2125 if (const auto result{Operate(cur_addr, node.GetNumPages(), KMemoryPermission::None, 2260 size_t num_allocator_blocks;
2126 OperationType::Unmap)}; 2261 R_TRY(this->CheckMemoryState(std::addressof(num_allocator_blocks), address, size,
2127 result.IsError()) { 2262 KMemoryState::All, KMemoryState::Free, KMemoryPermission::None,
2128 R_RETURN(result); 2263 KMemoryPermission::None, KMemoryAttribute::None,
2129 } 2264 KMemoryAttribute::None));
2130 2265
2131 cur_addr += node.GetNumPages() * PageSize; 2266 // Create an update allocator.
2132 } 2267 Result allocator_result;
2268 KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result),
2269 m_memory_block_slab_manager, num_allocator_blocks);
2270 R_TRY(allocator_result);
2271
2272 // We're going to perform an update, so create a helper.
2273 KScopedPageTableUpdater updater(this);
2274
2275 // Map the pages.
2276 R_TRY(this->AllocateAndMapPagesImpl(updater.GetPageList(), address, num_pages, perm));
2277
2278 // Update the blocks.
2279 m_memory_block_manager.Update(std::addressof(allocator), address, num_pages, state, perm,
2280 KMemoryAttribute::None, KMemoryBlockDisableMergeAttribute::Normal,
2281 KMemoryBlockDisableMergeAttribute::None);
2133 2282
2134 R_SUCCEED(); 2283 R_SUCCEED();
2135} 2284}
2136 2285
2137Result KPageTable::UnmapPages(VAddr address, KPageGroup& page_linked_list, KMemoryState state) { 2286Result KPageTable::UnmapPages(KProcessAddress address, size_t num_pages, KMemoryState state) {
2138 // Check that the unmap is in range. 2287 // Check that the unmap is in range.
2139 const size_t num_pages{page_linked_list.GetNumPages()}; 2288 const size_t size = num_pages * PageSize;
2140 const size_t size{num_pages * PageSize};
2141 R_UNLESS(this->Contains(address, size), ResultInvalidCurrentMemory); 2289 R_UNLESS(this->Contains(address, size), ResultInvalidCurrentMemory);
2142 2290
2143 // Lock the table. 2291 // Lock the table.
@@ -2151,13 +2299,18 @@ Result KPageTable::UnmapPages(VAddr address, KPageGroup& page_linked_list, KMemo
2151 KMemoryAttribute::None)); 2299 KMemoryAttribute::None));
2152 2300
2153 // Create an update allocator. 2301 // Create an update allocator.
2154 Result allocator_result{ResultSuccess}; 2302 Result allocator_result;
2155 KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result), 2303 KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result),
2156 m_memory_block_slab_manager, num_allocator_blocks); 2304 m_memory_block_slab_manager, num_allocator_blocks);
2157 R_TRY(allocator_result); 2305 R_TRY(allocator_result);
2158 2306
2307 // We're going to perform an update, so create a helper.
2308 KScopedPageTableUpdater updater(this);
2309
2159 // Perform the unmap. 2310 // Perform the unmap.
2160 R_TRY(UnmapPages(address, page_linked_list)); 2311 const KPageProperties unmap_properties = {KMemoryPermission::None, false, false,
2312 DisableMergeAttribute::None};
2313 R_TRY(this->Operate(address, num_pages, unmap_properties.perm, OperationType::Unmap));
2161 2314
2162 // Update the blocks. 2315 // Update the blocks.
2163 m_memory_block_manager.Update(std::addressof(allocator), address, num_pages, KMemoryState::Free, 2316 m_memory_block_manager.Update(std::addressof(allocator), address, num_pages, KMemoryState::Free,
@@ -2168,29 +2321,130 @@ Result KPageTable::UnmapPages(VAddr address, KPageGroup& page_linked_list, KMemo
2168 R_SUCCEED(); 2321 R_SUCCEED();
2169} 2322}
2170 2323
2171Result KPageTable::UnmapPages(VAddr address, size_t num_pages, KMemoryState state) { 2324Result KPageTable::MapPageGroup(KProcessAddress* out_addr, const KPageGroup& pg,
2172 // Check that the unmap is in range. 2325 KProcessAddress region_start, size_t region_num_pages,
2326 KMemoryState state, KMemoryPermission perm) {
2327 ASSERT(!this->IsLockedByCurrentThread());
2328
2329 // Ensure this is a valid map request.
2330 const size_t num_pages = pg.GetNumPages();
2331 R_UNLESS(this->CanContain(region_start, region_num_pages * PageSize, state),
2332 ResultInvalidCurrentMemory);
2333 R_UNLESS(num_pages < region_num_pages, ResultOutOfMemory);
2334
2335 // Lock the table.
2336 KScopedLightLock lk(m_general_lock);
2337
2338 // Find a random address to map at.
2339 KProcessAddress addr = this->FindFreeArea(region_start, region_num_pages, num_pages, PageSize,
2340 0, this->GetNumGuardPages());
2341 R_UNLESS(addr != 0, ResultOutOfMemory);
2342 ASSERT(this->CanContain(addr, num_pages * PageSize, state));
2343 ASSERT(this->CheckMemoryState(addr, num_pages * PageSize, KMemoryState::All, KMemoryState::Free,
2344 KMemoryPermission::None, KMemoryPermission::None,
2345 KMemoryAttribute::None, KMemoryAttribute::None) == ResultSuccess);
2346
2347 // Create an update allocator.
2348 Result allocator_result;
2349 KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result),
2350 m_memory_block_slab_manager);
2351 R_TRY(allocator_result);
2352
2353 // We're going to perform an update, so create a helper.
2354 KScopedPageTableUpdater updater(this);
2355
2356 // Perform mapping operation.
2357 const KPageProperties properties = {perm, state == KMemoryState::Io, false,
2358 DisableMergeAttribute::DisableHead};
2359 R_TRY(this->MapPageGroupImpl(updater.GetPageList(), addr, pg, properties, false));
2360
2361 // Update the blocks.
2362 m_memory_block_manager.Update(std::addressof(allocator), addr, num_pages, state, perm,
2363 KMemoryAttribute::None, KMemoryBlockDisableMergeAttribute::Normal,
2364 KMemoryBlockDisableMergeAttribute::None);
2365
2366 // We successfully mapped the pages.
2367 *out_addr = addr;
2368 R_SUCCEED();
2369}
2370
2371Result KPageTable::MapPageGroup(KProcessAddress addr, const KPageGroup& pg, KMemoryState state,
2372 KMemoryPermission perm) {
2373 ASSERT(!this->IsLockedByCurrentThread());
2374
2375 // Ensure this is a valid map request.
2376 const size_t num_pages = pg.GetNumPages();
2173 const size_t size = num_pages * PageSize; 2377 const size_t size = num_pages * PageSize;
2174 R_UNLESS(this->Contains(address, size), ResultInvalidCurrentMemory); 2378 R_UNLESS(this->CanContain(addr, size, state), ResultInvalidCurrentMemory);
2175 2379
2176 // Lock the table. 2380 // Lock the table.
2177 KScopedLightLock lk(m_general_lock); 2381 KScopedLightLock lk(m_general_lock);
2178 2382
2179 // Check the memory state. 2383 // Check if state allows us to map.
2180 size_t num_allocator_blocks{}; 2384 size_t num_allocator_blocks;
2385 R_TRY(this->CheckMemoryState(std::addressof(num_allocator_blocks), addr, size,
2386 KMemoryState::All, KMemoryState::Free, KMemoryPermission::None,
2387 KMemoryPermission::None, KMemoryAttribute::None,
2388 KMemoryAttribute::None));
2389
2390 // Create an update allocator.
2391 Result allocator_result;
2392 KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result),
2393 m_memory_block_slab_manager, num_allocator_blocks);
2394 R_TRY(allocator_result);
2395
2396 // We're going to perform an update, so create a helper.
2397 KScopedPageTableUpdater updater(this);
2398
2399 // Perform mapping operation.
2400 const KPageProperties properties = {perm, state == KMemoryState::Io, false,
2401 DisableMergeAttribute::DisableHead};
2402 R_TRY(this->MapPageGroupImpl(updater.GetPageList(), addr, pg, properties, false));
2403
2404 // Update the blocks.
2405 m_memory_block_manager.Update(std::addressof(allocator), addr, num_pages, state, perm,
2406 KMemoryAttribute::None, KMemoryBlockDisableMergeAttribute::Normal,
2407 KMemoryBlockDisableMergeAttribute::None);
2408
2409 // We successfully mapped the pages.
2410 R_SUCCEED();
2411}
2412
2413Result KPageTable::UnmapPageGroup(KProcessAddress address, const KPageGroup& pg,
2414 KMemoryState state) {
2415 ASSERT(!this->IsLockedByCurrentThread());
2416
2417 // Ensure this is a valid unmap request.
2418 const size_t num_pages = pg.GetNumPages();
2419 const size_t size = num_pages * PageSize;
2420 R_UNLESS(this->CanContain(address, size, state), ResultInvalidCurrentMemory);
2421
2422 // Lock the table.
2423 KScopedLightLock lk(m_general_lock);
2424
2425 // Check if state allows us to unmap.
2426 size_t num_allocator_blocks;
2181 R_TRY(this->CheckMemoryState(std::addressof(num_allocator_blocks), address, size, 2427 R_TRY(this->CheckMemoryState(std::addressof(num_allocator_blocks), address, size,
2182 KMemoryState::All, state, KMemoryPermission::None, 2428 KMemoryState::All, state, KMemoryPermission::None,
2183 KMemoryPermission::None, KMemoryAttribute::All, 2429 KMemoryPermission::None, KMemoryAttribute::All,
2184 KMemoryAttribute::None)); 2430 KMemoryAttribute::None));
2185 2431
2432 // Check that the page group is valid.
2433 R_UNLESS(this->IsValidPageGroup(pg, address, num_pages), ResultInvalidCurrentMemory);
2434
2186 // Create an update allocator. 2435 // Create an update allocator.
2187 Result allocator_result{ResultSuccess}; 2436 Result allocator_result;
2188 KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result), 2437 KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result),
2189 m_memory_block_slab_manager, num_allocator_blocks); 2438 m_memory_block_slab_manager, num_allocator_blocks);
2190 R_TRY(allocator_result); 2439 R_TRY(allocator_result);
2191 2440
2192 // Perform the unmap. 2441 // We're going to perform an update, so create a helper.
2193 R_TRY(Operate(address, num_pages, KMemoryPermission::None, OperationType::Unmap)); 2442 KScopedPageTableUpdater updater(this);
2443
2444 // Perform unmapping operation.
2445 const KPageProperties properties = {KMemoryPermission::None, false, false,
2446 DisableMergeAttribute::None};
2447 R_TRY(this->Operate(address, num_pages, properties.perm, OperationType::Unmap));
2194 2448
2195 // Update the blocks. 2449 // Update the blocks.
2196 m_memory_block_manager.Update(std::addressof(allocator), address, num_pages, KMemoryState::Free, 2450 m_memory_block_manager.Update(std::addressof(allocator), address, num_pages, KMemoryState::Free,
@@ -2550,54 +2804,6 @@ Result KPageTable::SetHeapSize(VAddr* out, size_t size) {
2550 } 2804 }
2551} 2805}
2552 2806
2553ResultVal<VAddr> KPageTable::AllocateAndMapMemory(size_t needed_num_pages, size_t align,
2554 bool is_map_only, VAddr region_start,
2555 size_t region_num_pages, KMemoryState state,
2556 KMemoryPermission perm, PAddr map_addr) {
2557 KScopedLightLock lk(m_general_lock);
2558
2559 R_UNLESS(CanContain(region_start, region_num_pages * PageSize, state),
2560 ResultInvalidCurrentMemory);
2561 R_UNLESS(region_num_pages > needed_num_pages, ResultOutOfMemory);
2562 const VAddr addr{
2563 AllocateVirtualMemory(region_start, region_num_pages, needed_num_pages, align)};
2564 R_UNLESS(addr, ResultOutOfMemory);
2565
2566 // Create an update allocator.
2567 Result allocator_result{ResultSuccess};
2568 KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result),
2569 m_memory_block_slab_manager);
2570
2571 if (is_map_only) {
2572 R_TRY(Operate(addr, needed_num_pages, perm, OperationType::Map, map_addr));
2573 } else {
2574 // Create a page group tohold the pages we allocate.
2575 KPageGroup pg{m_kernel, m_block_info_manager};
2576
2577 R_TRY(m_system.Kernel().MemoryManager().AllocateAndOpen(
2578 &pg, needed_num_pages,
2579 KMemoryManager::EncodeOption(m_memory_pool, m_allocation_option)));
2580
2581 // Ensure that the page group is closed when we're done working with it.
2582 SCOPE_EXIT({ pg.Close(); });
2583
2584 // Clear all pages.
2585 for (const auto& it : pg) {
2586 std::memset(m_system.DeviceMemory().GetPointer<void>(it.GetAddress()),
2587 m_heap_fill_value, it.GetSize());
2588 }
2589
2590 R_TRY(Operate(addr, needed_num_pages, pg, OperationType::MapGroup));
2591 }
2592
2593 // Update the blocks.
2594 m_memory_block_manager.Update(std::addressof(allocator), addr, needed_num_pages, state, perm,
2595 KMemoryAttribute::None, KMemoryBlockDisableMergeAttribute::Normal,
2596 KMemoryBlockDisableMergeAttribute::None);
2597
2598 return addr;
2599}
2600
2601Result KPageTable::LockForMapDeviceAddressSpace(bool* out_is_io, VAddr address, size_t size, 2807Result KPageTable::LockForMapDeviceAddressSpace(bool* out_is_io, VAddr address, size_t size,
2602 KMemoryPermission perm, bool is_aligned, 2808 KMemoryPermission perm, bool is_aligned,
2603 bool check_heap) { 2809 bool check_heap) {
diff --git a/src/core/hle/kernel/k_page_table.h b/src/core/hle/kernel/k_page_table.h
index 0a454b05b..367dab613 100644
--- a/src/core/hle/kernel/k_page_table.h
+++ b/src/core/hle/kernel/k_page_table.h
@@ -24,12 +24,36 @@ class System;
24 24
25namespace Kernel { 25namespace Kernel {
26 26
27enum class DisableMergeAttribute : u8 {
28 None = (0U << 0),
29 DisableHead = (1U << 0),
30 DisableHeadAndBody = (1U << 1),
31 EnableHeadAndBody = (1U << 2),
32 DisableTail = (1U << 3),
33 EnableTail = (1U << 4),
34 EnableAndMergeHeadBodyTail = (1U << 5),
35 EnableHeadBodyTail = EnableHeadAndBody | EnableTail,
36 DisableHeadBodyTail = DisableHeadAndBody | DisableTail,
37};
38
39struct KPageProperties {
40 KMemoryPermission perm;
41 bool io;
42 bool uncached;
43 DisableMergeAttribute disable_merge_attributes;
44};
45static_assert(std::is_trivial_v<KPageProperties>);
46static_assert(sizeof(KPageProperties) == sizeof(u32));
47
27class KBlockInfoManager; 48class KBlockInfoManager;
28class KMemoryBlockManager; 49class KMemoryBlockManager;
29class KResourceLimit; 50class KResourceLimit;
30class KSystemResource; 51class KSystemResource;
31 52
32class KPageTable final { 53class KPageTable final {
54protected:
55 struct PageLinkedList;
56
33public: 57public:
34 enum class ICacheInvalidationStrategy : u32 { InvalidateRange, InvalidateAll }; 58 enum class ICacheInvalidationStrategy : u32 { InvalidateRange, InvalidateAll };
35 59
@@ -57,27 +81,12 @@ public:
57 Result UnmapPhysicalMemory(VAddr addr, size_t size); 81 Result UnmapPhysicalMemory(VAddr addr, size_t size);
58 Result MapMemory(VAddr dst_addr, VAddr src_addr, size_t size); 82 Result MapMemory(VAddr dst_addr, VAddr src_addr, size_t size);
59 Result UnmapMemory(VAddr dst_addr, VAddr src_addr, size_t size); 83 Result UnmapMemory(VAddr dst_addr, VAddr src_addr, size_t size);
60 Result MapPages(VAddr addr, KPageGroup& page_linked_list, KMemoryState state,
61 KMemoryPermission perm);
62 Result MapPages(VAddr* out_addr, size_t num_pages, size_t alignment, PAddr phys_addr,
63 KMemoryState state, KMemoryPermission perm) {
64 R_RETURN(this->MapPages(out_addr, num_pages, alignment, phys_addr, true,
65 this->GetRegionAddress(state),
66 this->GetRegionSize(state) / PageSize, state, perm));
67 }
68 Result UnmapPages(VAddr addr, KPageGroup& page_linked_list, KMemoryState state);
69 Result UnmapPages(VAddr address, size_t num_pages, KMemoryState state);
70 Result SetProcessMemoryPermission(VAddr addr, size_t size, Svc::MemoryPermission svc_perm); 84 Result SetProcessMemoryPermission(VAddr addr, size_t size, Svc::MemoryPermission svc_perm);
71 KMemoryInfo QueryInfo(VAddr addr); 85 KMemoryInfo QueryInfo(VAddr addr);
72 Result SetMemoryPermission(VAddr addr, size_t size, Svc::MemoryPermission perm); 86 Result SetMemoryPermission(VAddr addr, size_t size, Svc::MemoryPermission perm);
73 Result SetMemoryAttribute(VAddr addr, size_t size, u32 mask, u32 attr); 87 Result SetMemoryAttribute(VAddr addr, size_t size, u32 mask, u32 attr);
74 Result SetMaxHeapSize(size_t size); 88 Result SetMaxHeapSize(size_t size);
75 Result SetHeapSize(VAddr* out, size_t size); 89 Result SetHeapSize(VAddr* out, size_t size);
76 ResultVal<VAddr> AllocateAndMapMemory(size_t needed_num_pages, size_t align, bool is_map_only,
77 VAddr region_start, size_t region_num_pages,
78 KMemoryState state, KMemoryPermission perm,
79 PAddr map_addr = 0);
80
81 Result LockForMapDeviceAddressSpace(bool* out_is_io, VAddr address, size_t size, 90 Result LockForMapDeviceAddressSpace(bool* out_is_io, VAddr address, size_t size,
82 KMemoryPermission perm, bool is_aligned, bool check_heap); 91 KMemoryPermission perm, bool is_aligned, bool check_heap);
83 Result LockForUnmapDeviceAddressSpace(VAddr address, size_t size, bool check_heap); 92 Result LockForUnmapDeviceAddressSpace(VAddr address, size_t size, bool check_heap);
@@ -113,6 +122,40 @@ public:
113 122
114 bool CanContain(VAddr addr, size_t size, KMemoryState state) const; 123 bool CanContain(VAddr addr, size_t size, KMemoryState state) const;
115 124
125 Result MapPages(KProcessAddress* out_addr, size_t num_pages, size_t alignment,
126 KPhysicalAddress phys_addr, KProcessAddress region_start,
127 size_t region_num_pages, KMemoryState state, KMemoryPermission perm) {
128 R_RETURN(this->MapPages(out_addr, num_pages, alignment, phys_addr, true, region_start,
129 region_num_pages, state, perm));
130 }
131
132 Result MapPages(KProcessAddress* out_addr, size_t num_pages, size_t alignment,
133 KPhysicalAddress phys_addr, KMemoryState state, KMemoryPermission perm) {
134 R_RETURN(this->MapPages(out_addr, num_pages, alignment, phys_addr, true,
135 this->GetRegionAddress(state),
136 this->GetRegionSize(state) / PageSize, state, perm));
137 }
138
139 Result MapPages(KProcessAddress* out_addr, size_t num_pages, KMemoryState state,
140 KMemoryPermission perm) {
141 R_RETURN(this->MapPages(out_addr, num_pages, PageSize, 0, false,
142 this->GetRegionAddress(state),
143 this->GetRegionSize(state) / PageSize, state, perm));
144 }
145
146 Result MapPages(KProcessAddress address, size_t num_pages, KMemoryState state,
147 KMemoryPermission perm);
148 Result UnmapPages(KProcessAddress address, size_t num_pages, KMemoryState state);
149
150 Result MapPageGroup(KProcessAddress* out_addr, const KPageGroup& pg,
151 KProcessAddress region_start, size_t region_num_pages, KMemoryState state,
152 KMemoryPermission perm);
153 Result MapPageGroup(KProcessAddress address, const KPageGroup& pg, KMemoryState state,
154 KMemoryPermission perm);
155 Result UnmapPageGroup(KProcessAddress address, const KPageGroup& pg, KMemoryState state);
156 void RemapPageGroup(PageLinkedList* page_list, KProcessAddress address, size_t size,
157 const KPageGroup& pg);
158
116protected: 159protected:
117 struct PageLinkedList { 160 struct PageLinkedList {
118 private: 161 private:
@@ -166,11 +209,9 @@ private:
166 static constexpr KMemoryAttribute DefaultMemoryIgnoreAttr = 209 static constexpr KMemoryAttribute DefaultMemoryIgnoreAttr =
167 KMemoryAttribute::IpcLocked | KMemoryAttribute::DeviceShared; 210 KMemoryAttribute::IpcLocked | KMemoryAttribute::DeviceShared;
168 211
169 Result MapPages(VAddr addr, const KPageGroup& page_linked_list, KMemoryPermission perm); 212 Result MapPages(KProcessAddress* out_addr, size_t num_pages, size_t alignment,
170 Result MapPages(VAddr* out_addr, size_t num_pages, size_t alignment, PAddr phys_addr, 213 KPhysicalAddress phys_addr, bool is_pa_valid, KProcessAddress region_start,
171 bool is_pa_valid, VAddr region_start, size_t region_num_pages, 214 size_t region_num_pages, KMemoryState state, KMemoryPermission perm);
172 KMemoryState state, KMemoryPermission perm);
173 Result UnmapPages(VAddr addr, const KPageGroup& page_linked_list);
174 bool IsRegionContiguous(VAddr addr, u64 size) const; 215 bool IsRegionContiguous(VAddr addr, u64 size) const;
175 void AddRegionToPages(VAddr start, size_t num_pages, KPageGroup& page_linked_list); 216 void AddRegionToPages(VAddr start, size_t num_pages, KPageGroup& page_linked_list);
176 KMemoryInfo QueryInfoImpl(VAddr addr); 217 KMemoryInfo QueryInfoImpl(VAddr addr);
@@ -265,6 +306,11 @@ private:
265 void CleanupForIpcClientOnServerSetupFailure(PageLinkedList* page_list, VAddr address, 306 void CleanupForIpcClientOnServerSetupFailure(PageLinkedList* page_list, VAddr address,
266 size_t size, KMemoryPermission prot_perm); 307 size_t size, KMemoryPermission prot_perm);
267 308
309 Result AllocateAndMapPagesImpl(PageLinkedList* page_list, KProcessAddress address,
310 size_t num_pages, KMemoryPermission perm);
311 Result MapPageGroupImpl(PageLinkedList* page_list, KProcessAddress address,
312 const KPageGroup& pg, const KPageProperties properties, bool reuse_ll);
313
268 mutable KLightLock m_general_lock; 314 mutable KLightLock m_general_lock;
269 mutable KLightLock m_map_physical_memory_lock; 315 mutable KLightLock m_map_physical_memory_lock;
270 316
diff --git a/src/core/hle/kernel/k_priority_queue.h b/src/core/hle/kernel/k_priority_queue.h
index cb2512b0b..645c5b531 100644
--- a/src/core/hle/kernel/k_priority_queue.h
+++ b/src/core/hle/kernel/k_priority_queue.h
@@ -17,35 +17,41 @@ namespace Kernel {
17class KThread; 17class KThread;
18 18
19template <typename T> 19template <typename T>
20concept KPriorityQueueAffinityMask = !std::is_reference_v<T> && requires(T & t) { 20concept KPriorityQueueAffinityMask = !
21 { t.GetAffinityMask() } -> Common::ConvertibleTo<u64>; 21std::is_reference_v<T>&& requires(T& t) {
22 {t.SetAffinityMask(0)}; 22 { t.GetAffinityMask() } -> Common::ConvertibleTo<u64>;
23 { t.SetAffinityMask(0) };
23 24
24 { t.GetAffinity(0) } -> std::same_as<bool>; 25 { t.GetAffinity(0) } -> std::same_as<bool>;
25 {t.SetAffinity(0, false)}; 26 { t.SetAffinity(0, false) };
26 {t.SetAll()}; 27 { t.SetAll() };
27}; 28 };
28 29
29template <typename T> 30template <typename T>
30concept KPriorityQueueMember = !std::is_reference_v<T> && requires(T & t) { 31concept KPriorityQueueMember = !
31 {typename T::QueueEntry()}; 32std::is_reference_v<T>&& requires(T& t) {
32 {(typename T::QueueEntry()).Initialize()}; 33 { typename T::QueueEntry() };
33 {(typename T::QueueEntry()).SetPrev(std::addressof(t))}; 34 { (typename T::QueueEntry()).Initialize() };
34 {(typename T::QueueEntry()).SetNext(std::addressof(t))}; 35 { (typename T::QueueEntry()).SetPrev(std::addressof(t)) };
35 { (typename T::QueueEntry()).GetNext() } -> std::same_as<T*>; 36 { (typename T::QueueEntry()).SetNext(std::addressof(t)) };
36 { (typename T::QueueEntry()).GetPrev() } -> std::same_as<T*>; 37 { (typename T::QueueEntry()).GetNext() } -> std::same_as<T*>;
37 { t.GetPriorityQueueEntry(0) } -> std::same_as<typename T::QueueEntry&>; 38 { (typename T::QueueEntry()).GetPrev() } -> std::same_as<T*>;
38 39 {
39 {t.GetAffinityMask()}; 40 t.GetPriorityQueueEntry(0)
40 { std::remove_cvref_t<decltype(t.GetAffinityMask())>() } -> KPriorityQueueAffinityMask; 41 } -> std::same_as<typename T::QueueEntry&>;
41 42
42 { t.GetActiveCore() } -> Common::ConvertibleTo<s32>; 43 { t.GetAffinityMask() };
43 { t.GetPriority() } -> Common::ConvertibleTo<s32>; 44 {
44 { t.IsDummyThread() } -> Common::ConvertibleTo<bool>; 45 std::remove_cvref_t<decltype(t.GetAffinityMask())>()
45}; 46 } -> KPriorityQueueAffinityMask;
47
48 { t.GetActiveCore() } -> Common::ConvertibleTo<s32>;
49 { t.GetPriority() } -> Common::ConvertibleTo<s32>;
50 { t.IsDummyThread() } -> Common::ConvertibleTo<bool>;
51 };
46 52
47template <typename Member, size_t NumCores_, int LowestPriority, int HighestPriority> 53template <typename Member, size_t NumCores_, int LowestPriority, int HighestPriority>
48requires KPriorityQueueMember<Member> 54 requires KPriorityQueueMember<Member>
49class KPriorityQueue { 55class KPriorityQueue {
50public: 56public:
51 using AffinityMaskType = std::remove_cv_t< 57 using AffinityMaskType = std::remove_cv_t<
diff --git a/src/core/hle/kernel/k_process.cpp b/src/core/hle/kernel/k_process.cpp
index a1abf5d68..e201bb0cd 100644
--- a/src/core/hle/kernel/k_process.cpp
+++ b/src/core/hle/kernel/k_process.cpp
@@ -417,9 +417,8 @@ Result KProcess::LoadFromMetadata(const FileSys::ProgramMetadata& metadata, std:
417} 417}
418 418
419void KProcess::Run(s32 main_thread_priority, u64 stack_size) { 419void KProcess::Run(s32 main_thread_priority, u64 stack_size) {
420 AllocateMainThreadStack(stack_size); 420 ASSERT(AllocateMainThreadStack(stack_size) == ResultSuccess);
421 resource_limit->Reserve(LimitableResource::ThreadCountMax, 1); 421 resource_limit->Reserve(LimitableResource::ThreadCountMax, 1);
422 resource_limit->Reserve(LimitableResource::PhysicalMemoryMax, main_thread_stack_size);
423 422
424 const std::size_t heap_capacity{memory_usage_capacity - (main_thread_stack_size + image_size)}; 423 const std::size_t heap_capacity{memory_usage_capacity - (main_thread_stack_size + image_size)};
425 ASSERT(!page_table.SetMaxHeapSize(heap_capacity).IsError()); 424 ASSERT(!page_table.SetMaxHeapSize(heap_capacity).IsError());
@@ -675,20 +674,31 @@ void KProcess::ChangeState(State new_state) {
675} 674}
676 675
677Result KProcess::AllocateMainThreadStack(std::size_t stack_size) { 676Result KProcess::AllocateMainThreadStack(std::size_t stack_size) {
678 ASSERT(stack_size); 677 // Ensure that we haven't already allocated stack.
679 678 ASSERT(main_thread_stack_size == 0);
680 // The kernel always ensures that the given stack size is page aligned. 679
681 main_thread_stack_size = Common::AlignUp(stack_size, PageSize); 680 // Ensure that we're allocating a valid stack.
682 681 stack_size = Common::AlignUp(stack_size, PageSize);
683 const VAddr start{page_table.GetStackRegionStart()}; 682 // R_UNLESS(stack_size + image_size <= m_max_process_memory, ResultOutOfMemory);
684 const std::size_t size{page_table.GetStackRegionEnd() - start}; 683 R_UNLESS(stack_size + image_size >= image_size, ResultOutOfMemory);
685 684
686 CASCADE_RESULT(main_thread_stack_top, 685 // Place a tentative reservation of memory for our new stack.
687 page_table.AllocateAndMapMemory( 686 KScopedResourceReservation mem_reservation(this, Svc::LimitableResource::PhysicalMemoryMax,
688 main_thread_stack_size / PageSize, PageSize, false, start, size / PageSize, 687 stack_size);
689 KMemoryState::Stack, KMemoryPermission::UserReadWrite)); 688 R_UNLESS(mem_reservation.Succeeded(), ResultLimitReached);
689
690 // Allocate and map our stack.
691 if (stack_size) {
692 KProcessAddress stack_bottom;
693 R_TRY(page_table.MapPages(std::addressof(stack_bottom), stack_size / PageSize,
694 KMemoryState::Stack, KMemoryPermission::UserReadWrite));
695
696 main_thread_stack_top = stack_bottom + stack_size;
697 main_thread_stack_size = stack_size;
698 }
690 699
691 main_thread_stack_top += main_thread_stack_size; 700 // We succeeded! Commit our memory reservation.
701 mem_reservation.Commit();
692 702
693 R_SUCCEED(); 703 R_SUCCEED();
694} 704}
diff --git a/src/core/hle/kernel/k_scoped_lock.h b/src/core/hle/kernel/k_scoped_lock.h
index 857e21156..59b3e32ae 100644
--- a/src/core/hle/kernel/k_scoped_lock.h
+++ b/src/core/hle/kernel/k_scoped_lock.h
@@ -9,13 +9,14 @@
9namespace Kernel { 9namespace Kernel {
10 10
11template <typename T> 11template <typename T>
12concept KLockable = !std::is_reference_v<T> && requires(T & t) { 12concept KLockable = !
13 { t.Lock() } -> std::same_as<void>; 13std::is_reference_v<T>&& requires(T& t) {
14 { t.Unlock() } -> std::same_as<void>; 14 { t.Lock() } -> std::same_as<void>;
15}; 15 { t.Unlock() } -> std::same_as<void>;
16 };
16 17
17template <typename T> 18template <typename T>
18requires KLockable<T> 19 requires KLockable<T>
19class [[nodiscard]] KScopedLock { 20class [[nodiscard]] KScopedLock {
20public: 21public:
21 explicit KScopedLock(T* l) : lock_ptr(l) { 22 explicit KScopedLock(T* l) : lock_ptr(l) {
diff --git a/src/core/hle/kernel/k_shared_memory.cpp b/src/core/hle/kernel/k_shared_memory.cpp
index 3cf2b5d91..df505edfe 100644
--- a/src/core/hle/kernel/k_shared_memory.cpp
+++ b/src/core/hle/kernel/k_shared_memory.cpp
@@ -94,15 +94,15 @@ Result KSharedMemory::Map(KProcess& target_process, VAddr address, std::size_t m
94 R_UNLESS(map_perm == test_perm, ResultInvalidNewMemoryPermission); 94 R_UNLESS(map_perm == test_perm, ResultInvalidNewMemoryPermission);
95 } 95 }
96 96
97 return target_process.PageTable().MapPages(address, *page_group, KMemoryState::Shared, 97 return target_process.PageTable().MapPageGroup(address, *page_group, KMemoryState::Shared,
98 ConvertToKMemoryPermission(map_perm)); 98 ConvertToKMemoryPermission(map_perm));
99} 99}
100 100
101Result KSharedMemory::Unmap(KProcess& target_process, VAddr address, std::size_t unmap_size) { 101Result KSharedMemory::Unmap(KProcess& target_process, VAddr address, std::size_t unmap_size) {
102 // Validate the size. 102 // Validate the size.
103 R_UNLESS(size == unmap_size, ResultInvalidSize); 103 R_UNLESS(size == unmap_size, ResultInvalidSize);
104 104
105 return target_process.PageTable().UnmapPages(address, *page_group, KMemoryState::Shared); 105 return target_process.PageTable().UnmapPageGroup(address, *page_group, KMemoryState::Shared);
106} 106}
107 107
108} // namespace Kernel 108} // namespace Kernel
diff --git a/src/core/hle/kernel/k_thread.cpp b/src/core/hle/kernel/k_thread.cpp
index 21207fe99..84ff3c64b 100644
--- a/src/core/hle/kernel/k_thread.cpp
+++ b/src/core/hle/kernel/k_thread.cpp
@@ -330,7 +330,7 @@ void KThread::Finalize() {
330 KThread* const waiter = std::addressof(*it); 330 KThread* const waiter = std::addressof(*it);
331 331
332 // The thread shouldn't be a kernel waiter. 332 // The thread shouldn't be a kernel waiter.
333 ASSERT(!IsKernelAddressKey(waiter->GetAddressKey())); 333 ASSERT(!waiter->GetAddressKeyIsKernel());
334 334
335 // Clear the lock owner. 335 // Clear the lock owner.
336 waiter->SetLockOwner(nullptr); 336 waiter->SetLockOwner(nullptr);
@@ -763,19 +763,6 @@ void KThread::Continue() {
763 KScheduler::OnThreadStateChanged(kernel, this, old_state); 763 KScheduler::OnThreadStateChanged(kernel, this, old_state);
764} 764}
765 765
766void KThread::WaitUntilSuspended() {
767 // Make sure we have a suspend requested.
768 ASSERT(IsSuspendRequested());
769
770 // Loop until the thread is not executing on any core.
771 for (std::size_t i = 0; i < static_cast<std::size_t>(Core::Hardware::NUM_CPU_CORES); ++i) {
772 KThread* core_thread{};
773 do {
774 core_thread = kernel.Scheduler(i).GetSchedulerCurrentThread();
775 } while (core_thread == this);
776 }
777}
778
779Result KThread::SetActivity(Svc::ThreadActivity activity) { 766Result KThread::SetActivity(Svc::ThreadActivity activity) {
780 // Lock ourselves. 767 // Lock ourselves.
781 KScopedLightLock lk(activity_pause_lock); 768 KScopedLightLock lk(activity_pause_lock);
@@ -897,7 +884,7 @@ void KThread::AddWaiterImpl(KThread* thread) {
897 } 884 }
898 885
899 // Keep track of how many kernel waiters we have. 886 // Keep track of how many kernel waiters we have.
900 if (IsKernelAddressKey(thread->GetAddressKey())) { 887 if (thread->GetAddressKeyIsKernel()) {
901 ASSERT((num_kernel_waiters++) >= 0); 888 ASSERT((num_kernel_waiters++) >= 0);
902 KScheduler::SetSchedulerUpdateNeeded(kernel); 889 KScheduler::SetSchedulerUpdateNeeded(kernel);
903 } 890 }
@@ -911,7 +898,7 @@ void KThread::RemoveWaiterImpl(KThread* thread) {
911 ASSERT(kernel.GlobalSchedulerContext().IsLocked()); 898 ASSERT(kernel.GlobalSchedulerContext().IsLocked());
912 899
913 // Keep track of how many kernel waiters we have. 900 // Keep track of how many kernel waiters we have.
914 if (IsKernelAddressKey(thread->GetAddressKey())) { 901 if (thread->GetAddressKeyIsKernel()) {
915 ASSERT((num_kernel_waiters--) > 0); 902 ASSERT((num_kernel_waiters--) > 0);
916 KScheduler::SetSchedulerUpdateNeeded(kernel); 903 KScheduler::SetSchedulerUpdateNeeded(kernel);
917 } 904 }
@@ -987,7 +974,7 @@ KThread* KThread::RemoveWaiterByKey(s32* out_num_waiters, VAddr key) {
987 KThread* thread = std::addressof(*it); 974 KThread* thread = std::addressof(*it);
988 975
989 // Keep track of how many kernel waiters we have. 976 // Keep track of how many kernel waiters we have.
990 if (IsKernelAddressKey(thread->GetAddressKey())) { 977 if (thread->GetAddressKeyIsKernel()) {
991 ASSERT((num_kernel_waiters--) > 0); 978 ASSERT((num_kernel_waiters--) > 0);
992 KScheduler::SetSchedulerUpdateNeeded(kernel); 979 KScheduler::SetSchedulerUpdateNeeded(kernel);
993 } 980 }
diff --git a/src/core/hle/kernel/k_thread.h b/src/core/hle/kernel/k_thread.h
index 7cd94a340..8b8dc51be 100644
--- a/src/core/hle/kernel/k_thread.h
+++ b/src/core/hle/kernel/k_thread.h
@@ -214,8 +214,6 @@ public:
214 214
215 void Continue(); 215 void Continue();
216 216
217 void WaitUntilSuspended();
218
219 constexpr void SetSyncedIndex(s32 index) { 217 constexpr void SetSyncedIndex(s32 index) {
220 synced_index = index; 218 synced_index = index;
221 } 219 }
@@ -607,13 +605,30 @@ public:
607 return address_key_value; 605 return address_key_value;
608 } 606 }
609 607
610 void SetAddressKey(VAddr key) { 608 [[nodiscard]] bool GetAddressKeyIsKernel() const {
609 return address_key_is_kernel;
610 }
611
612 //! NB: intentional deviation from official kernel.
613 //
614 // Separate SetAddressKey into user and kernel versions
615 // to cope with arbitrary host pointers making their way
616 // into things.
617
618 void SetUserAddressKey(VAddr key) {
611 address_key = key; 619 address_key = key;
620 address_key_is_kernel = false;
612 } 621 }
613 622
614 void SetAddressKey(VAddr key, u32 val) { 623 void SetUserAddressKey(VAddr key, u32 val) {
615 address_key = key; 624 address_key = key;
616 address_key_value = val; 625 address_key_value = val;
626 address_key_is_kernel = false;
627 }
628
629 void SetKernelAddressKey(VAddr key) {
630 address_key = key;
631 address_key_is_kernel = true;
617 } 632 }
618 633
619 void ClearWaitQueue() { 634 void ClearWaitQueue() {
@@ -662,7 +677,7 @@ private:
662 union SyncObjectBuffer { 677 union SyncObjectBuffer {
663 std::array<KSynchronizationObject*, Svc::ArgumentHandleCountMax> sync_objects{}; 678 std::array<KSynchronizationObject*, Svc::ArgumentHandleCountMax> sync_objects{};
664 std::array<Handle, 679 std::array<Handle,
665 Svc::ArgumentHandleCountMax*(sizeof(KSynchronizationObject*) / sizeof(Handle))> 680 Svc::ArgumentHandleCountMax * (sizeof(KSynchronizationObject*) / sizeof(Handle))>
666 handles; 681 handles;
667 constexpr SyncObjectBuffer() {} 682 constexpr SyncObjectBuffer() {}
668 }; 683 };
@@ -683,10 +698,8 @@ private:
683 }; 698 };
684 699
685 template <typename T> 700 template <typename T>
686 requires( 701 requires(std::same_as<T, KThread> || std::same_as<T, RedBlackKeyType>)
687 std::same_as<T, KThread> || 702 static constexpr int Compare(const T& lhs, const KThread& rhs) {
688 std::same_as<T, RedBlackKeyType>) static constexpr int Compare(const T& lhs,
689 const KThread& rhs) {
690 const u64 l_key = lhs.GetConditionVariableKey(); 703 const u64 l_key = lhs.GetConditionVariableKey();
691 const u64 r_key = rhs.GetConditionVariableKey(); 704 const u64 r_key = rhs.GetConditionVariableKey();
692 705
@@ -772,6 +785,7 @@ private:
772 bool debug_attached{}; 785 bool debug_attached{};
773 s8 priority_inheritance_count{}; 786 s8 priority_inheritance_count{};
774 bool resource_limit_release_hint{}; 787 bool resource_limit_release_hint{};
788 bool address_key_is_kernel{};
775 StackParameters stack_parameters{}; 789 StackParameters stack_parameters{};
776 Common::SpinLock context_guard{}; 790 Common::SpinLock context_guard{};
777 791
diff --git a/src/core/hle/kernel/k_thread_local_page.h b/src/core/hle/kernel/k_thread_local_page.h
index fe0cff084..71254eb55 100644
--- a/src/core/hle/kernel/k_thread_local_page.h
+++ b/src/core/hle/kernel/k_thread_local_page.h
@@ -70,10 +70,8 @@ public:
70 } 70 }
71 71
72 template <typename T> 72 template <typename T>
73 requires(std::same_as<T, KThreadLocalPage> || 73 requires(std::same_as<T, KThreadLocalPage> || std::same_as<T, RedBlackKeyType>)
74 std::same_as<T, RedBlackKeyType>) static constexpr int Compare(const T& lhs, 74 static constexpr int Compare(const T& lhs, const KThreadLocalPage& rhs) {
75 const KThreadLocalPage&
76 rhs) {
77 const VAddr lval = GetRedBlackKey(lhs); 75 const VAddr lval = GetRedBlackKey(lhs);
78 const VAddr rval = GetRedBlackKey(rhs); 76 const VAddr rval = GetRedBlackKey(rhs);
79 77
diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp
index 1fb25f221..d9eafe261 100644
--- a/src/core/hle/kernel/kernel.cpp
+++ b/src/core/hle/kernel/kernel.cpp
@@ -1198,28 +1198,35 @@ void KernelCore::Suspend(bool suspended) {
1198 const bool should_suspend{exception_exited || suspended}; 1198 const bool should_suspend{exception_exited || suspended};
1199 const auto activity = should_suspend ? ProcessActivity::Paused : ProcessActivity::Runnable; 1199 const auto activity = should_suspend ? ProcessActivity::Paused : ProcessActivity::Runnable;
1200 1200
1201 std::vector<KScopedAutoObject<KThread>> process_threads; 1201 //! This refers to the application process, not the current process.
1202 { 1202 KScopedAutoObject<KProcess> process = CurrentProcess();
1203 KScopedSchedulerLock sl{*this}; 1203 if (process.IsNull()) {
1204 return;
1205 }
1204 1206
1205 if (auto* process = CurrentProcess(); process != nullptr) { 1207 // Set the new activity.
1206 process->SetActivity(activity); 1208 process->SetActivity(activity);
1207 1209
1208 if (!should_suspend) { 1210 // Wait for process execution to stop.
1209 // Runnable now; no need to wait. 1211 bool must_wait{should_suspend};
1210 return; 1212
1211 } 1213 // KernelCore::Suspend must be called from locked context, or we
1214 // could race another call to SetActivity, interfering with waiting.
1215 while (must_wait) {
1216 KScopedSchedulerLock sl{*this};
1217
1218 // Assume that all threads have finished running.
1219 must_wait = false;
1212 1220
1213 for (auto* thread : process->GetThreadList()) { 1221 for (auto i = 0; i < static_cast<s32>(Core::Hardware::NUM_CPU_CORES); ++i) {
1214 process_threads.emplace_back(thread); 1222 if (Scheduler(i).GetSchedulerCurrentThread()->GetOwnerProcess() ==
1223 process.GetPointerUnsafe()) {
1224 // A thread has not finished running yet.
1225 // Continue waiting.
1226 must_wait = true;
1215 } 1227 }
1216 } 1228 }
1217 } 1229 }
1218
1219 // Wait for execution to stop.
1220 for (auto& thread : process_threads) {
1221 thread->WaitUntilSuspended();
1222 }
1223} 1230}
1224 1231
1225void KernelCore::ShutdownCores() { 1232void KernelCore::ShutdownCores() {
diff --git a/src/core/hle/kernel/kernel.h b/src/core/hle/kernel/kernel.h
index 8d22f8d2c..5f52e1e95 100644
--- a/src/core/hle/kernel/kernel.h
+++ b/src/core/hle/kernel/kernel.h
@@ -35,6 +35,7 @@ class GlobalSchedulerContext;
35class KAutoObjectWithListContainer; 35class KAutoObjectWithListContainer;
36class KClientSession; 36class KClientSession;
37class KDebug; 37class KDebug;
38class KDeviceAddressSpace;
38class KDynamicPageManager; 39class KDynamicPageManager;
39class KEvent; 40class KEvent;
40class KEventInfo; 41class KEventInfo;
@@ -359,6 +360,8 @@ public:
359 return slab_heap_container->transfer_memory; 360 return slab_heap_container->transfer_memory;
360 } else if constexpr (std::is_same_v<T, KCodeMemory>) { 361 } else if constexpr (std::is_same_v<T, KCodeMemory>) {
361 return slab_heap_container->code_memory; 362 return slab_heap_container->code_memory;
363 } else if constexpr (std::is_same_v<T, KDeviceAddressSpace>) {
364 return slab_heap_container->device_address_space;
362 } else if constexpr (std::is_same_v<T, KPageBuffer>) { 365 } else if constexpr (std::is_same_v<T, KPageBuffer>) {
363 return slab_heap_container->page_buffer; 366 return slab_heap_container->page_buffer;
364 } else if constexpr (std::is_same_v<T, KThreadLocalPage>) { 367 } else if constexpr (std::is_same_v<T, KThreadLocalPage>) {
@@ -431,6 +434,7 @@ private:
431 KSlabHeap<KThread> thread; 434 KSlabHeap<KThread> thread;
432 KSlabHeap<KTransferMemory> transfer_memory; 435 KSlabHeap<KTransferMemory> transfer_memory;
433 KSlabHeap<KCodeMemory> code_memory; 436 KSlabHeap<KCodeMemory> code_memory;
437 KSlabHeap<KDeviceAddressSpace> device_address_space;
434 KSlabHeap<KPageBuffer> page_buffer; 438 KSlabHeap<KPageBuffer> page_buffer;
435 KSlabHeap<KThreadLocalPage> thread_local_page; 439 KSlabHeap<KThreadLocalPage> thread_local_page;
436 KSlabHeap<KSessionRequest> session_request; 440 KSlabHeap<KSessionRequest> session_request;
diff --git a/src/core/hle/kernel/svc.cpp b/src/core/hle/kernel/svc.cpp
index aca442196..4cb6f40a0 100644
--- a/src/core/hle/kernel/svc.cpp
+++ b/src/core/hle/kernel/svc.cpp
@@ -1,2697 +1,16 @@
1// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project 1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later 2// SPDX-License-Identifier: GPL-2.0-or-later
3 3
4#include <algorithm> 4#include "common/common_types.h"
5#include <cinttypes>
6#include <iterator>
7#include <mutex>
8#include <vector>
9
10#include "common/alignment.h"
11#include "common/assert.h"
12#include "common/common_funcs.h"
13#include "common/fiber.h"
14#include "common/logging/log.h"
15#include "common/scope_exit.h"
16#include "core/core.h"
17#include "core/core_timing.h"
18#include "core/debugger/debugger.h"
19#include "core/hle/kernel/k_client_port.h"
20#include "core/hle/kernel/k_client_session.h"
21#include "core/hle/kernel/k_code_memory.h"
22#include "core/hle/kernel/k_event.h"
23#include "core/hle/kernel/k_handle_table.h"
24#include "core/hle/kernel/k_memory_block.h"
25#include "core/hle/kernel/k_memory_layout.h"
26#include "core/hle/kernel/k_page_table.h"
27#include "core/hle/kernel/k_port.h"
28#include "core/hle/kernel/k_process.h" 5#include "core/hle/kernel/k_process.h"
29#include "core/hle/kernel/k_readable_event.h"
30#include "core/hle/kernel/k_resource_limit.h"
31#include "core/hle/kernel/k_scheduler.h"
32#include "core/hle/kernel/k_scoped_resource_reservation.h"
33#include "core/hle/kernel/k_session.h"
34#include "core/hle/kernel/k_shared_memory.h"
35#include "core/hle/kernel/k_synchronization_object.h"
36#include "core/hle/kernel/k_thread.h" 6#include "core/hle/kernel/k_thread.h"
37#include "core/hle/kernel/k_thread_queue.h"
38#include "core/hle/kernel/k_transfer_memory.h"
39#include "core/hle/kernel/kernel.h"
40#include "core/hle/kernel/physical_core.h"
41#include "core/hle/kernel/svc.h" 7#include "core/hle/kernel/svc.h"
42#include "core/hle/kernel/svc_results.h"
43#include "core/hle/kernel/svc_types.h"
44#include "core/hle/kernel/svc_wrap.h" 8#include "core/hle/kernel/svc_wrap.h"
45#include "core/hle/result.h" 9#include "core/hle/result.h"
46#include "core/memory.h"
47#include "core/reporter.h"
48 10
49namespace Kernel::Svc { 11namespace Kernel::Svc {
50namespace { 12namespace {
51 13
52// Checks if address + size is greater than the given address
53// This can return false if the size causes an overflow of a 64-bit type
54// or if the given size is zero.
55constexpr bool IsValidAddressRange(VAddr address, u64 size) {
56 return address + size > address;
57}
58
59// Helper function that performs the common sanity checks for svcMapMemory
60// and svcUnmapMemory. This is doable, as both functions perform their sanitizing
61// in the same order.
62Result MapUnmapMemorySanityChecks(const KPageTable& manager, VAddr dst_addr, VAddr src_addr,
63 u64 size) {
64 if (!Common::Is4KBAligned(dst_addr)) {
65 LOG_ERROR(Kernel_SVC, "Destination address is not aligned to 4KB, 0x{:016X}", dst_addr);
66 return ResultInvalidAddress;
67 }
68
69 if (!Common::Is4KBAligned(src_addr)) {
70 LOG_ERROR(Kernel_SVC, "Source address is not aligned to 4KB, 0x{:016X}", src_addr);
71 return ResultInvalidSize;
72 }
73
74 if (size == 0) {
75 LOG_ERROR(Kernel_SVC, "Size is 0");
76 return ResultInvalidSize;
77 }
78
79 if (!Common::Is4KBAligned(size)) {
80 LOG_ERROR(Kernel_SVC, "Size is not aligned to 4KB, 0x{:016X}", size);
81 return ResultInvalidSize;
82 }
83
84 if (!IsValidAddressRange(dst_addr, size)) {
85 LOG_ERROR(Kernel_SVC,
86 "Destination is not a valid address range, addr=0x{:016X}, size=0x{:016X}",
87 dst_addr, size);
88 return ResultInvalidCurrentMemory;
89 }
90
91 if (!IsValidAddressRange(src_addr, size)) {
92 LOG_ERROR(Kernel_SVC, "Source is not a valid address range, addr=0x{:016X}, size=0x{:016X}",
93 src_addr, size);
94 return ResultInvalidCurrentMemory;
95 }
96
97 if (!manager.IsInsideAddressSpace(src_addr, size)) {
98 LOG_ERROR(Kernel_SVC,
99 "Source is not within the address space, addr=0x{:016X}, size=0x{:016X}",
100 src_addr, size);
101 return ResultInvalidCurrentMemory;
102 }
103
104 if (manager.IsOutsideStackRegion(dst_addr, size)) {
105 LOG_ERROR(Kernel_SVC,
106 "Destination is not within the stack region, addr=0x{:016X}, size=0x{:016X}",
107 dst_addr, size);
108 return ResultInvalidMemoryRegion;
109 }
110
111 if (manager.IsInsideHeapRegion(dst_addr, size)) {
112 LOG_ERROR(Kernel_SVC,
113 "Destination does not fit within the heap region, addr=0x{:016X}, "
114 "size=0x{:016X}",
115 dst_addr, size);
116 return ResultInvalidMemoryRegion;
117 }
118
119 if (manager.IsInsideAliasRegion(dst_addr, size)) {
120 LOG_ERROR(Kernel_SVC,
121 "Destination does not fit within the map region, addr=0x{:016X}, "
122 "size=0x{:016X}",
123 dst_addr, size);
124 return ResultInvalidMemoryRegion;
125 }
126
127 return ResultSuccess;
128}
129
130enum class ResourceLimitValueType {
131 CurrentValue,
132 LimitValue,
133 PeakValue,
134};
135
136} // Anonymous namespace
137
138/// Set the process heap to a given Size. It can both extend and shrink the heap.
139static Result SetHeapSize(Core::System& system, VAddr* out_address, u64 size) {
140 LOG_TRACE(Kernel_SVC, "called, heap_size=0x{:X}", size);
141
142 // Validate size.
143 R_UNLESS(Common::IsAligned(size, HeapSizeAlignment), ResultInvalidSize);
144 R_UNLESS(size < MainMemorySizeMax, ResultInvalidSize);
145
146 // Set the heap size.
147 R_TRY(system.Kernel().CurrentProcess()->PageTable().SetHeapSize(out_address, size));
148
149 return ResultSuccess;
150}
151
152static Result SetHeapSize32(Core::System& system, u32* heap_addr, u32 heap_size) {
153 VAddr temp_heap_addr{};
154 const Result result{SetHeapSize(system, &temp_heap_addr, heap_size)};
155 *heap_addr = static_cast<u32>(temp_heap_addr);
156 return result;
157}
158
159constexpr bool IsValidSetMemoryPermission(MemoryPermission perm) {
160 switch (perm) {
161 case MemoryPermission::None:
162 case MemoryPermission::Read:
163 case MemoryPermission::ReadWrite:
164 return true;
165 default:
166 return false;
167 }
168}
169
170static Result SetMemoryPermission(Core::System& system, VAddr address, u64 size,
171 MemoryPermission perm) {
172 LOG_DEBUG(Kernel_SVC, "called, address=0x{:016X}, size=0x{:X}, perm=0x{:08X", address, size,
173 perm);
174
175 // Validate address / size.
176 R_UNLESS(Common::IsAligned(address, PageSize), ResultInvalidAddress);
177 R_UNLESS(Common::IsAligned(size, PageSize), ResultInvalidSize);
178 R_UNLESS(size > 0, ResultInvalidSize);
179 R_UNLESS((address < address + size), ResultInvalidCurrentMemory);
180
181 // Validate the permission.
182 R_UNLESS(IsValidSetMemoryPermission(perm), ResultInvalidNewMemoryPermission);
183
184 // Validate that the region is in range for the current process.
185 auto& page_table = system.Kernel().CurrentProcess()->PageTable();
186 R_UNLESS(page_table.Contains(address, size), ResultInvalidCurrentMemory);
187
188 // Set the memory attribute.
189 return page_table.SetMemoryPermission(address, size, perm);
190}
191
192static Result SetMemoryAttribute(Core::System& system, VAddr address, u64 size, u32 mask,
193 u32 attr) {
194 LOG_DEBUG(Kernel_SVC,
195 "called, address=0x{:016X}, size=0x{:X}, mask=0x{:08X}, attribute=0x{:08X}", address,
196 size, mask, attr);
197
198 // Validate address / size.
199 R_UNLESS(Common::IsAligned(address, PageSize), ResultInvalidAddress);
200 R_UNLESS(Common::IsAligned(size, PageSize), ResultInvalidSize);
201 R_UNLESS(size > 0, ResultInvalidSize);
202 R_UNLESS((address < address + size), ResultInvalidCurrentMemory);
203
204 // Validate the attribute and mask.
205 constexpr u32 SupportedMask = static_cast<u32>(MemoryAttribute::Uncached);
206 R_UNLESS((mask | attr) == mask, ResultInvalidCombination);
207 R_UNLESS((mask | attr | SupportedMask) == SupportedMask, ResultInvalidCombination);
208
209 // Validate that the region is in range for the current process.
210 auto& page_table{system.Kernel().CurrentProcess()->PageTable()};
211 R_UNLESS(page_table.Contains(address, size), ResultInvalidCurrentMemory);
212
213 // Set the memory attribute.
214 return page_table.SetMemoryAttribute(address, size, mask, attr);
215}
216
217static Result SetMemoryAttribute32(Core::System& system, u32 address, u32 size, u32 mask,
218 u32 attr) {
219 return SetMemoryAttribute(system, address, size, mask, attr);
220}
221
222/// Maps a memory range into a different range.
223static Result MapMemory(Core::System& system, VAddr dst_addr, VAddr src_addr, u64 size) {
224 LOG_TRACE(Kernel_SVC, "called, dst_addr=0x{:X}, src_addr=0x{:X}, size=0x{:X}", dst_addr,
225 src_addr, size);
226
227 auto& page_table{system.Kernel().CurrentProcess()->PageTable()};
228
229 if (const Result result{MapUnmapMemorySanityChecks(page_table, dst_addr, src_addr, size)};
230 result.IsError()) {
231 return result;
232 }
233
234 return page_table.MapMemory(dst_addr, src_addr, size);
235}
236
237static Result MapMemory32(Core::System& system, u32 dst_addr, u32 src_addr, u32 size) {
238 return MapMemory(system, dst_addr, src_addr, size);
239}
240
241/// Unmaps a region that was previously mapped with svcMapMemory
242static Result UnmapMemory(Core::System& system, VAddr dst_addr, VAddr src_addr, u64 size) {
243 LOG_TRACE(Kernel_SVC, "called, dst_addr=0x{:X}, src_addr=0x{:X}, size=0x{:X}", dst_addr,
244 src_addr, size);
245
246 auto& page_table{system.Kernel().CurrentProcess()->PageTable()};
247
248 if (const Result result{MapUnmapMemorySanityChecks(page_table, dst_addr, src_addr, size)};
249 result.IsError()) {
250 return result;
251 }
252
253 return page_table.UnmapMemory(dst_addr, src_addr, size);
254}
255
256static Result UnmapMemory32(Core::System& system, u32 dst_addr, u32 src_addr, u32 size) {
257 return UnmapMemory(system, dst_addr, src_addr, size);
258}
259
260template <typename T>
261Result CreateSession(Core::System& system, Handle* out_server, Handle* out_client, u64 name) {
262 auto& process = *system.CurrentProcess();
263 auto& handle_table = process.GetHandleTable();
264
265 // Declare the session we're going to allocate.
266 T* session;
267
268 // Reserve a new session from the process resource limit.
269 // FIXME: LimitableResource_SessionCountMax
270 KScopedResourceReservation session_reservation(&process, LimitableResource::SessionCountMax);
271 if (session_reservation.Succeeded()) {
272 session = T::Create(system.Kernel());
273 } else {
274 return ResultLimitReached;
275
276 // // We couldn't reserve a session. Check that we support dynamically expanding the
277 // // resource limit.
278 // R_UNLESS(process.GetResourceLimit() ==
279 // &system.Kernel().GetSystemResourceLimit(), ResultLimitReached);
280 // R_UNLESS(KTargetSystem::IsDynamicResourceLimitsEnabled(), ResultLimitReached());
281
282 // // Try to allocate a session from unused slab memory.
283 // session = T::CreateFromUnusedSlabMemory();
284 // R_UNLESS(session != nullptr, ResultLimitReached);
285 // ON_RESULT_FAILURE { session->Close(); };
286
287 // // If we're creating a KSession, we want to add two KSessionRequests to the heap, to
288 // // prevent request exhaustion.
289 // // NOTE: Nintendo checks if session->DynamicCast<KSession *>() != nullptr, but there's
290 // // no reason to not do this statically.
291 // if constexpr (std::same_as<T, KSession>) {
292 // for (size_t i = 0; i < 2; i++) {
293 // KSessionRequest* request = KSessionRequest::CreateFromUnusedSlabMemory();
294 // R_UNLESS(request != nullptr, ResultLimitReached);
295 // request->Close();
296 // }
297 // }
298
299 // We successfully allocated a session, so add the object we allocated to the resource
300 // limit.
301 // system.Kernel().GetSystemResourceLimit().Reserve(LimitableResource::SessionCountMax, 1);
302 }
303
304 // Check that we successfully created a session.
305 R_UNLESS(session != nullptr, ResultOutOfResource);
306
307 // Initialize the session.
308 session->Initialize(nullptr, fmt::format("{}", name));
309
310 // Commit the session reservation.
311 session_reservation.Commit();
312
313 // Ensure that we clean up the session (and its only references are handle table) on function
314 // end.
315 SCOPE_EXIT({
316 session->GetClientSession().Close();
317 session->GetServerSession().Close();
318 });
319
320 // Register the session.
321 T::Register(system.Kernel(), session);
322
323 // Add the server session to the handle table.
324 R_TRY(handle_table.Add(out_server, &session->GetServerSession()));
325
326 // Add the client session to the handle table.
327 const auto result = handle_table.Add(out_client, &session->GetClientSession());
328
329 if (!R_SUCCEEDED(result)) {
330 // Ensure that we maintaing a clean handle state on exit.
331 handle_table.Remove(*out_server);
332 }
333
334 return result;
335}
336
337static Result CreateSession(Core::System& system, Handle* out_server, Handle* out_client,
338 u32 is_light, u64 name) {
339 if (is_light) {
340 // return CreateSession<KLightSession>(system, out_server, out_client, name);
341 return ResultUnknown;
342 } else {
343 return CreateSession<KSession>(system, out_server, out_client, name);
344 }
345}
346
347/// Connect to an OS service given the port name, returns the handle to the port to out
348static Result ConnectToNamedPort(Core::System& system, Handle* out, VAddr port_name_address) {
349 auto& memory = system.Memory();
350 if (!memory.IsValidVirtualAddress(port_name_address)) {
351 LOG_ERROR(Kernel_SVC,
352 "Port Name Address is not a valid virtual address, port_name_address=0x{:016X}",
353 port_name_address);
354 return ResultNotFound;
355 }
356
357 static constexpr std::size_t PortNameMaxLength = 11;
358 // Read 1 char beyond the max allowed port name to detect names that are too long.
359 const std::string port_name = memory.ReadCString(port_name_address, PortNameMaxLength + 1);
360 if (port_name.size() > PortNameMaxLength) {
361 LOG_ERROR(Kernel_SVC, "Port name is too long, expected {} but got {}", PortNameMaxLength,
362 port_name.size());
363 return ResultOutOfRange;
364 }
365
366 LOG_TRACE(Kernel_SVC, "called port_name={}", port_name);
367
368 // Get the current handle table.
369 auto& kernel = system.Kernel();
370 auto& handle_table = kernel.CurrentProcess()->GetHandleTable();
371
372 // Find the client port.
373 auto port = kernel.CreateNamedServicePort(port_name);
374 if (!port) {
375 LOG_ERROR(Kernel_SVC, "tried to connect to unknown port: {}", port_name);
376 return ResultNotFound;
377 }
378
379 // Reserve a handle for the port.
380 // NOTE: Nintendo really does write directly to the output handle here.
381 R_TRY(handle_table.Reserve(out));
382 auto handle_guard = SCOPE_GUARD({ handle_table.Unreserve(*out); });
383
384 // Create a session.
385 KClientSession* session{};
386 R_TRY(port->CreateSession(std::addressof(session)));
387
388 kernel.RegisterNamedServiceHandler(port_name, &port->GetParent()->GetServerPort());
389
390 // Register the session in the table, close the extra reference.
391 handle_table.Register(*out, session);
392 session->Close();
393
394 // We succeeded.
395 handle_guard.Cancel();
396 return ResultSuccess;
397}
398
399static Result ConnectToNamedPort32(Core::System& system, Handle* out_handle,
400 u32 port_name_address) {
401
402 return ConnectToNamedPort(system, out_handle, port_name_address);
403}
404
405/// Makes a blocking IPC call to a service.
406static Result SendSyncRequest(Core::System& system, Handle handle) {
407 auto& kernel = system.Kernel();
408
409 // Create the wait queue.
410 KThreadQueue wait_queue(kernel);
411
412 // Get the client session from its handle.
413 KScopedAutoObject session =
414 kernel.CurrentProcess()->GetHandleTable().GetObject<KClientSession>(handle);
415 R_UNLESS(session.IsNotNull(), ResultInvalidHandle);
416
417 LOG_TRACE(Kernel_SVC, "called handle=0x{:08X}({})", handle, session->GetName());
418
419 return session->SendSyncRequest();
420}
421
422static Result SendSyncRequest32(Core::System& system, Handle handle) {
423 return SendSyncRequest(system, handle);
424}
425
426static Result ReplyAndReceive(Core::System& system, s32* out_index, Handle* handles,
427 s32 num_handles, Handle reply_target, s64 timeout_ns) {
428 auto& kernel = system.Kernel();
429 auto& handle_table = GetCurrentThread(kernel).GetOwnerProcess()->GetHandleTable();
430
431 // Convert handle list to object table.
432 std::vector<KSynchronizationObject*> objs(num_handles);
433 R_UNLESS(
434 handle_table.GetMultipleObjects<KSynchronizationObject>(objs.data(), handles, num_handles),
435 ResultInvalidHandle);
436
437 // Ensure handles are closed when we're done.
438 SCOPE_EXIT({
439 for (auto i = 0; i < num_handles; ++i) {
440 objs[i]->Close();
441 }
442 });
443
444 // Reply to the target, if one is specified.
445 if (reply_target != InvalidHandle) {
446 KScopedAutoObject session = handle_table.GetObject<KServerSession>(reply_target);
447 R_UNLESS(session.IsNotNull(), ResultInvalidHandle);
448
449 // If we fail to reply, we want to set the output index to -1.
450 // ON_RESULT_FAILURE { *out_index = -1; };
451
452 // Send the reply.
453 // R_TRY(session->SendReply());
454
455 Result rc = session->SendReply();
456 if (!R_SUCCEEDED(rc)) {
457 *out_index = -1;
458 return rc;
459 }
460 }
461
462 // Wait for a message.
463 while (true) {
464 // Wait for an object.
465 s32 index;
466 Result result = KSynchronizationObject::Wait(kernel, &index, objs.data(),
467 static_cast<s32>(objs.size()), timeout_ns);
468 if (result == ResultTimedOut) {
469 return result;
470 }
471
472 // Receive the request.
473 if (R_SUCCEEDED(result)) {
474 KServerSession* session = objs[index]->DynamicCast<KServerSession*>();
475 if (session != nullptr) {
476 result = session->ReceiveRequest();
477 if (result == ResultNotFound) {
478 continue;
479 }
480 }
481 }
482
483 *out_index = index;
484 return result;
485 }
486}
487
488/// Get the ID for the specified thread.
489static Result GetThreadId(Core::System& system, u64* out_thread_id, Handle thread_handle) {
490 // Get the thread from its handle.
491 KScopedAutoObject thread =
492 system.Kernel().CurrentProcess()->GetHandleTable().GetObject<KThread>(thread_handle);
493 R_UNLESS(thread.IsNotNull(), ResultInvalidHandle);
494
495 // Get the thread's id.
496 *out_thread_id = thread->GetId();
497 return ResultSuccess;
498}
499
500static Result GetThreadId32(Core::System& system, u32* out_thread_id_low, u32* out_thread_id_high,
501 Handle thread_handle) {
502 u64 out_thread_id{};
503 const Result result{GetThreadId(system, &out_thread_id, thread_handle)};
504
505 *out_thread_id_low = static_cast<u32>(out_thread_id >> 32);
506 *out_thread_id_high = static_cast<u32>(out_thread_id & std::numeric_limits<u32>::max());
507
508 return result;
509}
510
511/// Gets the ID of the specified process or a specified thread's owning process.
512static Result GetProcessId(Core::System& system, u64* out_process_id, Handle handle) {
513 LOG_DEBUG(Kernel_SVC, "called handle=0x{:08X}", handle);
514
515 // Get the object from the handle table.
516 KScopedAutoObject obj =
517 system.Kernel().CurrentProcess()->GetHandleTable().GetObject<KAutoObject>(
518 static_cast<Handle>(handle));
519 R_UNLESS(obj.IsNotNull(), ResultInvalidHandle);
520
521 // Get the process from the object.
522 KProcess* process = nullptr;
523 if (KProcess* p = obj->DynamicCast<KProcess*>(); p != nullptr) {
524 // The object is a process, so we can use it directly.
525 process = p;
526 } else if (KThread* t = obj->DynamicCast<KThread*>(); t != nullptr) {
527 // The object is a thread, so we want to use its parent.
528 process = reinterpret_cast<KThread*>(obj.GetPointerUnsafe())->GetOwnerProcess();
529 } else {
530 // TODO(bunnei): This should also handle debug objects before returning.
531 UNIMPLEMENTED_MSG("Debug objects not implemented");
532 }
533
534 // Make sure the target process exists.
535 R_UNLESS(process != nullptr, ResultInvalidHandle);
536
537 // Get the process id.
538 *out_process_id = process->GetId();
539
540 return ResultSuccess;
541}
542
543static Result GetProcessId32(Core::System& system, u32* out_process_id_low,
544 u32* out_process_id_high, Handle handle) {
545 u64 out_process_id{};
546 const auto result = GetProcessId(system, &out_process_id, handle);
547 *out_process_id_low = static_cast<u32>(out_process_id);
548 *out_process_id_high = static_cast<u32>(out_process_id >> 32);
549 return result;
550}
551
552/// Wait for the given handles to synchronize, timeout after the specified nanoseconds
553static Result WaitSynchronization(Core::System& system, s32* index, VAddr handles_address,
554 s32 num_handles, s64 nano_seconds) {
555 LOG_TRACE(Kernel_SVC, "called handles_address=0x{:X}, num_handles={}, nano_seconds={}",
556 handles_address, num_handles, nano_seconds);
557
558 // Ensure number of handles is valid.
559 R_UNLESS(0 <= num_handles && num_handles <= ArgumentHandleCountMax, ResultOutOfRange);
560
561 auto& kernel = system.Kernel();
562 std::vector<KSynchronizationObject*> objs(num_handles);
563 const auto& handle_table = kernel.CurrentProcess()->GetHandleTable();
564 Handle* handles = system.Memory().GetPointer<Handle>(handles_address);
565
566 // Copy user handles.
567 if (num_handles > 0) {
568 // Convert the handles to objects.
569 R_UNLESS(handle_table.GetMultipleObjects<KSynchronizationObject>(objs.data(), handles,
570 num_handles),
571 ResultInvalidHandle);
572 for (const auto& obj : objs) {
573 kernel.RegisterInUseObject(obj);
574 }
575 }
576
577 // Ensure handles are closed when we're done.
578 SCOPE_EXIT({
579 for (s32 i = 0; i < num_handles; ++i) {
580 kernel.UnregisterInUseObject(objs[i]);
581 objs[i]->Close();
582 }
583 });
584
585 return KSynchronizationObject::Wait(kernel, index, objs.data(), static_cast<s32>(objs.size()),
586 nano_seconds);
587}
588
589static Result WaitSynchronization32(Core::System& system, u32 timeout_low, u32 handles_address,
590 s32 num_handles, u32 timeout_high, s32* index) {
591 const s64 nano_seconds{(static_cast<s64>(timeout_high) << 32) | static_cast<s64>(timeout_low)};
592 return WaitSynchronization(system, index, handles_address, num_handles, nano_seconds);
593}
594
595/// Resumes a thread waiting on WaitSynchronization
596static Result CancelSynchronization(Core::System& system, Handle handle) {
597 LOG_TRACE(Kernel_SVC, "called handle=0x{:X}", handle);
598
599 // Get the thread from its handle.
600 KScopedAutoObject thread =
601 system.Kernel().CurrentProcess()->GetHandleTable().GetObject<KThread>(handle);
602 R_UNLESS(thread.IsNotNull(), ResultInvalidHandle);
603
604 // Cancel the thread's wait.
605 thread->WaitCancel();
606 return ResultSuccess;
607}
608
609static Result CancelSynchronization32(Core::System& system, Handle handle) {
610 return CancelSynchronization(system, handle);
611}
612
613/// Attempts to locks a mutex
614static Result ArbitrateLock(Core::System& system, Handle thread_handle, VAddr address, u32 tag) {
615 LOG_TRACE(Kernel_SVC, "called thread_handle=0x{:08X}, address=0x{:X}, tag=0x{:08X}",
616 thread_handle, address, tag);
617
618 // Validate the input address.
619 if (IsKernelAddress(address)) {
620 LOG_ERROR(Kernel_SVC, "Attempting to arbitrate a lock on a kernel address (address={:08X})",
621 address);
622 return ResultInvalidCurrentMemory;
623 }
624 if (!Common::IsAligned(address, sizeof(u32))) {
625 LOG_ERROR(Kernel_SVC, "Input address must be 4 byte aligned (address: {:08X})", address);
626 return ResultInvalidAddress;
627 }
628
629 return system.Kernel().CurrentProcess()->WaitForAddress(thread_handle, address, tag);
630}
631
632static Result ArbitrateLock32(Core::System& system, Handle thread_handle, u32 address, u32 tag) {
633 return ArbitrateLock(system, thread_handle, address, tag);
634}
635
636/// Unlock a mutex
637static Result ArbitrateUnlock(Core::System& system, VAddr address) {
638 LOG_TRACE(Kernel_SVC, "called address=0x{:X}", address);
639
640 // Validate the input address.
641 if (IsKernelAddress(address)) {
642 LOG_ERROR(Kernel_SVC,
643 "Attempting to arbitrate an unlock on a kernel address (address={:08X})",
644 address);
645 return ResultInvalidCurrentMemory;
646 }
647 if (!Common::IsAligned(address, sizeof(u32))) {
648 LOG_ERROR(Kernel_SVC, "Input address must be 4 byte aligned (address: {:08X})", address);
649 return ResultInvalidAddress;
650 }
651
652 return system.Kernel().CurrentProcess()->SignalToAddress(address);
653}
654
655static Result ArbitrateUnlock32(Core::System& system, u32 address) {
656 return ArbitrateUnlock(system, address);
657}
658
659/// Break program execution
660static void Break(Core::System& system, u32 reason, u64 info1, u64 info2) {
661 BreakReason break_reason =
662 static_cast<BreakReason>(reason & ~static_cast<u32>(BreakReason::NotificationOnlyFlag));
663 bool notification_only = (reason & static_cast<u32>(BreakReason::NotificationOnlyFlag)) != 0;
664
665 bool has_dumped_buffer{};
666 std::vector<u8> debug_buffer;
667
668 const auto handle_debug_buffer = [&](VAddr addr, u64 sz) {
669 if (sz == 0 || addr == 0 || has_dumped_buffer) {
670 return;
671 }
672
673 auto& memory = system.Memory();
674
675 // This typically is an error code so we're going to assume this is the case
676 if (sz == sizeof(u32)) {
677 LOG_CRITICAL(Debug_Emulated, "debug_buffer_err_code={:X}", memory.Read32(addr));
678 } else {
679 // We don't know what's in here so we'll hexdump it
680 debug_buffer.resize(sz);
681 memory.ReadBlock(addr, debug_buffer.data(), sz);
682 std::string hexdump;
683 for (std::size_t i = 0; i < debug_buffer.size(); i++) {
684 hexdump += fmt::format("{:02X} ", debug_buffer[i]);
685 if (i != 0 && i % 16 == 0) {
686 hexdump += '\n';
687 }
688 }
689 LOG_CRITICAL(Debug_Emulated, "debug_buffer=\n{}", hexdump);
690 }
691 has_dumped_buffer = true;
692 };
693 switch (break_reason) {
694 case BreakReason::Panic:
695 LOG_CRITICAL(Debug_Emulated, "Userspace PANIC! info1=0x{:016X}, info2=0x{:016X}", info1,
696 info2);
697 handle_debug_buffer(info1, info2);
698 break;
699 case BreakReason::Assert:
700 LOG_CRITICAL(Debug_Emulated, "Userspace Assertion failed! info1=0x{:016X}, info2=0x{:016X}",
701 info1, info2);
702 handle_debug_buffer(info1, info2);
703 break;
704 case BreakReason::User:
705 LOG_WARNING(Debug_Emulated, "Userspace Break! 0x{:016X} with size 0x{:016X}", info1, info2);
706 handle_debug_buffer(info1, info2);
707 break;
708 case BreakReason::PreLoadDll:
709 LOG_INFO(Debug_Emulated,
710 "Userspace Attempting to load an NRO at 0x{:016X} with size 0x{:016X}", info1,
711 info2);
712 break;
713 case BreakReason::PostLoadDll:
714 LOG_INFO(Debug_Emulated, "Userspace Loaded an NRO at 0x{:016X} with size 0x{:016X}", info1,
715 info2);
716 break;
717 case BreakReason::PreUnloadDll:
718 LOG_INFO(Debug_Emulated,
719 "Userspace Attempting to unload an NRO at 0x{:016X} with size 0x{:016X}", info1,
720 info2);
721 break;
722 case BreakReason::PostUnloadDll:
723 LOG_INFO(Debug_Emulated, "Userspace Unloaded an NRO at 0x{:016X} with size 0x{:016X}",
724 info1, info2);
725 break;
726 case BreakReason::CppException:
727 LOG_CRITICAL(Debug_Emulated, "Signalling debugger. Uncaught C++ exception encountered.");
728 break;
729 default:
730 LOG_WARNING(
731 Debug_Emulated,
732 "Signalling debugger, Unknown break reason {:#X}, info1=0x{:016X}, info2=0x{:016X}",
733 reason, info1, info2);
734 handle_debug_buffer(info1, info2);
735 break;
736 }
737
738 system.GetReporter().SaveSvcBreakReport(reason, notification_only, info1, info2,
739 has_dumped_buffer ? std::make_optional(debug_buffer)
740 : std::nullopt);
741
742 if (!notification_only) {
743 LOG_CRITICAL(
744 Debug_Emulated,
745 "Emulated program broke execution! reason=0x{:016X}, info1=0x{:016X}, info2=0x{:016X}",
746 reason, info1, info2);
747
748 handle_debug_buffer(info1, info2);
749
750 auto* const current_thread = GetCurrentThreadPointer(system.Kernel());
751 const auto thread_processor_id = current_thread->GetActiveCore();
752 system.ArmInterface(static_cast<std::size_t>(thread_processor_id)).LogBacktrace();
753 }
754
755 if (system.DebuggerEnabled()) {
756 auto* thread = system.Kernel().GetCurrentEmuThread();
757 system.GetDebugger().NotifyThreadStopped(thread);
758 thread->RequestSuspend(Kernel::SuspendType::Debug);
759 }
760}
761
762static void Break32(Core::System& system, u32 reason, u32 info1, u32 info2) {
763 Break(system, reason, info1, info2);
764}
765
766/// Used to output a message on a debug hardware unit - does nothing on a retail unit
767static void OutputDebugString(Core::System& system, VAddr address, u64 len) {
768 if (len == 0) {
769 return;
770 }
771
772 std::string str(len, '\0');
773 system.Memory().ReadBlock(address, str.data(), str.size());
774 LOG_DEBUG(Debug_Emulated, "{}", str);
775}
776
777static void OutputDebugString32(Core::System& system, u32 address, u32 len) {
778 OutputDebugString(system, address, len);
779}
780
781/// Gets system/memory information for the current process
782static Result GetInfo(Core::System& system, u64* result, u64 info_id, Handle handle,
783 u64 info_sub_id) {
784 LOG_TRACE(Kernel_SVC, "called info_id=0x{:X}, info_sub_id=0x{:X}, handle=0x{:08X}", info_id,
785 info_sub_id, handle);
786
787 const auto info_id_type = static_cast<InfoType>(info_id);
788
789 switch (info_id_type) {
790 case InfoType::CoreMask:
791 case InfoType::PriorityMask:
792 case InfoType::AliasRegionAddress:
793 case InfoType::AliasRegionSize:
794 case InfoType::HeapRegionAddress:
795 case InfoType::HeapRegionSize:
796 case InfoType::AslrRegionAddress:
797 case InfoType::AslrRegionSize:
798 case InfoType::StackRegionAddress:
799 case InfoType::StackRegionSize:
800 case InfoType::TotalMemorySize:
801 case InfoType::UsedMemorySize:
802 case InfoType::SystemResourceSizeTotal:
803 case InfoType::SystemResourceSizeUsed:
804 case InfoType::ProgramId:
805 case InfoType::UserExceptionContextAddress:
806 case InfoType::TotalNonSystemMemorySize:
807 case InfoType::UsedNonSystemMemorySize:
808 case InfoType::IsApplication:
809 case InfoType::FreeThreadCount: {
810 if (info_sub_id != 0) {
811 LOG_ERROR(Kernel_SVC, "Info sub id is non zero! info_id={}, info_sub_id={}", info_id,
812 info_sub_id);
813 return ResultInvalidEnumValue;
814 }
815
816 const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable();
817 KScopedAutoObject process = handle_table.GetObject<KProcess>(handle);
818 if (process.IsNull()) {
819 LOG_ERROR(Kernel_SVC, "Process is not valid! info_id={}, info_sub_id={}, handle={:08X}",
820 info_id, info_sub_id, handle);
821 return ResultInvalidHandle;
822 }
823
824 switch (info_id_type) {
825 case InfoType::CoreMask:
826 *result = process->GetCoreMask();
827 return ResultSuccess;
828
829 case InfoType::PriorityMask:
830 *result = process->GetPriorityMask();
831 return ResultSuccess;
832
833 case InfoType::AliasRegionAddress:
834 *result = process->PageTable().GetAliasRegionStart();
835 return ResultSuccess;
836
837 case InfoType::AliasRegionSize:
838 *result = process->PageTable().GetAliasRegionSize();
839 return ResultSuccess;
840
841 case InfoType::HeapRegionAddress:
842 *result = process->PageTable().GetHeapRegionStart();
843 return ResultSuccess;
844
845 case InfoType::HeapRegionSize:
846 *result = process->PageTable().GetHeapRegionSize();
847 return ResultSuccess;
848
849 case InfoType::AslrRegionAddress:
850 *result = process->PageTable().GetAliasCodeRegionStart();
851 return ResultSuccess;
852
853 case InfoType::AslrRegionSize:
854 *result = process->PageTable().GetAliasCodeRegionSize();
855 return ResultSuccess;
856
857 case InfoType::StackRegionAddress:
858 *result = process->PageTable().GetStackRegionStart();
859 return ResultSuccess;
860
861 case InfoType::StackRegionSize:
862 *result = process->PageTable().GetStackRegionSize();
863 return ResultSuccess;
864
865 case InfoType::TotalMemorySize:
866 *result = process->GetTotalPhysicalMemoryAvailable();
867 return ResultSuccess;
868
869 case InfoType::UsedMemorySize:
870 *result = process->GetTotalPhysicalMemoryUsed();
871 return ResultSuccess;
872
873 case InfoType::SystemResourceSizeTotal:
874 *result = process->GetSystemResourceSize();
875 return ResultSuccess;
876
877 case InfoType::SystemResourceSizeUsed:
878 LOG_WARNING(Kernel_SVC, "(STUBBED) Attempted to query system resource usage");
879 *result = process->GetSystemResourceUsage();
880 return ResultSuccess;
881
882 case InfoType::ProgramId:
883 *result = process->GetProgramID();
884 return ResultSuccess;
885
886 case InfoType::UserExceptionContextAddress:
887 *result = process->GetProcessLocalRegionAddress();
888 return ResultSuccess;
889
890 case InfoType::TotalNonSystemMemorySize:
891 *result = process->GetTotalPhysicalMemoryAvailableWithoutSystemResource();
892 return ResultSuccess;
893
894 case InfoType::UsedNonSystemMemorySize:
895 *result = process->GetTotalPhysicalMemoryUsedWithoutSystemResource();
896 return ResultSuccess;
897
898 case InfoType::FreeThreadCount:
899 *result = process->GetFreeThreadCount();
900 return ResultSuccess;
901
902 default:
903 break;
904 }
905
906 LOG_ERROR(Kernel_SVC, "Unimplemented svcGetInfo id=0x{:016X}", info_id);
907 return ResultInvalidEnumValue;
908 }
909
910 case InfoType::DebuggerAttached:
911 *result = 0;
912 return ResultSuccess;
913
914 case InfoType::ResourceLimit: {
915 if (handle != 0) {
916 LOG_ERROR(Kernel, "Handle is non zero! handle={:08X}", handle);
917 return ResultInvalidHandle;
918 }
919
920 if (info_sub_id != 0) {
921 LOG_ERROR(Kernel, "Info sub id is non zero! info_id={}, info_sub_id={}", info_id,
922 info_sub_id);
923 return ResultInvalidCombination;
924 }
925
926 KProcess* const current_process = system.Kernel().CurrentProcess();
927 KHandleTable& handle_table = current_process->GetHandleTable();
928 const auto resource_limit = current_process->GetResourceLimit();
929 if (!resource_limit) {
930 *result = Svc::InvalidHandle;
931 // Yes, the kernel considers this a successful operation.
932 return ResultSuccess;
933 }
934
935 Handle resource_handle{};
936 R_TRY(handle_table.Add(&resource_handle, resource_limit));
937
938 *result = resource_handle;
939 return ResultSuccess;
940 }
941
942 case InfoType::RandomEntropy:
943 if (handle != 0) {
944 LOG_ERROR(Kernel_SVC, "Process Handle is non zero, expected 0 result but got {:016X}",
945 handle);
946 return ResultInvalidHandle;
947 }
948
949 if (info_sub_id >= KProcess::RANDOM_ENTROPY_SIZE) {
950 LOG_ERROR(Kernel_SVC, "Entropy size is out of range, expected {} but got {}",
951 KProcess::RANDOM_ENTROPY_SIZE, info_sub_id);
952 return ResultInvalidCombination;
953 }
954
955 *result = system.Kernel().CurrentProcess()->GetRandomEntropy(info_sub_id);
956 return ResultSuccess;
957
958 case InfoType::InitialProcessIdRange:
959 LOG_WARNING(Kernel_SVC,
960 "(STUBBED) Attempted to query privileged process id bounds, returned 0");
961 *result = 0;
962 return ResultSuccess;
963
964 case InfoType::ThreadTickCount: {
965 constexpr u64 num_cpus = 4;
966 if (info_sub_id != 0xFFFFFFFFFFFFFFFF && info_sub_id >= num_cpus) {
967 LOG_ERROR(Kernel_SVC, "Core count is out of range, expected {} but got {}", num_cpus,
968 info_sub_id);
969 return ResultInvalidCombination;
970 }
971
972 KScopedAutoObject thread =
973 system.Kernel().CurrentProcess()->GetHandleTable().GetObject<KThread>(
974 static_cast<Handle>(handle));
975 if (thread.IsNull()) {
976 LOG_ERROR(Kernel_SVC, "Thread handle does not exist, handle=0x{:08X}",
977 static_cast<Handle>(handle));
978 return ResultInvalidHandle;
979 }
980
981 const auto& core_timing = system.CoreTiming();
982 const auto& scheduler = *system.Kernel().CurrentScheduler();
983 const auto* const current_thread = GetCurrentThreadPointer(system.Kernel());
984 const bool same_thread = current_thread == thread.GetPointerUnsafe();
985
986 const u64 prev_ctx_ticks = scheduler.GetLastContextSwitchTime();
987 u64 out_ticks = 0;
988 if (same_thread && info_sub_id == 0xFFFFFFFFFFFFFFFF) {
989 const u64 thread_ticks = current_thread->GetCpuTime();
990
991 out_ticks = thread_ticks + (core_timing.GetCPUTicks() - prev_ctx_ticks);
992 } else if (same_thread && info_sub_id == system.Kernel().CurrentPhysicalCoreIndex()) {
993 out_ticks = core_timing.GetCPUTicks() - prev_ctx_ticks;
994 }
995
996 *result = out_ticks;
997 return ResultSuccess;
998 }
999 case InfoType::IdleTickCount: {
1000 // Verify the input handle is invalid.
1001 R_UNLESS(handle == InvalidHandle, ResultInvalidHandle);
1002
1003 // Verify the requested core is valid.
1004 const bool core_valid =
1005 (info_sub_id == 0xFFFFFFFFFFFFFFFF) ||
1006 (info_sub_id == static_cast<u64>(system.Kernel().CurrentPhysicalCoreIndex()));
1007 R_UNLESS(core_valid, ResultInvalidCombination);
1008
1009 // Get the idle tick count.
1010 *result = system.Kernel().CurrentScheduler()->GetIdleThread()->GetCpuTime();
1011 return ResultSuccess;
1012 }
1013 case InfoType::MesosphereCurrentProcess: {
1014 // Verify the input handle is invalid.
1015 R_UNLESS(handle == InvalidHandle, ResultInvalidHandle);
1016
1017 // Verify the sub-type is valid.
1018 R_UNLESS(info_sub_id == 0, ResultInvalidCombination);
1019
1020 // Get the handle table.
1021 KProcess* current_process = system.Kernel().CurrentProcess();
1022 KHandleTable& handle_table = current_process->GetHandleTable();
1023
1024 // Get a new handle for the current process.
1025 Handle tmp;
1026 R_TRY(handle_table.Add(&tmp, current_process));
1027
1028 // Set the output.
1029 *result = tmp;
1030
1031 // We succeeded.
1032 return ResultSuccess;
1033 }
1034 default:
1035 LOG_ERROR(Kernel_SVC, "Unimplemented svcGetInfo id=0x{:016X}", info_id);
1036 return ResultInvalidEnumValue;
1037 }
1038}
1039
1040static Result GetInfo32(Core::System& system, u32* result_low, u32* result_high, u32 sub_id_low,
1041 u32 info_id, u32 handle, u32 sub_id_high) {
1042 const u64 sub_id{u64{sub_id_low} | (u64{sub_id_high} << 32)};
1043 u64 res_value{};
1044
1045 const Result result{GetInfo(system, &res_value, info_id, handle, sub_id)};
1046 *result_high = static_cast<u32>(res_value >> 32);
1047 *result_low = static_cast<u32>(res_value & std::numeric_limits<u32>::max());
1048
1049 return result;
1050}
1051
1052/// Maps memory at a desired address
1053static Result MapPhysicalMemory(Core::System& system, VAddr addr, u64 size) {
1054 LOG_DEBUG(Kernel_SVC, "called, addr=0x{:016X}, size=0x{:X}", addr, size);
1055
1056 if (!Common::Is4KBAligned(addr)) {
1057 LOG_ERROR(Kernel_SVC, "Address is not aligned to 4KB, 0x{:016X}", addr);
1058 return ResultInvalidAddress;
1059 }
1060
1061 if (!Common::Is4KBAligned(size)) {
1062 LOG_ERROR(Kernel_SVC, "Size is not aligned to 4KB, 0x{:X}", size);
1063 return ResultInvalidSize;
1064 }
1065
1066 if (size == 0) {
1067 LOG_ERROR(Kernel_SVC, "Size is zero");
1068 return ResultInvalidSize;
1069 }
1070
1071 if (!(addr < addr + size)) {
1072 LOG_ERROR(Kernel_SVC, "Size causes 64-bit overflow of address");
1073 return ResultInvalidMemoryRegion;
1074 }
1075
1076 KProcess* const current_process{system.Kernel().CurrentProcess()};
1077 auto& page_table{current_process->PageTable()};
1078
1079 if (current_process->GetSystemResourceSize() == 0) {
1080 LOG_ERROR(Kernel_SVC, "System Resource Size is zero");
1081 return ResultInvalidState;
1082 }
1083
1084 if (!page_table.IsInsideAddressSpace(addr, size)) {
1085 LOG_ERROR(Kernel_SVC,
1086 "Address is not within the address space, addr=0x{:016X}, size=0x{:016X}", addr,
1087 size);
1088 return ResultInvalidMemoryRegion;
1089 }
1090
1091 if (page_table.IsOutsideAliasRegion(addr, size)) {
1092 LOG_ERROR(Kernel_SVC,
1093 "Address is not within the alias region, addr=0x{:016X}, size=0x{:016X}", addr,
1094 size);
1095 return ResultInvalidMemoryRegion;
1096 }
1097
1098 return page_table.MapPhysicalMemory(addr, size);
1099}
1100
1101static Result MapPhysicalMemory32(Core::System& system, u32 addr, u32 size) {
1102 return MapPhysicalMemory(system, addr, size);
1103}
1104
1105/// Unmaps memory previously mapped via MapPhysicalMemory
1106static Result UnmapPhysicalMemory(Core::System& system, VAddr addr, u64 size) {
1107 LOG_DEBUG(Kernel_SVC, "called, addr=0x{:016X}, size=0x{:X}", addr, size);
1108
1109 if (!Common::Is4KBAligned(addr)) {
1110 LOG_ERROR(Kernel_SVC, "Address is not aligned to 4KB, 0x{:016X}", addr);
1111 return ResultInvalidAddress;
1112 }
1113
1114 if (!Common::Is4KBAligned(size)) {
1115 LOG_ERROR(Kernel_SVC, "Size is not aligned to 4KB, 0x{:X}", size);
1116 return ResultInvalidSize;
1117 }
1118
1119 if (size == 0) {
1120 LOG_ERROR(Kernel_SVC, "Size is zero");
1121 return ResultInvalidSize;
1122 }
1123
1124 if (!(addr < addr + size)) {
1125 LOG_ERROR(Kernel_SVC, "Size causes 64-bit overflow of address");
1126 return ResultInvalidMemoryRegion;
1127 }
1128
1129 KProcess* const current_process{system.Kernel().CurrentProcess()};
1130 auto& page_table{current_process->PageTable()};
1131
1132 if (current_process->GetSystemResourceSize() == 0) {
1133 LOG_ERROR(Kernel_SVC, "System Resource Size is zero");
1134 return ResultInvalidState;
1135 }
1136
1137 if (!page_table.IsInsideAddressSpace(addr, size)) {
1138 LOG_ERROR(Kernel_SVC,
1139 "Address is not within the address space, addr=0x{:016X}, size=0x{:016X}", addr,
1140 size);
1141 return ResultInvalidMemoryRegion;
1142 }
1143
1144 if (page_table.IsOutsideAliasRegion(addr, size)) {
1145 LOG_ERROR(Kernel_SVC,
1146 "Address is not within the alias region, addr=0x{:016X}, size=0x{:016X}", addr,
1147 size);
1148 return ResultInvalidMemoryRegion;
1149 }
1150
1151 return page_table.UnmapPhysicalMemory(addr, size);
1152}
1153
1154static Result UnmapPhysicalMemory32(Core::System& system, u32 addr, u32 size) {
1155 return UnmapPhysicalMemory(system, addr, size);
1156}
1157
1158/// Sets the thread activity
1159static Result SetThreadActivity(Core::System& system, Handle thread_handle,
1160 ThreadActivity thread_activity) {
1161 LOG_DEBUG(Kernel_SVC, "called, handle=0x{:08X}, activity=0x{:08X}", thread_handle,
1162 thread_activity);
1163
1164 // Validate the activity.
1165 constexpr auto IsValidThreadActivity = [](ThreadActivity activity) {
1166 return activity == ThreadActivity::Runnable || activity == ThreadActivity::Paused;
1167 };
1168 R_UNLESS(IsValidThreadActivity(thread_activity), ResultInvalidEnumValue);
1169
1170 // Get the thread from its handle.
1171 KScopedAutoObject thread =
1172 system.Kernel().CurrentProcess()->GetHandleTable().GetObject<KThread>(thread_handle);
1173 R_UNLESS(thread.IsNotNull(), ResultInvalidHandle);
1174
1175 // Check that the activity is being set on a non-current thread for the current process.
1176 R_UNLESS(thread->GetOwnerProcess() == system.Kernel().CurrentProcess(), ResultInvalidHandle);
1177 R_UNLESS(thread.GetPointerUnsafe() != GetCurrentThreadPointer(system.Kernel()), ResultBusy);
1178
1179 // Set the activity.
1180 R_TRY(thread->SetActivity(thread_activity));
1181
1182 return ResultSuccess;
1183}
1184
1185static Result SetThreadActivity32(Core::System& system, Handle thread_handle,
1186 Svc::ThreadActivity thread_activity) {
1187 return SetThreadActivity(system, thread_handle, thread_activity);
1188}
1189
1190/// Gets the thread context
1191static Result GetThreadContext(Core::System& system, VAddr out_context, Handle thread_handle) {
1192 LOG_DEBUG(Kernel_SVC, "called, out_context=0x{:08X}, thread_handle=0x{:X}", out_context,
1193 thread_handle);
1194
1195 auto& kernel = system.Kernel();
1196
1197 // Get the thread from its handle.
1198 KScopedAutoObject thread =
1199 kernel.CurrentProcess()->GetHandleTable().GetObject<KThread>(thread_handle);
1200 R_UNLESS(thread.IsNotNull(), ResultInvalidHandle);
1201
1202 // Require the handle be to a non-current thread in the current process.
1203 const auto* current_process = kernel.CurrentProcess();
1204 R_UNLESS(current_process == thread->GetOwnerProcess(), ResultInvalidId);
1205
1206 // Verify that the thread isn't terminated.
1207 R_UNLESS(thread->GetState() != ThreadState::Terminated, ResultTerminationRequested);
1208
1209 /// Check that the thread is not the current one.
1210 /// NOTE: Nintendo does not check this, and thus the following loop will deadlock.
1211 R_UNLESS(thread.GetPointerUnsafe() != GetCurrentThreadPointer(kernel), ResultInvalidId);
1212
1213 // Try to get the thread context until the thread isn't current on any core.
1214 while (true) {
1215 KScopedSchedulerLock sl{kernel};
1216
1217 // TODO(bunnei): Enforce that thread is suspended for debug here.
1218
1219 // If the thread's raw state isn't runnable, check if it's current on some core.
1220 if (thread->GetRawState() != ThreadState::Runnable) {
1221 bool current = false;
1222 for (auto i = 0; i < static_cast<s32>(Core::Hardware::NUM_CPU_CORES); ++i) {
1223 if (thread.GetPointerUnsafe() == kernel.Scheduler(i).GetSchedulerCurrentThread()) {
1224 current = true;
1225 break;
1226 }
1227 }
1228
1229 // If the thread is current, retry until it isn't.
1230 if (current) {
1231 continue;
1232 }
1233 }
1234
1235 // Get the thread context.
1236 std::vector<u8> context;
1237 R_TRY(thread->GetThreadContext3(context));
1238
1239 // Copy the thread context to user space.
1240 system.Memory().WriteBlock(out_context, context.data(), context.size());
1241
1242 return ResultSuccess;
1243 }
1244
1245 return ResultSuccess;
1246}
1247
1248static Result GetThreadContext32(Core::System& system, u32 out_context, Handle thread_handle) {
1249 return GetThreadContext(system, out_context, thread_handle);
1250}
1251
1252/// Gets the priority for the specified thread
1253static Result GetThreadPriority(Core::System& system, u32* out_priority, Handle handle) {
1254 LOG_TRACE(Kernel_SVC, "called");
1255
1256 // Get the thread from its handle.
1257 KScopedAutoObject thread =
1258 system.Kernel().CurrentProcess()->GetHandleTable().GetObject<KThread>(handle);
1259 R_UNLESS(thread.IsNotNull(), ResultInvalidHandle);
1260
1261 // Get the thread's priority.
1262 *out_priority = thread->GetPriority();
1263 return ResultSuccess;
1264}
1265
1266static Result GetThreadPriority32(Core::System& system, u32* out_priority, Handle handle) {
1267 return GetThreadPriority(system, out_priority, handle);
1268}
1269
1270/// Sets the priority for the specified thread
1271static Result SetThreadPriority(Core::System& system, Handle thread_handle, u32 priority) {
1272 // Get the current process.
1273 KProcess& process = *system.Kernel().CurrentProcess();
1274
1275 // Validate the priority.
1276 R_UNLESS(HighestThreadPriority <= priority && priority <= LowestThreadPriority,
1277 ResultInvalidPriority);
1278 R_UNLESS(process.CheckThreadPriority(priority), ResultInvalidPriority);
1279
1280 // Get the thread from its handle.
1281 KScopedAutoObject thread = process.GetHandleTable().GetObject<KThread>(thread_handle);
1282 R_UNLESS(thread.IsNotNull(), ResultInvalidHandle);
1283
1284 // Set the thread priority.
1285 thread->SetBasePriority(priority);
1286 return ResultSuccess;
1287}
1288
1289static Result SetThreadPriority32(Core::System& system, Handle thread_handle, u32 priority) {
1290 return SetThreadPriority(system, thread_handle, priority);
1291}
1292
1293/// Get which CPU core is executing the current thread
1294static u32 GetCurrentProcessorNumber(Core::System& system) {
1295 LOG_TRACE(Kernel_SVC, "called");
1296 return static_cast<u32>(system.CurrentPhysicalCore().CoreIndex());
1297}
1298
1299static u32 GetCurrentProcessorNumber32(Core::System& system) {
1300 return GetCurrentProcessorNumber(system);
1301}
1302
1303namespace {
1304
1305constexpr bool IsValidSharedMemoryPermission(Svc::MemoryPermission perm) {
1306 switch (perm) {
1307 case Svc::MemoryPermission::Read:
1308 case Svc::MemoryPermission::ReadWrite:
1309 return true;
1310 default:
1311 return false;
1312 }
1313}
1314
1315[[maybe_unused]] constexpr bool IsValidRemoteSharedMemoryPermission(Svc::MemoryPermission perm) {
1316 return IsValidSharedMemoryPermission(perm) || perm == Svc::MemoryPermission::DontCare;
1317}
1318
1319constexpr bool IsValidProcessMemoryPermission(Svc::MemoryPermission perm) {
1320 switch (perm) {
1321 case Svc::MemoryPermission::None:
1322 case Svc::MemoryPermission::Read:
1323 case Svc::MemoryPermission::ReadWrite:
1324 case Svc::MemoryPermission::ReadExecute:
1325 return true;
1326 default:
1327 return false;
1328 }
1329}
1330
1331constexpr bool IsValidMapCodeMemoryPermission(Svc::MemoryPermission perm) {
1332 return perm == Svc::MemoryPermission::ReadWrite;
1333}
1334
1335constexpr bool IsValidMapToOwnerCodeMemoryPermission(Svc::MemoryPermission perm) {
1336 return perm == Svc::MemoryPermission::Read || perm == Svc::MemoryPermission::ReadExecute;
1337}
1338
1339constexpr bool IsValidUnmapCodeMemoryPermission(Svc::MemoryPermission perm) {
1340 return perm == Svc::MemoryPermission::None;
1341}
1342
1343constexpr bool IsValidUnmapFromOwnerCodeMemoryPermission(Svc::MemoryPermission perm) {
1344 return perm == Svc::MemoryPermission::None;
1345}
1346
1347} // Anonymous namespace
1348
1349static Result MapSharedMemory(Core::System& system, Handle shmem_handle, VAddr address, u64 size,
1350 Svc::MemoryPermission map_perm) {
1351 LOG_TRACE(Kernel_SVC,
1352 "called, shared_memory_handle=0x{:X}, addr=0x{:X}, size=0x{:X}, permissions=0x{:08X}",
1353 shmem_handle, address, size, map_perm);
1354
1355 // Validate the address/size.
1356 R_UNLESS(Common::IsAligned(address, PageSize), ResultInvalidAddress);
1357 R_UNLESS(Common::IsAligned(size, PageSize), ResultInvalidSize);
1358 R_UNLESS(size > 0, ResultInvalidSize);
1359 R_UNLESS((address < address + size), ResultInvalidCurrentMemory);
1360
1361 // Validate the permission.
1362 R_UNLESS(IsValidSharedMemoryPermission(map_perm), ResultInvalidNewMemoryPermission);
1363
1364 // Get the current process.
1365 auto& process = *system.Kernel().CurrentProcess();
1366 auto& page_table = process.PageTable();
1367
1368 // Get the shared memory.
1369 KScopedAutoObject shmem = process.GetHandleTable().GetObject<KSharedMemory>(shmem_handle);
1370 R_UNLESS(shmem.IsNotNull(), ResultInvalidHandle);
1371
1372 // Verify that the mapping is in range.
1373 R_UNLESS(page_table.CanContain(address, size, KMemoryState::Shared), ResultInvalidMemoryRegion);
1374
1375 // Add the shared memory to the process.
1376 R_TRY(process.AddSharedMemory(shmem.GetPointerUnsafe(), address, size));
1377
1378 // Ensure that we clean up the shared memory if we fail to map it.
1379 auto guard =
1380 SCOPE_GUARD({ process.RemoveSharedMemory(shmem.GetPointerUnsafe(), address, size); });
1381
1382 // Map the shared memory.
1383 R_TRY(shmem->Map(process, address, size, map_perm));
1384
1385 // We succeeded.
1386 guard.Cancel();
1387 return ResultSuccess;
1388}
1389
1390static Result MapSharedMemory32(Core::System& system, Handle shmem_handle, u32 address, u32 size,
1391 Svc::MemoryPermission map_perm) {
1392 return MapSharedMemory(system, shmem_handle, address, size, map_perm);
1393}
1394
1395static Result UnmapSharedMemory(Core::System& system, Handle shmem_handle, VAddr address,
1396 u64 size) {
1397 // Validate the address/size.
1398 R_UNLESS(Common::IsAligned(address, PageSize), ResultInvalidAddress);
1399 R_UNLESS(Common::IsAligned(size, PageSize), ResultInvalidSize);
1400 R_UNLESS(size > 0, ResultInvalidSize);
1401 R_UNLESS((address < address + size), ResultInvalidCurrentMemory);
1402
1403 // Get the current process.
1404 auto& process = *system.Kernel().CurrentProcess();
1405 auto& page_table = process.PageTable();
1406
1407 // Get the shared memory.
1408 KScopedAutoObject shmem = process.GetHandleTable().GetObject<KSharedMemory>(shmem_handle);
1409 R_UNLESS(shmem.IsNotNull(), ResultInvalidHandle);
1410
1411 // Verify that the mapping is in range.
1412 R_UNLESS(page_table.CanContain(address, size, KMemoryState::Shared), ResultInvalidMemoryRegion);
1413
1414 // Unmap the shared memory.
1415 R_TRY(shmem->Unmap(process, address, size));
1416
1417 // Remove the shared memory from the process.
1418 process.RemoveSharedMemory(shmem.GetPointerUnsafe(), address, size);
1419
1420 return ResultSuccess;
1421}
1422
1423static Result UnmapSharedMemory32(Core::System& system, Handle shmem_handle, u32 address,
1424 u32 size) {
1425 return UnmapSharedMemory(system, shmem_handle, address, size);
1426}
1427
1428static Result SetProcessMemoryPermission(Core::System& system, Handle process_handle, VAddr address,
1429 u64 size, Svc::MemoryPermission perm) {
1430 LOG_TRACE(Kernel_SVC,
1431 "called, process_handle=0x{:X}, addr=0x{:X}, size=0x{:X}, permissions=0x{:08X}",
1432 process_handle, address, size, perm);
1433
1434 // Validate the address/size.
1435 R_UNLESS(Common::IsAligned(address, PageSize), ResultInvalidAddress);
1436 R_UNLESS(Common::IsAligned(size, PageSize), ResultInvalidSize);
1437 R_UNLESS(size > 0, ResultInvalidSize);
1438 R_UNLESS((address < address + size), ResultInvalidCurrentMemory);
1439 R_UNLESS(address == static_cast<uintptr_t>(address), ResultInvalidCurrentMemory);
1440 R_UNLESS(size == static_cast<size_t>(size), ResultInvalidCurrentMemory);
1441
1442 // Validate the memory permission.
1443 R_UNLESS(IsValidProcessMemoryPermission(perm), ResultInvalidNewMemoryPermission);
1444
1445 // Get the process from its handle.
1446 KScopedAutoObject process =
1447 system.CurrentProcess()->GetHandleTable().GetObject<KProcess>(process_handle);
1448 R_UNLESS(process.IsNotNull(), ResultInvalidHandle);
1449
1450 // Validate that the address is in range.
1451 auto& page_table = process->PageTable();
1452 R_UNLESS(page_table.Contains(address, size), ResultInvalidCurrentMemory);
1453
1454 // Set the memory permission.
1455 return page_table.SetProcessMemoryPermission(address, size, perm);
1456}
1457
1458static Result MapProcessMemory(Core::System& system, VAddr dst_address, Handle process_handle,
1459 VAddr src_address, u64 size) {
1460 LOG_TRACE(Kernel_SVC,
1461 "called, dst_address=0x{:X}, process_handle=0x{:X}, src_address=0x{:X}, size=0x{:X}",
1462 dst_address, process_handle, src_address, size);
1463
1464 // Validate the address/size.
1465 R_UNLESS(Common::IsAligned(dst_address, PageSize), ResultInvalidAddress);
1466 R_UNLESS(Common::IsAligned(src_address, PageSize), ResultInvalidAddress);
1467 R_UNLESS(Common::IsAligned(size, PageSize), ResultInvalidSize);
1468 R_UNLESS(size > 0, ResultInvalidSize);
1469 R_UNLESS((dst_address < dst_address + size), ResultInvalidCurrentMemory);
1470 R_UNLESS((src_address < src_address + size), ResultInvalidCurrentMemory);
1471
1472 // Get the processes.
1473 KProcess* dst_process = system.CurrentProcess();
1474 KScopedAutoObject src_process =
1475 dst_process->GetHandleTable().GetObjectWithoutPseudoHandle<KProcess>(process_handle);
1476 R_UNLESS(src_process.IsNotNull(), ResultInvalidHandle);
1477
1478 // Get the page tables.
1479 auto& dst_pt = dst_process->PageTable();
1480 auto& src_pt = src_process->PageTable();
1481
1482 // Validate that the mapping is in range.
1483 R_UNLESS(src_pt.Contains(src_address, size), ResultInvalidCurrentMemory);
1484 R_UNLESS(dst_pt.CanContain(dst_address, size, KMemoryState::SharedCode),
1485 ResultInvalidMemoryRegion);
1486
1487 // Create a new page group.
1488 KPageGroup pg{system.Kernel(), dst_pt.GetBlockInfoManager()};
1489 R_TRY(src_pt.MakeAndOpenPageGroup(
1490 std::addressof(pg), src_address, size / PageSize, KMemoryState::FlagCanMapProcess,
1491 KMemoryState::FlagCanMapProcess, KMemoryPermission::None, KMemoryPermission::None,
1492 KMemoryAttribute::All, KMemoryAttribute::None));
1493
1494 // Map the group.
1495 R_TRY(dst_pt.MapPages(dst_address, pg, KMemoryState::SharedCode,
1496 KMemoryPermission::UserReadWrite));
1497
1498 return ResultSuccess;
1499}
1500
1501static Result UnmapProcessMemory(Core::System& system, VAddr dst_address, Handle process_handle,
1502 VAddr src_address, u64 size) {
1503 LOG_TRACE(Kernel_SVC,
1504 "called, dst_address=0x{:X}, process_handle=0x{:X}, src_address=0x{:X}, size=0x{:X}",
1505 dst_address, process_handle, src_address, size);
1506
1507 // Validate the address/size.
1508 R_UNLESS(Common::IsAligned(dst_address, PageSize), ResultInvalidAddress);
1509 R_UNLESS(Common::IsAligned(src_address, PageSize), ResultInvalidAddress);
1510 R_UNLESS(Common::IsAligned(size, PageSize), ResultInvalidSize);
1511 R_UNLESS(size > 0, ResultInvalidSize);
1512 R_UNLESS((dst_address < dst_address + size), ResultInvalidCurrentMemory);
1513 R_UNLESS((src_address < src_address + size), ResultInvalidCurrentMemory);
1514
1515 // Get the processes.
1516 KProcess* dst_process = system.CurrentProcess();
1517 KScopedAutoObject src_process =
1518 dst_process->GetHandleTable().GetObjectWithoutPseudoHandle<KProcess>(process_handle);
1519 R_UNLESS(src_process.IsNotNull(), ResultInvalidHandle);
1520
1521 // Get the page tables.
1522 auto& dst_pt = dst_process->PageTable();
1523 auto& src_pt = src_process->PageTable();
1524
1525 // Validate that the mapping is in range.
1526 R_UNLESS(src_pt.Contains(src_address, size), ResultInvalidCurrentMemory);
1527 R_UNLESS(dst_pt.CanContain(dst_address, size, KMemoryState::SharedCode),
1528 ResultInvalidMemoryRegion);
1529
1530 // Unmap the memory.
1531 R_TRY(dst_pt.UnmapProcessMemory(dst_address, size, src_pt, src_address));
1532
1533 return ResultSuccess;
1534}
1535
1536static Result CreateCodeMemory(Core::System& system, Handle* out, VAddr address, size_t size) {
1537 LOG_TRACE(Kernel_SVC, "called, address=0x{:X}, size=0x{:X}", address, size);
1538
1539 // Get kernel instance.
1540 auto& kernel = system.Kernel();
1541
1542 // Validate address / size.
1543 R_UNLESS(Common::IsAligned(address, PageSize), ResultInvalidAddress);
1544 R_UNLESS(Common::IsAligned(size, PageSize), ResultInvalidSize);
1545 R_UNLESS(size > 0, ResultInvalidSize);
1546 R_UNLESS((address < address + size), ResultInvalidCurrentMemory);
1547
1548 // Create the code memory.
1549
1550 KCodeMemory* code_mem = KCodeMemory::Create(kernel);
1551 R_UNLESS(code_mem != nullptr, ResultOutOfResource);
1552
1553 // Verify that the region is in range.
1554 R_UNLESS(system.CurrentProcess()->PageTable().Contains(address, size),
1555 ResultInvalidCurrentMemory);
1556
1557 // Initialize the code memory.
1558 R_TRY(code_mem->Initialize(system.DeviceMemory(), address, size));
1559
1560 // Register the code memory.
1561 KCodeMemory::Register(kernel, code_mem);
1562
1563 // Add the code memory to the handle table.
1564 R_TRY(system.CurrentProcess()->GetHandleTable().Add(out, code_mem));
1565
1566 code_mem->Close();
1567
1568 return ResultSuccess;
1569}
1570
1571static Result CreateCodeMemory32(Core::System& system, Handle* out, u32 address, u32 size) {
1572 return CreateCodeMemory(system, out, address, size);
1573}
1574
1575static Result ControlCodeMemory(Core::System& system, Handle code_memory_handle, u32 operation,
1576 VAddr address, size_t size, Svc::MemoryPermission perm) {
1577
1578 LOG_TRACE(Kernel_SVC,
1579 "called, code_memory_handle=0x{:X}, operation=0x{:X}, address=0x{:X}, size=0x{:X}, "
1580 "permission=0x{:X}",
1581 code_memory_handle, operation, address, size, perm);
1582
1583 // Validate the address / size.
1584 R_UNLESS(Common::IsAligned(address, PageSize), ResultInvalidAddress);
1585 R_UNLESS(Common::IsAligned(size, PageSize), ResultInvalidSize);
1586 R_UNLESS(size > 0, ResultInvalidSize);
1587 R_UNLESS((address < address + size), ResultInvalidCurrentMemory);
1588
1589 // Get the code memory from its handle.
1590 KScopedAutoObject code_mem =
1591 system.CurrentProcess()->GetHandleTable().GetObject<KCodeMemory>(code_memory_handle);
1592 R_UNLESS(code_mem.IsNotNull(), ResultInvalidHandle);
1593
1594 // NOTE: Here, Atmosphere extends the SVC to allow code memory operations on one's own process.
1595 // This enables homebrew usage of these SVCs for JIT.
1596
1597 // Perform the operation.
1598 switch (static_cast<CodeMemoryOperation>(operation)) {
1599 case CodeMemoryOperation::Map: {
1600 // Check that the region is in range.
1601 R_UNLESS(
1602 system.CurrentProcess()->PageTable().CanContain(address, size, KMemoryState::CodeOut),
1603 ResultInvalidMemoryRegion);
1604
1605 // Check the memory permission.
1606 R_UNLESS(IsValidMapCodeMemoryPermission(perm), ResultInvalidNewMemoryPermission);
1607
1608 // Map the memory.
1609 R_TRY(code_mem->Map(address, size));
1610 } break;
1611 case CodeMemoryOperation::Unmap: {
1612 // Check that the region is in range.
1613 R_UNLESS(
1614 system.CurrentProcess()->PageTable().CanContain(address, size, KMemoryState::CodeOut),
1615 ResultInvalidMemoryRegion);
1616
1617 // Check the memory permission.
1618 R_UNLESS(IsValidUnmapCodeMemoryPermission(perm), ResultInvalidNewMemoryPermission);
1619
1620 // Unmap the memory.
1621 R_TRY(code_mem->Unmap(address, size));
1622 } break;
1623 case CodeMemoryOperation::MapToOwner: {
1624 // Check that the region is in range.
1625 R_UNLESS(code_mem->GetOwner()->PageTable().CanContain(address, size,
1626 KMemoryState::GeneratedCode),
1627 ResultInvalidMemoryRegion);
1628
1629 // Check the memory permission.
1630 R_UNLESS(IsValidMapToOwnerCodeMemoryPermission(perm), ResultInvalidNewMemoryPermission);
1631
1632 // Map the memory to its owner.
1633 R_TRY(code_mem->MapToOwner(address, size, perm));
1634 } break;
1635 case CodeMemoryOperation::UnmapFromOwner: {
1636 // Check that the region is in range.
1637 R_UNLESS(code_mem->GetOwner()->PageTable().CanContain(address, size,
1638 KMemoryState::GeneratedCode),
1639 ResultInvalidMemoryRegion);
1640
1641 // Check the memory permission.
1642 R_UNLESS(IsValidUnmapFromOwnerCodeMemoryPermission(perm), ResultInvalidNewMemoryPermission);
1643
1644 // Unmap the memory from its owner.
1645 R_TRY(code_mem->UnmapFromOwner(address, size));
1646 } break;
1647 default:
1648 return ResultInvalidEnumValue;
1649 }
1650
1651 return ResultSuccess;
1652}
1653
1654static Result ControlCodeMemory32(Core::System& system, Handle code_memory_handle, u32 operation,
1655 u64 address, u64 size, Svc::MemoryPermission perm) {
1656 return ControlCodeMemory(system, code_memory_handle, operation, address, size, perm);
1657}
1658
1659static Result QueryProcessMemory(Core::System& system, VAddr memory_info_address,
1660 VAddr page_info_address, Handle process_handle, VAddr address) {
1661 LOG_TRACE(Kernel_SVC, "called process=0x{:08X} address={:X}", process_handle, address);
1662 const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable();
1663 KScopedAutoObject process = handle_table.GetObject<KProcess>(process_handle);
1664 if (process.IsNull()) {
1665 LOG_ERROR(Kernel_SVC, "Process handle does not exist, process_handle=0x{:08X}",
1666 process_handle);
1667 return ResultInvalidHandle;
1668 }
1669
1670 auto& memory{system.Memory()};
1671 const auto memory_info{process->PageTable().QueryInfo(address).GetSvcMemoryInfo()};
1672
1673 memory.Write64(memory_info_address + 0x00, memory_info.base_address);
1674 memory.Write64(memory_info_address + 0x08, memory_info.size);
1675 memory.Write32(memory_info_address + 0x10, static_cast<u32>(memory_info.state) & 0xff);
1676 memory.Write32(memory_info_address + 0x14, static_cast<u32>(memory_info.attribute));
1677 memory.Write32(memory_info_address + 0x18, static_cast<u32>(memory_info.permission));
1678 memory.Write32(memory_info_address + 0x1c, memory_info.ipc_count);
1679 memory.Write32(memory_info_address + 0x20, memory_info.device_count);
1680 memory.Write32(memory_info_address + 0x24, 0);
1681
1682 // Page info appears to be currently unused by the kernel and is always set to zero.
1683 memory.Write32(page_info_address, 0);
1684
1685 return ResultSuccess;
1686}
1687
1688static Result QueryMemory(Core::System& system, VAddr memory_info_address, VAddr page_info_address,
1689 VAddr query_address) {
1690 LOG_TRACE(Kernel_SVC,
1691 "called, memory_info_address=0x{:016X}, page_info_address=0x{:016X}, "
1692 "query_address=0x{:016X}",
1693 memory_info_address, page_info_address, query_address);
1694
1695 return QueryProcessMemory(system, memory_info_address, page_info_address, CurrentProcess,
1696 query_address);
1697}
1698
1699static Result QueryMemory32(Core::System& system, u32 memory_info_address, u32 page_info_address,
1700 u32 query_address) {
1701 return QueryMemory(system, memory_info_address, page_info_address, query_address);
1702}
1703
1704static Result MapProcessCodeMemory(Core::System& system, Handle process_handle, u64 dst_address,
1705 u64 src_address, u64 size) {
1706 LOG_DEBUG(Kernel_SVC,
1707 "called. process_handle=0x{:08X}, dst_address=0x{:016X}, "
1708 "src_address=0x{:016X}, size=0x{:016X}",
1709 process_handle, dst_address, src_address, size);
1710
1711 if (!Common::Is4KBAligned(src_address)) {
1712 LOG_ERROR(Kernel_SVC, "src_address is not page-aligned (src_address=0x{:016X}).",
1713 src_address);
1714 return ResultInvalidAddress;
1715 }
1716
1717 if (!Common::Is4KBAligned(dst_address)) {
1718 LOG_ERROR(Kernel_SVC, "dst_address is not page-aligned (dst_address=0x{:016X}).",
1719 dst_address);
1720 return ResultInvalidAddress;
1721 }
1722
1723 if (size == 0 || !Common::Is4KBAligned(size)) {
1724 LOG_ERROR(Kernel_SVC, "Size is zero or not page-aligned (size=0x{:016X})", size);
1725 return ResultInvalidSize;
1726 }
1727
1728 if (!IsValidAddressRange(dst_address, size)) {
1729 LOG_ERROR(Kernel_SVC,
1730 "Destination address range overflows the address space (dst_address=0x{:016X}, "
1731 "size=0x{:016X}).",
1732 dst_address, size);
1733 return ResultInvalidCurrentMemory;
1734 }
1735
1736 if (!IsValidAddressRange(src_address, size)) {
1737 LOG_ERROR(Kernel_SVC,
1738 "Source address range overflows the address space (src_address=0x{:016X}, "
1739 "size=0x{:016X}).",
1740 src_address, size);
1741 return ResultInvalidCurrentMemory;
1742 }
1743
1744 const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable();
1745 KScopedAutoObject process = handle_table.GetObject<KProcess>(process_handle);
1746 if (process.IsNull()) {
1747 LOG_ERROR(Kernel_SVC, "Invalid process handle specified (handle=0x{:08X}).",
1748 process_handle);
1749 return ResultInvalidHandle;
1750 }
1751
1752 auto& page_table = process->PageTable();
1753 if (!page_table.IsInsideAddressSpace(src_address, size)) {
1754 LOG_ERROR(Kernel_SVC,
1755 "Source address range is not within the address space (src_address=0x{:016X}, "
1756 "size=0x{:016X}).",
1757 src_address, size);
1758 return ResultInvalidCurrentMemory;
1759 }
1760
1761 if (!page_table.IsInsideASLRRegion(dst_address, size)) {
1762 LOG_ERROR(Kernel_SVC,
1763 "Destination address range is not within the ASLR region (dst_address=0x{:016X}, "
1764 "size=0x{:016X}).",
1765 dst_address, size);
1766 return ResultInvalidMemoryRegion;
1767 }
1768
1769 return page_table.MapCodeMemory(dst_address, src_address, size);
1770}
1771
1772static Result UnmapProcessCodeMemory(Core::System& system, Handle process_handle, u64 dst_address,
1773 u64 src_address, u64 size) {
1774 LOG_DEBUG(Kernel_SVC,
1775 "called. process_handle=0x{:08X}, dst_address=0x{:016X}, src_address=0x{:016X}, "
1776 "size=0x{:016X}",
1777 process_handle, dst_address, src_address, size);
1778
1779 if (!Common::Is4KBAligned(dst_address)) {
1780 LOG_ERROR(Kernel_SVC, "dst_address is not page-aligned (dst_address=0x{:016X}).",
1781 dst_address);
1782 return ResultInvalidAddress;
1783 }
1784
1785 if (!Common::Is4KBAligned(src_address)) {
1786 LOG_ERROR(Kernel_SVC, "src_address is not page-aligned (src_address=0x{:016X}).",
1787 src_address);
1788 return ResultInvalidAddress;
1789 }
1790
1791 if (size == 0 || !Common::Is4KBAligned(size)) {
1792 LOG_ERROR(Kernel_SVC, "Size is zero or not page-aligned (size=0x{:016X}).", size);
1793 return ResultInvalidSize;
1794 }
1795
1796 if (!IsValidAddressRange(dst_address, size)) {
1797 LOG_ERROR(Kernel_SVC,
1798 "Destination address range overflows the address space (dst_address=0x{:016X}, "
1799 "size=0x{:016X}).",
1800 dst_address, size);
1801 return ResultInvalidCurrentMemory;
1802 }
1803
1804 if (!IsValidAddressRange(src_address, size)) {
1805 LOG_ERROR(Kernel_SVC,
1806 "Source address range overflows the address space (src_address=0x{:016X}, "
1807 "size=0x{:016X}).",
1808 src_address, size);
1809 return ResultInvalidCurrentMemory;
1810 }
1811
1812 const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable();
1813 KScopedAutoObject process = handle_table.GetObject<KProcess>(process_handle);
1814 if (process.IsNull()) {
1815 LOG_ERROR(Kernel_SVC, "Invalid process handle specified (handle=0x{:08X}).",
1816 process_handle);
1817 return ResultInvalidHandle;
1818 }
1819
1820 auto& page_table = process->PageTable();
1821 if (!page_table.IsInsideAddressSpace(src_address, size)) {
1822 LOG_ERROR(Kernel_SVC,
1823 "Source address range is not within the address space (src_address=0x{:016X}, "
1824 "size=0x{:016X}).",
1825 src_address, size);
1826 return ResultInvalidCurrentMemory;
1827 }
1828
1829 if (!page_table.IsInsideASLRRegion(dst_address, size)) {
1830 LOG_ERROR(Kernel_SVC,
1831 "Destination address range is not within the ASLR region (dst_address=0x{:016X}, "
1832 "size=0x{:016X}).",
1833 dst_address, size);
1834 return ResultInvalidMemoryRegion;
1835 }
1836
1837 return page_table.UnmapCodeMemory(dst_address, src_address, size,
1838 KPageTable::ICacheInvalidationStrategy::InvalidateAll);
1839}
1840
1841/// Exits the current process
1842static void ExitProcess(Core::System& system) {
1843 auto* current_process = system.Kernel().CurrentProcess();
1844
1845 LOG_INFO(Kernel_SVC, "Process {} exiting", current_process->GetProcessID());
1846 ASSERT_MSG(current_process->GetState() == KProcess::State::Running,
1847 "Process has already exited");
1848
1849 system.Exit();
1850}
1851
1852static void ExitProcess32(Core::System& system) {
1853 ExitProcess(system);
1854}
1855
1856namespace {
1857
1858constexpr bool IsValidVirtualCoreId(int32_t core_id) {
1859 return (0 <= core_id && core_id < static_cast<int32_t>(Core::Hardware::NUM_CPU_CORES));
1860}
1861
1862} // Anonymous namespace
1863
1864/// Creates a new thread
1865static Result CreateThread(Core::System& system, Handle* out_handle, VAddr entry_point, u64 arg,
1866 VAddr stack_bottom, u32 priority, s32 core_id) {
1867 LOG_DEBUG(Kernel_SVC,
1868 "called entry_point=0x{:08X}, arg=0x{:08X}, stack_bottom=0x{:08X}, "
1869 "priority=0x{:08X}, core_id=0x{:08X}",
1870 entry_point, arg, stack_bottom, priority, core_id);
1871
1872 // Adjust core id, if it's the default magic.
1873 auto& kernel = system.Kernel();
1874 auto& process = *kernel.CurrentProcess();
1875 if (core_id == IdealCoreUseProcessValue) {
1876 core_id = process.GetIdealCoreId();
1877 }
1878
1879 // Validate arguments.
1880 if (!IsValidVirtualCoreId(core_id)) {
1881 LOG_ERROR(Kernel_SVC, "Invalid Core ID specified (id={})", core_id);
1882 return ResultInvalidCoreId;
1883 }
1884 if (((1ULL << core_id) & process.GetCoreMask()) == 0) {
1885 LOG_ERROR(Kernel_SVC, "Core ID doesn't fall within allowable cores (id={})", core_id);
1886 return ResultInvalidCoreId;
1887 }
1888
1889 if (HighestThreadPriority > priority || priority > LowestThreadPriority) {
1890 LOG_ERROR(Kernel_SVC, "Invalid priority specified (priority={})", priority);
1891 return ResultInvalidPriority;
1892 }
1893 if (!process.CheckThreadPriority(priority)) {
1894 LOG_ERROR(Kernel_SVC, "Invalid allowable thread priority (priority={})", priority);
1895 return ResultInvalidPriority;
1896 }
1897
1898 // Reserve a new thread from the process resource limit (waiting up to 100ms).
1899 KScopedResourceReservation thread_reservation(
1900 kernel.CurrentProcess(), LimitableResource::ThreadCountMax, 1,
1901 system.CoreTiming().GetGlobalTimeNs().count() + 100000000);
1902 if (!thread_reservation.Succeeded()) {
1903 LOG_ERROR(Kernel_SVC, "Could not reserve a new thread");
1904 return ResultLimitReached;
1905 }
1906
1907 // Create the thread.
1908 KThread* thread = KThread::Create(kernel);
1909 if (!thread) {
1910 LOG_ERROR(Kernel_SVC, "Unable to create new threads. Thread creation limit reached.");
1911 return ResultOutOfResource;
1912 }
1913 SCOPE_EXIT({ thread->Close(); });
1914
1915 // Initialize the thread.
1916 {
1917 KScopedLightLock lk{process.GetStateLock()};
1918 R_TRY(KThread::InitializeUserThread(system, thread, entry_point, arg, stack_bottom,
1919 priority, core_id, &process));
1920 }
1921
1922 // Set the thread name for debugging purposes.
1923 thread->SetName(fmt::format("thread[entry_point={:X}, handle={:X}]", entry_point, *out_handle));
1924
1925 // Commit the thread reservation.
1926 thread_reservation.Commit();
1927
1928 // Register the new thread.
1929 KThread::Register(kernel, thread);
1930
1931 // Add the thread to the handle table.
1932 R_TRY(process.GetHandleTable().Add(out_handle, thread));
1933
1934 return ResultSuccess;
1935}
1936
1937static Result CreateThread32(Core::System& system, Handle* out_handle, u32 priority,
1938 u32 entry_point, u32 arg, u32 stack_top, s32 processor_id) {
1939 return CreateThread(system, out_handle, entry_point, arg, stack_top, priority, processor_id);
1940}
1941
1942/// Starts the thread for the provided handle
1943static Result StartThread(Core::System& system, Handle thread_handle) {
1944 LOG_DEBUG(Kernel_SVC, "called thread=0x{:08X}", thread_handle);
1945
1946 // Get the thread from its handle.
1947 KScopedAutoObject thread =
1948 system.Kernel().CurrentProcess()->GetHandleTable().GetObject<KThread>(thread_handle);
1949 R_UNLESS(thread.IsNotNull(), ResultInvalidHandle);
1950
1951 // Try to start the thread.
1952 R_TRY(thread->Run());
1953
1954 // If we succeeded, persist a reference to the thread.
1955 thread->Open();
1956 system.Kernel().RegisterInUseObject(thread.GetPointerUnsafe());
1957
1958 return ResultSuccess;
1959}
1960
1961static Result StartThread32(Core::System& system, Handle thread_handle) {
1962 return StartThread(system, thread_handle);
1963}
1964
1965/// Called when a thread exits
1966static void ExitThread(Core::System& system) {
1967 LOG_DEBUG(Kernel_SVC, "called, pc=0x{:08X}", system.CurrentArmInterface().GetPC());
1968
1969 auto* const current_thread = GetCurrentThreadPointer(system.Kernel());
1970 system.GlobalSchedulerContext().RemoveThread(current_thread);
1971 current_thread->Exit();
1972 system.Kernel().UnregisterInUseObject(current_thread);
1973}
1974
1975static void ExitThread32(Core::System& system) {
1976 ExitThread(system);
1977}
1978
1979/// Sleep the current thread
1980static void SleepThread(Core::System& system, s64 nanoseconds) {
1981 auto& kernel = system.Kernel();
1982 const auto yield_type = static_cast<Svc::YieldType>(nanoseconds);
1983
1984 LOG_TRACE(Kernel_SVC, "called nanoseconds={}", nanoseconds);
1985
1986 // When the input tick is positive, sleep.
1987 if (nanoseconds > 0) {
1988 // Convert the timeout from nanoseconds to ticks.
1989 // NOTE: Nintendo does not use this conversion logic in WaitSynchronization...
1990
1991 // Sleep.
1992 // NOTE: Nintendo does not check the result of this sleep.
1993 static_cast<void>(GetCurrentThread(kernel).Sleep(nanoseconds));
1994 } else if (yield_type == Svc::YieldType::WithoutCoreMigration) {
1995 KScheduler::YieldWithoutCoreMigration(kernel);
1996 } else if (yield_type == Svc::YieldType::WithCoreMigration) {
1997 KScheduler::YieldWithCoreMigration(kernel);
1998 } else if (yield_type == Svc::YieldType::ToAnyThread) {
1999 KScheduler::YieldToAnyThread(kernel);
2000 } else {
2001 // Nintendo does nothing at all if an otherwise invalid value is passed.
2002 ASSERT_MSG(false, "Unimplemented sleep yield type '{:016X}'!", nanoseconds);
2003 }
2004}
2005
2006static void SleepThread32(Core::System& system, u32 nanoseconds_low, u32 nanoseconds_high) {
2007 const auto nanoseconds = static_cast<s64>(u64{nanoseconds_low} | (u64{nanoseconds_high} << 32));
2008 SleepThread(system, nanoseconds);
2009}
2010
2011/// Wait process wide key atomic
2012static Result WaitProcessWideKeyAtomic(Core::System& system, VAddr address, VAddr cv_key, u32 tag,
2013 s64 timeout_ns) {
2014 LOG_TRACE(Kernel_SVC, "called address={:X}, cv_key={:X}, tag=0x{:08X}, timeout_ns={}", address,
2015 cv_key, tag, timeout_ns);
2016
2017 // Validate input.
2018 if (IsKernelAddress(address)) {
2019 LOG_ERROR(Kernel_SVC, "Attempted to wait on kernel address (address={:08X})", address);
2020 return ResultInvalidCurrentMemory;
2021 }
2022 if (!Common::IsAligned(address, sizeof(s32))) {
2023 LOG_ERROR(Kernel_SVC, "Address must be 4 byte aligned (address={:08X})", address);
2024 return ResultInvalidAddress;
2025 }
2026
2027 // Convert timeout from nanoseconds to ticks.
2028 s64 timeout{};
2029 if (timeout_ns > 0) {
2030 const s64 offset_tick(timeout_ns);
2031 if (offset_tick > 0) {
2032 timeout = offset_tick + 2;
2033 if (timeout <= 0) {
2034 timeout = std::numeric_limits<s64>::max();
2035 }
2036 } else {
2037 timeout = std::numeric_limits<s64>::max();
2038 }
2039 } else {
2040 timeout = timeout_ns;
2041 }
2042
2043 // Wait on the condition variable.
2044 return system.Kernel().CurrentProcess()->WaitConditionVariable(
2045 address, Common::AlignDown(cv_key, sizeof(u32)), tag, timeout);
2046}
2047
2048static Result WaitProcessWideKeyAtomic32(Core::System& system, u32 address, u32 cv_key, u32 tag,
2049 u32 timeout_ns_low, u32 timeout_ns_high) {
2050 const auto timeout_ns = static_cast<s64>(timeout_ns_low | (u64{timeout_ns_high} << 32));
2051 return WaitProcessWideKeyAtomic(system, address, cv_key, tag, timeout_ns);
2052}
2053
2054/// Signal process wide key
2055static void SignalProcessWideKey(Core::System& system, VAddr cv_key, s32 count) {
2056 LOG_TRACE(Kernel_SVC, "called, cv_key=0x{:X}, count=0x{:08X}", cv_key, count);
2057
2058 // Signal the condition variable.
2059 return system.Kernel().CurrentProcess()->SignalConditionVariable(
2060 Common::AlignDown(cv_key, sizeof(u32)), count);
2061}
2062
2063static void SignalProcessWideKey32(Core::System& system, u32 cv_key, s32 count) {
2064 SignalProcessWideKey(system, cv_key, count);
2065}
2066
2067namespace {
2068
2069constexpr bool IsValidSignalType(Svc::SignalType type) {
2070 switch (type) {
2071 case Svc::SignalType::Signal:
2072 case Svc::SignalType::SignalAndIncrementIfEqual:
2073 case Svc::SignalType::SignalAndModifyByWaitingCountIfEqual:
2074 return true;
2075 default:
2076 return false;
2077 }
2078}
2079
2080constexpr bool IsValidArbitrationType(Svc::ArbitrationType type) {
2081 switch (type) {
2082 case Svc::ArbitrationType::WaitIfLessThan:
2083 case Svc::ArbitrationType::DecrementAndWaitIfLessThan:
2084 case Svc::ArbitrationType::WaitIfEqual:
2085 return true;
2086 default:
2087 return false;
2088 }
2089}
2090
2091} // namespace
2092
2093// Wait for an address (via Address Arbiter)
2094static Result WaitForAddress(Core::System& system, VAddr address, Svc::ArbitrationType arb_type,
2095 s32 value, s64 timeout_ns) {
2096 LOG_TRACE(Kernel_SVC, "called, address=0x{:X}, arb_type=0x{:X}, value=0x{:X}, timeout_ns={}",
2097 address, arb_type, value, timeout_ns);
2098
2099 // Validate input.
2100 if (IsKernelAddress(address)) {
2101 LOG_ERROR(Kernel_SVC, "Attempting to wait on kernel address (address={:08X})", address);
2102 return ResultInvalidCurrentMemory;
2103 }
2104 if (!Common::IsAligned(address, sizeof(s32))) {
2105 LOG_ERROR(Kernel_SVC, "Wait address must be 4 byte aligned (address={:08X})", address);
2106 return ResultInvalidAddress;
2107 }
2108 if (!IsValidArbitrationType(arb_type)) {
2109 LOG_ERROR(Kernel_SVC, "Invalid arbitration type specified (type={})", arb_type);
2110 return ResultInvalidEnumValue;
2111 }
2112
2113 // Convert timeout from nanoseconds to ticks.
2114 s64 timeout{};
2115 if (timeout_ns > 0) {
2116 const s64 offset_tick(timeout_ns);
2117 if (offset_tick > 0) {
2118 timeout = offset_tick + 2;
2119 if (timeout <= 0) {
2120 timeout = std::numeric_limits<s64>::max();
2121 }
2122 } else {
2123 timeout = std::numeric_limits<s64>::max();
2124 }
2125 } else {
2126 timeout = timeout_ns;
2127 }
2128
2129 return system.Kernel().CurrentProcess()->WaitAddressArbiter(address, arb_type, value, timeout);
2130}
2131
2132static Result WaitForAddress32(Core::System& system, u32 address, Svc::ArbitrationType arb_type,
2133 s32 value, u32 timeout_ns_low, u32 timeout_ns_high) {
2134 const auto timeout = static_cast<s64>(timeout_ns_low | (u64{timeout_ns_high} << 32));
2135 return WaitForAddress(system, address, arb_type, value, timeout);
2136}
2137
2138// Signals to an address (via Address Arbiter)
2139static Result SignalToAddress(Core::System& system, VAddr address, Svc::SignalType signal_type,
2140 s32 value, s32 count) {
2141 LOG_TRACE(Kernel_SVC, "called, address=0x{:X}, signal_type=0x{:X}, value=0x{:X}, count=0x{:X}",
2142 address, signal_type, value, count);
2143
2144 // Validate input.
2145 if (IsKernelAddress(address)) {
2146 LOG_ERROR(Kernel_SVC, "Attempting to signal to a kernel address (address={:08X})", address);
2147 return ResultInvalidCurrentMemory;
2148 }
2149 if (!Common::IsAligned(address, sizeof(s32))) {
2150 LOG_ERROR(Kernel_SVC, "Signaled address must be 4 byte aligned (address={:08X})", address);
2151 return ResultInvalidAddress;
2152 }
2153 if (!IsValidSignalType(signal_type)) {
2154 LOG_ERROR(Kernel_SVC, "Invalid signal type specified (type={})", signal_type);
2155 return ResultInvalidEnumValue;
2156 }
2157
2158 return system.Kernel().CurrentProcess()->SignalAddressArbiter(address, signal_type, value,
2159 count);
2160}
2161
2162static void SynchronizePreemptionState(Core::System& system) {
2163 auto& kernel = system.Kernel();
2164
2165 // Lock the scheduler.
2166 KScopedSchedulerLock sl{kernel};
2167
2168 // If the current thread is pinned, unpin it.
2169 KProcess* cur_process = system.Kernel().CurrentProcess();
2170 const auto core_id = GetCurrentCoreId(kernel);
2171
2172 if (cur_process->GetPinnedThread(core_id) == GetCurrentThreadPointer(kernel)) {
2173 // Clear the current thread's interrupt flag.
2174 GetCurrentThread(kernel).ClearInterruptFlag();
2175
2176 // Unpin the current thread.
2177 cur_process->UnpinCurrentThread(core_id);
2178 }
2179}
2180
2181static Result SignalToAddress32(Core::System& system, u32 address, Svc::SignalType signal_type,
2182 s32 value, s32 count) {
2183 return SignalToAddress(system, address, signal_type, value, count);
2184}
2185
2186static void KernelDebug([[maybe_unused]] Core::System& system,
2187 [[maybe_unused]] u32 kernel_debug_type, [[maybe_unused]] u64 param1,
2188 [[maybe_unused]] u64 param2, [[maybe_unused]] u64 param3) {
2189 // Intentionally do nothing, as this does nothing in released kernel binaries.
2190}
2191
2192static void ChangeKernelTraceState([[maybe_unused]] Core::System& system,
2193 [[maybe_unused]] u32 trace_state) {
2194 // Intentionally do nothing, as this does nothing in released kernel binaries.
2195}
2196
2197/// This returns the total CPU ticks elapsed since the CPU was powered-on
2198static u64 GetSystemTick(Core::System& system) {
2199 LOG_TRACE(Kernel_SVC, "called");
2200
2201 auto& core_timing = system.CoreTiming();
2202
2203 // Returns the value of cntpct_el0 (https://switchbrew.org/wiki/SVC#svcGetSystemTick)
2204 const u64 result{core_timing.GetClockTicks()};
2205
2206 if (!system.Kernel().IsMulticore()) {
2207 core_timing.AddTicks(400U);
2208 }
2209
2210 return result;
2211}
2212
2213static void GetSystemTick32(Core::System& system, u32* time_low, u32* time_high) {
2214 const auto time = GetSystemTick(system);
2215 *time_low = static_cast<u32>(time);
2216 *time_high = static_cast<u32>(time >> 32);
2217}
2218
2219/// Close a handle
2220static Result CloseHandle(Core::System& system, Handle handle) {
2221 LOG_TRACE(Kernel_SVC, "Closing handle 0x{:08X}", handle);
2222
2223 // Remove the handle.
2224 R_UNLESS(system.Kernel().CurrentProcess()->GetHandleTable().Remove(handle),
2225 ResultInvalidHandle);
2226
2227 return ResultSuccess;
2228}
2229
2230static Result CloseHandle32(Core::System& system, Handle handle) {
2231 return CloseHandle(system, handle);
2232}
2233
2234/// Clears the signaled state of an event or process.
2235static Result ResetSignal(Core::System& system, Handle handle) {
2236 LOG_DEBUG(Kernel_SVC, "called handle 0x{:08X}", handle);
2237
2238 // Get the current handle table.
2239 const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable();
2240
2241 // Try to reset as readable event.
2242 {
2243 KScopedAutoObject readable_event = handle_table.GetObject<KReadableEvent>(handle);
2244 if (readable_event.IsNotNull()) {
2245 return readable_event->Reset();
2246 }
2247 }
2248
2249 // Try to reset as process.
2250 {
2251 KScopedAutoObject process = handle_table.GetObject<KProcess>(handle);
2252 if (process.IsNotNull()) {
2253 return process->Reset();
2254 }
2255 }
2256
2257 LOG_ERROR(Kernel_SVC, "invalid handle (0x{:08X})", handle);
2258
2259 return ResultInvalidHandle;
2260}
2261
2262static Result ResetSignal32(Core::System& system, Handle handle) {
2263 return ResetSignal(system, handle);
2264}
2265
2266namespace {
2267
2268constexpr bool IsValidTransferMemoryPermission(MemoryPermission perm) {
2269 switch (perm) {
2270 case MemoryPermission::None:
2271 case MemoryPermission::Read:
2272 case MemoryPermission::ReadWrite:
2273 return true;
2274 default:
2275 return false;
2276 }
2277}
2278
2279} // Anonymous namespace
2280
2281/// Creates a TransferMemory object
2282static Result CreateTransferMemory(Core::System& system, Handle* out, VAddr address, u64 size,
2283 MemoryPermission map_perm) {
2284 auto& kernel = system.Kernel();
2285
2286 // Validate the size.
2287 R_UNLESS(Common::IsAligned(address, PageSize), ResultInvalidAddress);
2288 R_UNLESS(Common::IsAligned(size, PageSize), ResultInvalidSize);
2289 R_UNLESS(size > 0, ResultInvalidSize);
2290 R_UNLESS((address < address + size), ResultInvalidCurrentMemory);
2291
2292 // Validate the permissions.
2293 R_UNLESS(IsValidTransferMemoryPermission(map_perm), ResultInvalidNewMemoryPermission);
2294
2295 // Get the current process and handle table.
2296 auto& process = *kernel.CurrentProcess();
2297 auto& handle_table = process.GetHandleTable();
2298
2299 // Reserve a new transfer memory from the process resource limit.
2300 KScopedResourceReservation trmem_reservation(kernel.CurrentProcess(),
2301 LimitableResource::TransferMemoryCountMax);
2302 R_UNLESS(trmem_reservation.Succeeded(), ResultLimitReached);
2303
2304 // Create the transfer memory.
2305 KTransferMemory* trmem = KTransferMemory::Create(kernel);
2306 R_UNLESS(trmem != nullptr, ResultOutOfResource);
2307
2308 // Ensure the only reference is in the handle table when we're done.
2309 SCOPE_EXIT({ trmem->Close(); });
2310
2311 // Ensure that the region is in range.
2312 R_UNLESS(process.PageTable().Contains(address, size), ResultInvalidCurrentMemory);
2313
2314 // Initialize the transfer memory.
2315 R_TRY(trmem->Initialize(address, size, map_perm));
2316
2317 // Commit the reservation.
2318 trmem_reservation.Commit();
2319
2320 // Register the transfer memory.
2321 KTransferMemory::Register(kernel, trmem);
2322
2323 // Add the transfer memory to the handle table.
2324 R_TRY(handle_table.Add(out, trmem));
2325
2326 return ResultSuccess;
2327}
2328
2329static Result CreateTransferMemory32(Core::System& system, Handle* out, u32 address, u32 size,
2330 MemoryPermission map_perm) {
2331 return CreateTransferMemory(system, out, address, size, map_perm);
2332}
2333
2334static Result GetThreadCoreMask(Core::System& system, Handle thread_handle, s32* out_core_id,
2335 u64* out_affinity_mask) {
2336 LOG_TRACE(Kernel_SVC, "called, handle=0x{:08X}", thread_handle);
2337
2338 // Get the thread from its handle.
2339 KScopedAutoObject thread =
2340 system.Kernel().CurrentProcess()->GetHandleTable().GetObject<KThread>(thread_handle);
2341 R_UNLESS(thread.IsNotNull(), ResultInvalidHandle);
2342
2343 // Get the core mask.
2344 R_TRY(thread->GetCoreMask(out_core_id, out_affinity_mask));
2345
2346 return ResultSuccess;
2347}
2348
2349static Result GetThreadCoreMask32(Core::System& system, Handle thread_handle, s32* out_core_id,
2350 u32* out_affinity_mask_low, u32* out_affinity_mask_high) {
2351 u64 out_affinity_mask{};
2352 const auto result = GetThreadCoreMask(system, thread_handle, out_core_id, &out_affinity_mask);
2353 *out_affinity_mask_high = static_cast<u32>(out_affinity_mask >> 32);
2354 *out_affinity_mask_low = static_cast<u32>(out_affinity_mask);
2355 return result;
2356}
2357
2358static Result SetThreadCoreMask(Core::System& system, Handle thread_handle, s32 core_id,
2359 u64 affinity_mask) {
2360 // Determine the core id/affinity mask.
2361 if (core_id == IdealCoreUseProcessValue) {
2362 core_id = system.Kernel().CurrentProcess()->GetIdealCoreId();
2363 affinity_mask = (1ULL << core_id);
2364 } else {
2365 // Validate the affinity mask.
2366 const u64 process_core_mask = system.Kernel().CurrentProcess()->GetCoreMask();
2367 R_UNLESS((affinity_mask | process_core_mask) == process_core_mask, ResultInvalidCoreId);
2368 R_UNLESS(affinity_mask != 0, ResultInvalidCombination);
2369
2370 // Validate the core id.
2371 if (IsValidVirtualCoreId(core_id)) {
2372 R_UNLESS(((1ULL << core_id) & affinity_mask) != 0, ResultInvalidCombination);
2373 } else {
2374 R_UNLESS(core_id == IdealCoreNoUpdate || core_id == IdealCoreDontCare,
2375 ResultInvalidCoreId);
2376 }
2377 }
2378
2379 // Get the thread from its handle.
2380 KScopedAutoObject thread =
2381 system.Kernel().CurrentProcess()->GetHandleTable().GetObject<KThread>(thread_handle);
2382 R_UNLESS(thread.IsNotNull(), ResultInvalidHandle);
2383
2384 // Set the core mask.
2385 R_TRY(thread->SetCoreMask(core_id, affinity_mask));
2386
2387 return ResultSuccess;
2388}
2389
2390static Result SetThreadCoreMask32(Core::System& system, Handle thread_handle, s32 core_id,
2391 u32 affinity_mask_low, u32 affinity_mask_high) {
2392 const auto affinity_mask = u64{affinity_mask_low} | (u64{affinity_mask_high} << 32);
2393 return SetThreadCoreMask(system, thread_handle, core_id, affinity_mask);
2394}
2395
2396static Result SignalEvent(Core::System& system, Handle event_handle) {
2397 LOG_DEBUG(Kernel_SVC, "called, event_handle=0x{:08X}", event_handle);
2398
2399 // Get the current handle table.
2400 const KHandleTable& handle_table = system.Kernel().CurrentProcess()->GetHandleTable();
2401
2402 // Get the event.
2403 KScopedAutoObject event = handle_table.GetObject<KEvent>(event_handle);
2404 R_UNLESS(event.IsNotNull(), ResultInvalidHandle);
2405
2406 return event->Signal();
2407}
2408
2409static Result SignalEvent32(Core::System& system, Handle event_handle) {
2410 return SignalEvent(system, event_handle);
2411}
2412
2413static Result ClearEvent(Core::System& system, Handle event_handle) {
2414 LOG_TRACE(Kernel_SVC, "called, event_handle=0x{:08X}", event_handle);
2415
2416 // Get the current handle table.
2417 const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable();
2418
2419 // Try to clear the writable event.
2420 {
2421 KScopedAutoObject event = handle_table.GetObject<KEvent>(event_handle);
2422 if (event.IsNotNull()) {
2423 return event->Clear();
2424 }
2425 }
2426
2427 // Try to clear the readable event.
2428 {
2429 KScopedAutoObject readable_event = handle_table.GetObject<KReadableEvent>(event_handle);
2430 if (readable_event.IsNotNull()) {
2431 return readable_event->Clear();
2432 }
2433 }
2434
2435 LOG_ERROR(Kernel_SVC, "Event handle does not exist, event_handle=0x{:08X}", event_handle);
2436
2437 return ResultInvalidHandle;
2438}
2439
2440static Result ClearEvent32(Core::System& system, Handle event_handle) {
2441 return ClearEvent(system, event_handle);
2442}
2443
2444static Result CreateEvent(Core::System& system, Handle* out_write, Handle* out_read) {
2445 LOG_DEBUG(Kernel_SVC, "called");
2446
2447 // Get the kernel reference and handle table.
2448 auto& kernel = system.Kernel();
2449 auto& handle_table = kernel.CurrentProcess()->GetHandleTable();
2450
2451 // Reserve a new event from the process resource limit
2452 KScopedResourceReservation event_reservation(kernel.CurrentProcess(),
2453 LimitableResource::EventCountMax);
2454 R_UNLESS(event_reservation.Succeeded(), ResultLimitReached);
2455
2456 // Create a new event.
2457 KEvent* event = KEvent::Create(kernel);
2458 R_UNLESS(event != nullptr, ResultOutOfResource);
2459
2460 // Initialize the event.
2461 event->Initialize(kernel.CurrentProcess());
2462
2463 // Commit the thread reservation.
2464 event_reservation.Commit();
2465
2466 // Ensure that we clean up the event (and its only references are handle table) on function end.
2467 SCOPE_EXIT({
2468 event->GetReadableEvent().Close();
2469 event->Close();
2470 });
2471
2472 // Register the event.
2473 KEvent::Register(kernel, event);
2474
2475 // Add the event to the handle table.
2476 R_TRY(handle_table.Add(out_write, event));
2477
2478 // Ensure that we maintaing a clean handle state on exit.
2479 auto handle_guard = SCOPE_GUARD({ handle_table.Remove(*out_write); });
2480
2481 // Add the readable event to the handle table.
2482 R_TRY(handle_table.Add(out_read, std::addressof(event->GetReadableEvent())));
2483
2484 // We succeeded.
2485 handle_guard.Cancel();
2486 return ResultSuccess;
2487}
2488
2489static Result CreateEvent32(Core::System& system, Handle* out_write, Handle* out_read) {
2490 return CreateEvent(system, out_write, out_read);
2491}
2492
2493static Result GetProcessInfo(Core::System& system, u64* out, Handle process_handle, u32 type) {
2494 LOG_DEBUG(Kernel_SVC, "called, handle=0x{:08X}, type=0x{:X}", process_handle, type);
2495
2496 const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable();
2497 KScopedAutoObject process = handle_table.GetObject<KProcess>(process_handle);
2498 if (process.IsNull()) {
2499 LOG_ERROR(Kernel_SVC, "Process handle does not exist, process_handle=0x{:08X}",
2500 process_handle);
2501 return ResultInvalidHandle;
2502 }
2503
2504 const auto info_type = static_cast<ProcessInfoType>(type);
2505 if (info_type != ProcessInfoType::ProcessState) {
2506 LOG_ERROR(Kernel_SVC, "Expected info_type to be ProcessState but got {} instead", type);
2507 return ResultInvalidEnumValue;
2508 }
2509
2510 *out = static_cast<u64>(process->GetState());
2511 return ResultSuccess;
2512}
2513
2514static Result CreateResourceLimit(Core::System& system, Handle* out_handle) {
2515 LOG_DEBUG(Kernel_SVC, "called");
2516
2517 // Create a new resource limit.
2518 auto& kernel = system.Kernel();
2519 KResourceLimit* resource_limit = KResourceLimit::Create(kernel);
2520 R_UNLESS(resource_limit != nullptr, ResultOutOfResource);
2521
2522 // Ensure we don't leak a reference to the limit.
2523 SCOPE_EXIT({ resource_limit->Close(); });
2524
2525 // Initialize the resource limit.
2526 resource_limit->Initialize(&system.CoreTiming());
2527
2528 // Register the limit.
2529 KResourceLimit::Register(kernel, resource_limit);
2530
2531 // Add the limit to the handle table.
2532 R_TRY(kernel.CurrentProcess()->GetHandleTable().Add(out_handle, resource_limit));
2533
2534 return ResultSuccess;
2535}
2536
2537static Result GetResourceLimitLimitValue(Core::System& system, u64* out_limit_value,
2538 Handle resource_limit_handle, LimitableResource which) {
2539 LOG_DEBUG(Kernel_SVC, "called, resource_limit_handle={:08X}, which={}", resource_limit_handle,
2540 which);
2541
2542 // Validate the resource.
2543 R_UNLESS(IsValidResourceType(which), ResultInvalidEnumValue);
2544
2545 // Get the resource limit.
2546 auto& kernel = system.Kernel();
2547 KScopedAutoObject resource_limit =
2548 kernel.CurrentProcess()->GetHandleTable().GetObject<KResourceLimit>(resource_limit_handle);
2549 R_UNLESS(resource_limit.IsNotNull(), ResultInvalidHandle);
2550
2551 // Get the limit value.
2552 *out_limit_value = resource_limit->GetLimitValue(which);
2553
2554 return ResultSuccess;
2555}
2556
2557static Result GetResourceLimitCurrentValue(Core::System& system, u64* out_current_value,
2558 Handle resource_limit_handle, LimitableResource which) {
2559 LOG_DEBUG(Kernel_SVC, "called, resource_limit_handle={:08X}, which={}", resource_limit_handle,
2560 which);
2561
2562 // Validate the resource.
2563 R_UNLESS(IsValidResourceType(which), ResultInvalidEnumValue);
2564
2565 // Get the resource limit.
2566 auto& kernel = system.Kernel();
2567 KScopedAutoObject resource_limit =
2568 kernel.CurrentProcess()->GetHandleTable().GetObject<KResourceLimit>(resource_limit_handle);
2569 R_UNLESS(resource_limit.IsNotNull(), ResultInvalidHandle);
2570
2571 // Get the current value.
2572 *out_current_value = resource_limit->GetCurrentValue(which);
2573
2574 return ResultSuccess;
2575}
2576
2577static Result SetResourceLimitLimitValue(Core::System& system, Handle resource_limit_handle,
2578 LimitableResource which, u64 limit_value) {
2579 LOG_DEBUG(Kernel_SVC, "called, resource_limit_handle={:08X}, which={}, limit_value={}",
2580 resource_limit_handle, which, limit_value);
2581
2582 // Validate the resource.
2583 R_UNLESS(IsValidResourceType(which), ResultInvalidEnumValue);
2584
2585 // Get the resource limit.
2586 auto& kernel = system.Kernel();
2587 KScopedAutoObject resource_limit =
2588 kernel.CurrentProcess()->GetHandleTable().GetObject<KResourceLimit>(resource_limit_handle);
2589 R_UNLESS(resource_limit.IsNotNull(), ResultInvalidHandle);
2590
2591 // Set the limit value.
2592 R_TRY(resource_limit->SetLimitValue(which, limit_value));
2593
2594 return ResultSuccess;
2595}
2596
2597static Result GetProcessList(Core::System& system, u32* out_num_processes, VAddr out_process_ids,
2598 u32 out_process_ids_size) {
2599 LOG_DEBUG(Kernel_SVC, "called. out_process_ids=0x{:016X}, out_process_ids_size={}",
2600 out_process_ids, out_process_ids_size);
2601
2602 // If the supplied size is negative or greater than INT32_MAX / sizeof(u64), bail.
2603 if ((out_process_ids_size & 0xF0000000) != 0) {
2604 LOG_ERROR(Kernel_SVC,
2605 "Supplied size outside [0, 0x0FFFFFFF] range. out_process_ids_size={}",
2606 out_process_ids_size);
2607 return ResultOutOfRange;
2608 }
2609
2610 const auto& kernel = system.Kernel();
2611 const auto total_copy_size = out_process_ids_size * sizeof(u64);
2612
2613 if (out_process_ids_size > 0 && !kernel.CurrentProcess()->PageTable().IsInsideAddressSpace(
2614 out_process_ids, total_copy_size)) {
2615 LOG_ERROR(Kernel_SVC, "Address range outside address space. begin=0x{:016X}, end=0x{:016X}",
2616 out_process_ids, out_process_ids + total_copy_size);
2617 return ResultInvalidCurrentMemory;
2618 }
2619
2620 auto& memory = system.Memory();
2621 const auto& process_list = kernel.GetProcessList();
2622 const auto num_processes = process_list.size();
2623 const auto copy_amount = std::min(std::size_t{out_process_ids_size}, num_processes);
2624
2625 for (std::size_t i = 0; i < copy_amount; ++i) {
2626 memory.Write64(out_process_ids, process_list[i]->GetProcessID());
2627 out_process_ids += sizeof(u64);
2628 }
2629
2630 *out_num_processes = static_cast<u32>(num_processes);
2631 return ResultSuccess;
2632}
2633
2634static Result GetThreadList(Core::System& system, u32* out_num_threads, VAddr out_thread_ids,
2635 u32 out_thread_ids_size, Handle debug_handle) {
2636 // TODO: Handle this case when debug events are supported.
2637 UNIMPLEMENTED_IF(debug_handle != InvalidHandle);
2638
2639 LOG_DEBUG(Kernel_SVC, "called. out_thread_ids=0x{:016X}, out_thread_ids_size={}",
2640 out_thread_ids, out_thread_ids_size);
2641
2642 // If the size is negative or larger than INT32_MAX / sizeof(u64)
2643 if ((out_thread_ids_size & 0xF0000000) != 0) {
2644 LOG_ERROR(Kernel_SVC, "Supplied size outside [0, 0x0FFFFFFF] range. size={}",
2645 out_thread_ids_size);
2646 return ResultOutOfRange;
2647 }
2648
2649 auto* const current_process = system.Kernel().CurrentProcess();
2650 const auto total_copy_size = out_thread_ids_size * sizeof(u64);
2651
2652 if (out_thread_ids_size > 0 &&
2653 !current_process->PageTable().IsInsideAddressSpace(out_thread_ids, total_copy_size)) {
2654 LOG_ERROR(Kernel_SVC, "Address range outside address space. begin=0x{:016X}, end=0x{:016X}",
2655 out_thread_ids, out_thread_ids + total_copy_size);
2656 return ResultInvalidCurrentMemory;
2657 }
2658
2659 auto& memory = system.Memory();
2660 const auto& thread_list = current_process->GetThreadList();
2661 const auto num_threads = thread_list.size();
2662 const auto copy_amount = std::min(std::size_t{out_thread_ids_size}, num_threads);
2663
2664 auto list_iter = thread_list.cbegin();
2665 for (std::size_t i = 0; i < copy_amount; ++i, ++list_iter) {
2666 memory.Write64(out_thread_ids, (*list_iter)->GetThreadID());
2667 out_thread_ids += sizeof(u64);
2668 }
2669
2670 *out_num_threads = static_cast<u32>(num_threads);
2671 return ResultSuccess;
2672}
2673
2674static Result FlushProcessDataCache32(Core::System& system, Handle process_handle, u64 address,
2675 u64 size) {
2676 // Validate address/size.
2677 R_UNLESS(size > 0, ResultInvalidSize);
2678 R_UNLESS(address == static_cast<uintptr_t>(address), ResultInvalidCurrentMemory);
2679 R_UNLESS(size == static_cast<size_t>(size), ResultInvalidCurrentMemory);
2680
2681 // Get the process from its handle.
2682 KScopedAutoObject process =
2683 system.Kernel().CurrentProcess()->GetHandleTable().GetObject<KProcess>(process_handle);
2684 R_UNLESS(process.IsNotNull(), ResultInvalidHandle);
2685
2686 // Verify the region is within range.
2687 auto& page_table = process->PageTable();
2688 R_UNLESS(page_table.Contains(address, size), ResultInvalidCurrentMemory);
2689
2690 // Perform the operation.
2691 R_RETURN(system.Memory().FlushDataCache(*process, address, size));
2692}
2693
2694namespace {
2695struct FunctionDef { 14struct FunctionDef {
2696 using Func = void(Core::System&); 15 using Func = void(Core::System&);
2697 16
@@ -2699,6 +18,7 @@ struct FunctionDef {
2699 Func* func; 18 Func* func;
2700 const char* name; 19 const char* name;
2701}; 20};
21
2702} // namespace 22} // namespace
2703 23
2704static const FunctionDef SVC_Table_32[] = { 24static const FunctionDef SVC_Table_32[] = {
diff --git a/src/core/hle/kernel/svc.h b/src/core/hle/kernel/svc.h
index 13f061b83..b599f9a3d 100644
--- a/src/core/hle/kernel/svc.h
+++ b/src/core/hle/kernel/svc.h
@@ -4,6 +4,8 @@
4#pragma once 4#pragma once
5 5
6#include "common/common_types.h" 6#include "common/common_types.h"
7#include "core/hle/kernel/svc_types.h"
8#include "core/hle/result.h"
7 9
8namespace Core { 10namespace Core {
9class System; 11class System;
@@ -13,4 +15,158 @@ namespace Kernel::Svc {
13 15
14void Call(Core::System& system, u32 immediate); 16void Call(Core::System& system, u32 immediate);
15 17
18Result SetHeapSize(Core::System& system, VAddr* out_address, u64 size);
19Result SetMemoryPermission(Core::System& system, VAddr address, u64 size, MemoryPermission perm);
20Result SetMemoryAttribute(Core::System& system, VAddr address, u64 size, u32 mask, u32 attr);
21Result MapMemory(Core::System& system, VAddr dst_addr, VAddr src_addr, u64 size);
22Result UnmapMemory(Core::System& system, VAddr dst_addr, VAddr src_addr, u64 size);
23Result QueryMemory(Core::System& system, VAddr memory_info_address, VAddr page_info_address,
24 VAddr query_address);
25void ExitProcess(Core::System& system);
26Result CreateThread(Core::System& system, Handle* out_handle, VAddr entry_point, u64 arg,
27 VAddr stack_bottom, u32 priority, s32 core_id);
28Result StartThread(Core::System& system, Handle thread_handle);
29void ExitThread(Core::System& system);
30void SleepThread(Core::System& system, s64 nanoseconds);
31Result GetThreadPriority(Core::System& system, u32* out_priority, Handle handle);
32Result SetThreadPriority(Core::System& system, Handle thread_handle, u32 priority);
33Result GetThreadCoreMask(Core::System& system, Handle thread_handle, s32* out_core_id,
34 u64* out_affinity_mask);
35Result SetThreadCoreMask(Core::System& system, Handle thread_handle, s32 core_id,
36 u64 affinity_mask);
37u32 GetCurrentProcessorNumber(Core::System& system);
38Result SignalEvent(Core::System& system, Handle event_handle);
39Result ClearEvent(Core::System& system, Handle event_handle);
40Result MapSharedMemory(Core::System& system, Handle shmem_handle, VAddr address, u64 size,
41 MemoryPermission map_perm);
42Result UnmapSharedMemory(Core::System& system, Handle shmem_handle, VAddr address, u64 size);
43Result CreateTransferMemory(Core::System& system, Handle* out, VAddr address, u64 size,
44 MemoryPermission map_perm);
45Result CloseHandle(Core::System& system, Handle handle);
46Result ResetSignal(Core::System& system, Handle handle);
47Result WaitSynchronization(Core::System& system, s32* index, VAddr handles_address, s32 num_handles,
48 s64 nano_seconds);
49Result CancelSynchronization(Core::System& system, Handle handle);
50Result ArbitrateLock(Core::System& system, Handle thread_handle, VAddr address, u32 tag);
51Result ArbitrateUnlock(Core::System& system, VAddr address);
52Result WaitProcessWideKeyAtomic(Core::System& system, VAddr address, VAddr cv_key, u32 tag,
53 s64 timeout_ns);
54void SignalProcessWideKey(Core::System& system, VAddr cv_key, s32 count);
55u64 GetSystemTick(Core::System& system);
56Result ConnectToNamedPort(Core::System& system, Handle* out, VAddr port_name_address);
57Result SendSyncRequest(Core::System& system, Handle handle);
58Result GetProcessId(Core::System& system, u64* out_process_id, Handle handle);
59Result GetThreadId(Core::System& system, u64* out_thread_id, Handle thread_handle);
60void Break(Core::System& system, u32 reason, u64 info1, u64 info2);
61void OutputDebugString(Core::System& system, VAddr address, u64 len);
62Result GetInfo(Core::System& system, u64* result, u64 info_id, Handle handle, u64 info_sub_id);
63Result MapPhysicalMemory(Core::System& system, VAddr addr, u64 size);
64Result UnmapPhysicalMemory(Core::System& system, VAddr addr, u64 size);
65Result GetResourceLimitLimitValue(Core::System& system, u64* out_limit_value,
66 Handle resource_limit_handle, LimitableResource which);
67Result GetResourceLimitCurrentValue(Core::System& system, u64* out_current_value,
68 Handle resource_limit_handle, LimitableResource which);
69Result SetThreadActivity(Core::System& system, Handle thread_handle,
70 ThreadActivity thread_activity);
71Result GetThreadContext(Core::System& system, VAddr out_context, Handle thread_handle);
72Result WaitForAddress(Core::System& system, VAddr address, ArbitrationType arb_type, s32 value,
73 s64 timeout_ns);
74Result SignalToAddress(Core::System& system, VAddr address, SignalType signal_type, s32 value,
75 s32 count);
76void SynchronizePreemptionState(Core::System& system);
77void KernelDebug(Core::System& system, u32 kernel_debug_type, u64 param1, u64 param2, u64 param3);
78void ChangeKernelTraceState(Core::System& system, u32 trace_state);
79Result CreateSession(Core::System& system, Handle* out_server, Handle* out_client, u32 is_light,
80 u64 name);
81Result ReplyAndReceive(Core::System& system, s32* out_index, Handle* handles, s32 num_handles,
82 Handle reply_target, s64 timeout_ns);
83Result CreateEvent(Core::System& system, Handle* out_write, Handle* out_read);
84Result CreateCodeMemory(Core::System& system, Handle* out, VAddr address, size_t size);
85Result ControlCodeMemory(Core::System& system, Handle code_memory_handle, u32 operation,
86 VAddr address, size_t size, MemoryPermission perm);
87Result GetProcessList(Core::System& system, u32* out_num_processes, VAddr out_process_ids,
88 u32 out_process_ids_size);
89Result GetThreadList(Core::System& system, u32* out_num_threads, VAddr out_thread_ids,
90 u32 out_thread_ids_size, Handle debug_handle);
91Result SetProcessMemoryPermission(Core::System& system, Handle process_handle, VAddr address,
92 u64 size, MemoryPermission perm);
93Result MapProcessMemory(Core::System& system, VAddr dst_address, Handle process_handle,
94 VAddr src_address, u64 size);
95Result UnmapProcessMemory(Core::System& system, VAddr dst_address, Handle process_handle,
96 VAddr src_address, u64 size);
97Result QueryProcessMemory(Core::System& system, VAddr memory_info_address, VAddr page_info_address,
98 Handle process_handle, VAddr address);
99Result MapProcessCodeMemory(Core::System& system, Handle process_handle, u64 dst_address,
100 u64 src_address, u64 size);
101Result UnmapProcessCodeMemory(Core::System& system, Handle process_handle, u64 dst_address,
102 u64 src_address, u64 size);
103Result GetProcessInfo(Core::System& system, u64* out, Handle process_handle, u32 type);
104Result CreateResourceLimit(Core::System& system, Handle* out_handle);
105Result SetResourceLimitLimitValue(Core::System& system, Handle resource_limit_handle,
106 LimitableResource which, u64 limit_value);
107
108//
109
110Result SetHeapSize32(Core::System& system, u32* heap_addr, u32 heap_size);
111Result SetMemoryAttribute32(Core::System& system, u32 address, u32 size, u32 mask, u32 attr);
112Result MapMemory32(Core::System& system, u32 dst_addr, u32 src_addr, u32 size);
113Result UnmapMemory32(Core::System& system, u32 dst_addr, u32 src_addr, u32 size);
114Result QueryMemory32(Core::System& system, u32 memory_info_address, u32 page_info_address,
115 u32 query_address);
116void ExitProcess32(Core::System& system);
117Result CreateThread32(Core::System& system, Handle* out_handle, u32 priority, u32 entry_point,
118 u32 arg, u32 stack_top, s32 processor_id);
119Result StartThread32(Core::System& system, Handle thread_handle);
120void ExitThread32(Core::System& system);
121void SleepThread32(Core::System& system, u32 nanoseconds_low, u32 nanoseconds_high);
122Result GetThreadPriority32(Core::System& system, u32* out_priority, Handle handle);
123Result SetThreadPriority32(Core::System& system, Handle thread_handle, u32 priority);
124Result GetThreadCoreMask32(Core::System& system, Handle thread_handle, s32* out_core_id,
125 u32* out_affinity_mask_low, u32* out_affinity_mask_high);
126Result SetThreadCoreMask32(Core::System& system, Handle thread_handle, s32 core_id,
127 u32 affinity_mask_low, u32 affinity_mask_high);
128u32 GetCurrentProcessorNumber32(Core::System& system);
129Result SignalEvent32(Core::System& system, Handle event_handle);
130Result ClearEvent32(Core::System& system, Handle event_handle);
131Result MapSharedMemory32(Core::System& system, Handle shmem_handle, u32 address, u32 size,
132 MemoryPermission map_perm);
133Result UnmapSharedMemory32(Core::System& system, Handle shmem_handle, u32 address, u32 size);
134Result CreateTransferMemory32(Core::System& system, Handle* out, u32 address, u32 size,
135 MemoryPermission map_perm);
136Result CloseHandle32(Core::System& system, Handle handle);
137Result ResetSignal32(Core::System& system, Handle handle);
138Result WaitSynchronization32(Core::System& system, u32 timeout_low, u32 handles_address,
139 s32 num_handles, u32 timeout_high, s32* index);
140Result CancelSynchronization32(Core::System& system, Handle handle);
141Result ArbitrateLock32(Core::System& system, Handle thread_handle, u32 address, u32 tag);
142Result ArbitrateUnlock32(Core::System& system, u32 address);
143Result WaitProcessWideKeyAtomic32(Core::System& system, u32 address, u32 cv_key, u32 tag,
144 u32 timeout_ns_low, u32 timeout_ns_high);
145void SignalProcessWideKey32(Core::System& system, u32 cv_key, s32 count);
146void GetSystemTick32(Core::System& system, u32* time_low, u32* time_high);
147Result ConnectToNamedPort32(Core::System& system, Handle* out_handle, u32 port_name_address);
148Result SendSyncRequest32(Core::System& system, Handle handle);
149Result GetProcessId32(Core::System& system, u32* out_process_id_low, u32* out_process_id_high,
150 Handle handle);
151Result GetThreadId32(Core::System& system, u32* out_thread_id_low, u32* out_thread_id_high,
152 Handle thread_handle);
153void Break32(Core::System& system, u32 reason, u32 info1, u32 info2);
154void OutputDebugString32(Core::System& system, u32 address, u32 len);
155Result GetInfo32(Core::System& system, u32* result_low, u32* result_high, u32 sub_id_low,
156 u32 info_id, u32 handle, u32 sub_id_high);
157Result MapPhysicalMemory32(Core::System& system, u32 addr, u32 size);
158Result UnmapPhysicalMemory32(Core::System& system, u32 addr, u32 size);
159Result SetThreadActivity32(Core::System& system, Handle thread_handle,
160 ThreadActivity thread_activity);
161Result GetThreadContext32(Core::System& system, u32 out_context, Handle thread_handle);
162Result WaitForAddress32(Core::System& system, u32 address, ArbitrationType arb_type, s32 value,
163 u32 timeout_ns_low, u32 timeout_ns_high);
164Result SignalToAddress32(Core::System& system, u32 address, SignalType signal_type, s32 value,
165 s32 count);
166Result CreateEvent32(Core::System& system, Handle* out_write, Handle* out_read);
167Result CreateCodeMemory32(Core::System& system, Handle* out, u32 address, u32 size);
168Result ControlCodeMemory32(Core::System& system, Handle code_memory_handle, u32 operation,
169 u64 address, u64 size, MemoryPermission perm);
170Result FlushProcessDataCache32(Core::System& system, Handle process_handle, u64 address, u64 size);
171
16} // namespace Kernel::Svc 172} // namespace Kernel::Svc
diff --git a/src/core/hle/kernel/svc/svc_activity.cpp b/src/core/hle/kernel/svc/svc_activity.cpp
new file mode 100644
index 000000000..8774a5c98
--- /dev/null
+++ b/src/core/hle/kernel/svc/svc_activity.cpp
@@ -0,0 +1,44 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "core/core.h"
5#include "core/hle/kernel/k_process.h"
6#include "core/hle/kernel/k_thread.h"
7#include "core/hle/kernel/svc.h"
8#include "core/hle/kernel/svc_results.h"
9
10namespace Kernel::Svc {
11
12/// Sets the thread activity
13Result SetThreadActivity(Core::System& system, Handle thread_handle,
14 ThreadActivity thread_activity) {
15 LOG_DEBUG(Kernel_SVC, "called, handle=0x{:08X}, activity=0x{:08X}", thread_handle,
16 thread_activity);
17
18 // Validate the activity.
19 constexpr auto IsValidThreadActivity = [](ThreadActivity activity) {
20 return activity == ThreadActivity::Runnable || activity == ThreadActivity::Paused;
21 };
22 R_UNLESS(IsValidThreadActivity(thread_activity), ResultInvalidEnumValue);
23
24 // Get the thread from its handle.
25 KScopedAutoObject thread =
26 system.Kernel().CurrentProcess()->GetHandleTable().GetObject<KThread>(thread_handle);
27 R_UNLESS(thread.IsNotNull(), ResultInvalidHandle);
28
29 // Check that the activity is being set on a non-current thread for the current process.
30 R_UNLESS(thread->GetOwnerProcess() == system.Kernel().CurrentProcess(), ResultInvalidHandle);
31 R_UNLESS(thread.GetPointerUnsafe() != GetCurrentThreadPointer(system.Kernel()), ResultBusy);
32
33 // Set the activity.
34 R_TRY(thread->SetActivity(thread_activity));
35
36 return ResultSuccess;
37}
38
39Result SetThreadActivity32(Core::System& system, Handle thread_handle,
40 ThreadActivity thread_activity) {
41 return SetThreadActivity(system, thread_handle, thread_activity);
42}
43
44} // namespace Kernel::Svc
diff --git a/src/core/hle/kernel/svc/svc_address_arbiter.cpp b/src/core/hle/kernel/svc/svc_address_arbiter.cpp
new file mode 100644
index 000000000..842107726
--- /dev/null
+++ b/src/core/hle/kernel/svc/svc_address_arbiter.cpp
@@ -0,0 +1,113 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "core/core.h"
5#include "core/hle/kernel/k_memory_layout.h"
6#include "core/hle/kernel/k_process.h"
7#include "core/hle/kernel/kernel.h"
8#include "core/hle/kernel/svc.h"
9#include "core/hle/kernel/svc_results.h"
10#include "core/hle/kernel/svc_types.h"
11
12namespace Kernel::Svc {
13namespace {
14
15constexpr bool IsValidSignalType(Svc::SignalType type) {
16 switch (type) {
17 case Svc::SignalType::Signal:
18 case Svc::SignalType::SignalAndIncrementIfEqual:
19 case Svc::SignalType::SignalAndModifyByWaitingCountIfEqual:
20 return true;
21 default:
22 return false;
23 }
24}
25
26constexpr bool IsValidArbitrationType(Svc::ArbitrationType type) {
27 switch (type) {
28 case Svc::ArbitrationType::WaitIfLessThan:
29 case Svc::ArbitrationType::DecrementAndWaitIfLessThan:
30 case Svc::ArbitrationType::WaitIfEqual:
31 return true;
32 default:
33 return false;
34 }
35}
36
37} // namespace
38
39// Wait for an address (via Address Arbiter)
40Result WaitForAddress(Core::System& system, VAddr address, ArbitrationType arb_type, s32 value,
41 s64 timeout_ns) {
42 LOG_TRACE(Kernel_SVC, "called, address=0x{:X}, arb_type=0x{:X}, value=0x{:X}, timeout_ns={}",
43 address, arb_type, value, timeout_ns);
44
45 // Validate input.
46 if (IsKernelAddress(address)) {
47 LOG_ERROR(Kernel_SVC, "Attempting to wait on kernel address (address={:08X})", address);
48 return ResultInvalidCurrentMemory;
49 }
50 if (!Common::IsAligned(address, sizeof(s32))) {
51 LOG_ERROR(Kernel_SVC, "Wait address must be 4 byte aligned (address={:08X})", address);
52 return ResultInvalidAddress;
53 }
54 if (!IsValidArbitrationType(arb_type)) {
55 LOG_ERROR(Kernel_SVC, "Invalid arbitration type specified (type={})", arb_type);
56 return ResultInvalidEnumValue;
57 }
58
59 // Convert timeout from nanoseconds to ticks.
60 s64 timeout{};
61 if (timeout_ns > 0) {
62 const s64 offset_tick(timeout_ns);
63 if (offset_tick > 0) {
64 timeout = offset_tick + 2;
65 if (timeout <= 0) {
66 timeout = std::numeric_limits<s64>::max();
67 }
68 } else {
69 timeout = std::numeric_limits<s64>::max();
70 }
71 } else {
72 timeout = timeout_ns;
73 }
74
75 return system.Kernel().CurrentProcess()->WaitAddressArbiter(address, arb_type, value, timeout);
76}
77
78Result WaitForAddress32(Core::System& system, u32 address, ArbitrationType arb_type, s32 value,
79 u32 timeout_ns_low, u32 timeout_ns_high) {
80 const auto timeout = static_cast<s64>(timeout_ns_low | (u64{timeout_ns_high} << 32));
81 return WaitForAddress(system, address, arb_type, value, timeout);
82}
83
84// Signals to an address (via Address Arbiter)
85Result SignalToAddress(Core::System& system, VAddr address, SignalType signal_type, s32 value,
86 s32 count) {
87 LOG_TRACE(Kernel_SVC, "called, address=0x{:X}, signal_type=0x{:X}, value=0x{:X}, count=0x{:X}",
88 address, signal_type, value, count);
89
90 // Validate input.
91 if (IsKernelAddress(address)) {
92 LOG_ERROR(Kernel_SVC, "Attempting to signal to a kernel address (address={:08X})", address);
93 return ResultInvalidCurrentMemory;
94 }
95 if (!Common::IsAligned(address, sizeof(s32))) {
96 LOG_ERROR(Kernel_SVC, "Signaled address must be 4 byte aligned (address={:08X})", address);
97 return ResultInvalidAddress;
98 }
99 if (!IsValidSignalType(signal_type)) {
100 LOG_ERROR(Kernel_SVC, "Invalid signal type specified (type={})", signal_type);
101 return ResultInvalidEnumValue;
102 }
103
104 return system.Kernel().CurrentProcess()->SignalAddressArbiter(address, signal_type, value,
105 count);
106}
107
108Result SignalToAddress32(Core::System& system, u32 address, SignalType signal_type, s32 value,
109 s32 count) {
110 return SignalToAddress(system, address, signal_type, value, count);
111}
112
113} // namespace Kernel::Svc
diff --git a/src/core/hle/kernel/svc/svc_address_translation.cpp b/src/core/hle/kernel/svc/svc_address_translation.cpp
new file mode 100644
index 000000000..299e22ae6
--- /dev/null
+++ b/src/core/hle/kernel/svc/svc_address_translation.cpp
@@ -0,0 +1,6 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "core/hle/kernel/svc.h"
5
6namespace Kernel::Svc {} // namespace Kernel::Svc
diff --git a/src/core/hle/kernel/svc/svc_cache.cpp b/src/core/hle/kernel/svc/svc_cache.cpp
new file mode 100644
index 000000000..42167d35b
--- /dev/null
+++ b/src/core/hle/kernel/svc/svc_cache.cpp
@@ -0,0 +1,31 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "core/core.h"
5#include "core/hle/kernel/k_process.h"
6#include "core/hle/kernel/svc.h"
7#include "core/hle/kernel/svc_results.h"
8#include "core/hle/kernel/svc_types.h"
9
10namespace Kernel::Svc {
11
12Result FlushProcessDataCache32(Core::System& system, Handle process_handle, u64 address, u64 size) {
13 // Validate address/size.
14 R_UNLESS(size > 0, ResultInvalidSize);
15 R_UNLESS(address == static_cast<uintptr_t>(address), ResultInvalidCurrentMemory);
16 R_UNLESS(size == static_cast<size_t>(size), ResultInvalidCurrentMemory);
17
18 // Get the process from its handle.
19 KScopedAutoObject process =
20 system.Kernel().CurrentProcess()->GetHandleTable().GetObject<KProcess>(process_handle);
21 R_UNLESS(process.IsNotNull(), ResultInvalidHandle);
22
23 // Verify the region is within range.
24 auto& page_table = process->PageTable();
25 R_UNLESS(page_table.Contains(address, size), ResultInvalidCurrentMemory);
26
27 // Perform the operation.
28 R_RETURN(system.Memory().FlushDataCache(*process, address, size));
29}
30
31} // namespace Kernel::Svc
diff --git a/src/core/hle/kernel/svc/svc_code_memory.cpp b/src/core/hle/kernel/svc/svc_code_memory.cpp
new file mode 100644
index 000000000..4cb21e101
--- /dev/null
+++ b/src/core/hle/kernel/svc/svc_code_memory.cpp
@@ -0,0 +1,154 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "core/core.h"
5#include "core/hle/kernel/k_code_memory.h"
6#include "core/hle/kernel/k_process.h"
7#include "core/hle/kernel/kernel.h"
8#include "core/hle/kernel/svc.h"
9
10namespace Kernel::Svc {
11namespace {
12
13constexpr bool IsValidMapCodeMemoryPermission(MemoryPermission perm) {
14 return perm == MemoryPermission::ReadWrite;
15}
16
17constexpr bool IsValidMapToOwnerCodeMemoryPermission(MemoryPermission perm) {
18 return perm == MemoryPermission::Read || perm == MemoryPermission::ReadExecute;
19}
20
21constexpr bool IsValidUnmapCodeMemoryPermission(MemoryPermission perm) {
22 return perm == MemoryPermission::None;
23}
24
25constexpr bool IsValidUnmapFromOwnerCodeMemoryPermission(MemoryPermission perm) {
26 return perm == MemoryPermission::None;
27}
28
29} // namespace
30
31Result CreateCodeMemory(Core::System& system, Handle* out, VAddr address, size_t size) {
32 LOG_TRACE(Kernel_SVC, "called, address=0x{:X}, size=0x{:X}", address, size);
33
34 // Get kernel instance.
35 auto& kernel = system.Kernel();
36
37 // Validate address / size.
38 R_UNLESS(Common::IsAligned(address, PageSize), ResultInvalidAddress);
39 R_UNLESS(Common::IsAligned(size, PageSize), ResultInvalidSize);
40 R_UNLESS(size > 0, ResultInvalidSize);
41 R_UNLESS((address < address + size), ResultInvalidCurrentMemory);
42
43 // Create the code memory.
44
45 KCodeMemory* code_mem = KCodeMemory::Create(kernel);
46 R_UNLESS(code_mem != nullptr, ResultOutOfResource);
47
48 // Verify that the region is in range.
49 R_UNLESS(system.CurrentProcess()->PageTable().Contains(address, size),
50 ResultInvalidCurrentMemory);
51
52 // Initialize the code memory.
53 R_TRY(code_mem->Initialize(system.DeviceMemory(), address, size));
54
55 // Register the code memory.
56 KCodeMemory::Register(kernel, code_mem);
57
58 // Add the code memory to the handle table.
59 R_TRY(system.CurrentProcess()->GetHandleTable().Add(out, code_mem));
60
61 code_mem->Close();
62
63 return ResultSuccess;
64}
65
66Result CreateCodeMemory32(Core::System& system, Handle* out, u32 address, u32 size) {
67 return CreateCodeMemory(system, out, address, size);
68}
69
70Result ControlCodeMemory(Core::System& system, Handle code_memory_handle, u32 operation,
71 VAddr address, size_t size, MemoryPermission perm) {
72
73 LOG_TRACE(Kernel_SVC,
74 "called, code_memory_handle=0x{:X}, operation=0x{:X}, address=0x{:X}, size=0x{:X}, "
75 "permission=0x{:X}",
76 code_memory_handle, operation, address, size, perm);
77
78 // Validate the address / size.
79 R_UNLESS(Common::IsAligned(address, PageSize), ResultInvalidAddress);
80 R_UNLESS(Common::IsAligned(size, PageSize), ResultInvalidSize);
81 R_UNLESS(size > 0, ResultInvalidSize);
82 R_UNLESS((address < address + size), ResultInvalidCurrentMemory);
83
84 // Get the code memory from its handle.
85 KScopedAutoObject code_mem =
86 system.CurrentProcess()->GetHandleTable().GetObject<KCodeMemory>(code_memory_handle);
87 R_UNLESS(code_mem.IsNotNull(), ResultInvalidHandle);
88
89 // NOTE: Here, Atmosphere extends the SVC to allow code memory operations on one's own process.
90 // This enables homebrew usage of these SVCs for JIT.
91
92 // Perform the operation.
93 switch (static_cast<CodeMemoryOperation>(operation)) {
94 case CodeMemoryOperation::Map: {
95 // Check that the region is in range.
96 R_UNLESS(
97 system.CurrentProcess()->PageTable().CanContain(address, size, KMemoryState::CodeOut),
98 ResultInvalidMemoryRegion);
99
100 // Check the memory permission.
101 R_UNLESS(IsValidMapCodeMemoryPermission(perm), ResultInvalidNewMemoryPermission);
102
103 // Map the memory.
104 R_TRY(code_mem->Map(address, size));
105 } break;
106 case CodeMemoryOperation::Unmap: {
107 // Check that the region is in range.
108 R_UNLESS(
109 system.CurrentProcess()->PageTable().CanContain(address, size, KMemoryState::CodeOut),
110 ResultInvalidMemoryRegion);
111
112 // Check the memory permission.
113 R_UNLESS(IsValidUnmapCodeMemoryPermission(perm), ResultInvalidNewMemoryPermission);
114
115 // Unmap the memory.
116 R_TRY(code_mem->Unmap(address, size));
117 } break;
118 case CodeMemoryOperation::MapToOwner: {
119 // Check that the region is in range.
120 R_UNLESS(code_mem->GetOwner()->PageTable().CanContain(address, size,
121 KMemoryState::GeneratedCode),
122 ResultInvalidMemoryRegion);
123
124 // Check the memory permission.
125 R_UNLESS(IsValidMapToOwnerCodeMemoryPermission(perm), ResultInvalidNewMemoryPermission);
126
127 // Map the memory to its owner.
128 R_TRY(code_mem->MapToOwner(address, size, perm));
129 } break;
130 case CodeMemoryOperation::UnmapFromOwner: {
131 // Check that the region is in range.
132 R_UNLESS(code_mem->GetOwner()->PageTable().CanContain(address, size,
133 KMemoryState::GeneratedCode),
134 ResultInvalidMemoryRegion);
135
136 // Check the memory permission.
137 R_UNLESS(IsValidUnmapFromOwnerCodeMemoryPermission(perm), ResultInvalidNewMemoryPermission);
138
139 // Unmap the memory from its owner.
140 R_TRY(code_mem->UnmapFromOwner(address, size));
141 } break;
142 default:
143 return ResultInvalidEnumValue;
144 }
145
146 return ResultSuccess;
147}
148
149Result ControlCodeMemory32(Core::System& system, Handle code_memory_handle, u32 operation,
150 u64 address, u64 size, MemoryPermission perm) {
151 return ControlCodeMemory(system, code_memory_handle, operation, address, size, perm);
152}
153
154} // namespace Kernel::Svc
diff --git a/src/core/hle/kernel/svc/svc_condition_variable.cpp b/src/core/hle/kernel/svc/svc_condition_variable.cpp
new file mode 100644
index 000000000..d6cfc87c5
--- /dev/null
+++ b/src/core/hle/kernel/svc/svc_condition_variable.cpp
@@ -0,0 +1,69 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "core/core.h"
5#include "core/hle/kernel/k_memory_layout.h"
6#include "core/hle/kernel/k_process.h"
7#include "core/hle/kernel/kernel.h"
8#include "core/hle/kernel/svc.h"
9#include "core/hle/kernel/svc_results.h"
10
11namespace Kernel::Svc {
12
13/// Wait process wide key atomic
14Result WaitProcessWideKeyAtomic(Core::System& system, VAddr address, VAddr cv_key, u32 tag,
15 s64 timeout_ns) {
16 LOG_TRACE(Kernel_SVC, "called address={:X}, cv_key={:X}, tag=0x{:08X}, timeout_ns={}", address,
17 cv_key, tag, timeout_ns);
18
19 // Validate input.
20 if (IsKernelAddress(address)) {
21 LOG_ERROR(Kernel_SVC, "Attempted to wait on kernel address (address={:08X})", address);
22 return ResultInvalidCurrentMemory;
23 }
24 if (!Common::IsAligned(address, sizeof(s32))) {
25 LOG_ERROR(Kernel_SVC, "Address must be 4 byte aligned (address={:08X})", address);
26 return ResultInvalidAddress;
27 }
28
29 // Convert timeout from nanoseconds to ticks.
30 s64 timeout{};
31 if (timeout_ns > 0) {
32 const s64 offset_tick(timeout_ns);
33 if (offset_tick > 0) {
34 timeout = offset_tick + 2;
35 if (timeout <= 0) {
36 timeout = std::numeric_limits<s64>::max();
37 }
38 } else {
39 timeout = std::numeric_limits<s64>::max();
40 }
41 } else {
42 timeout = timeout_ns;
43 }
44
45 // Wait on the condition variable.
46 return system.Kernel().CurrentProcess()->WaitConditionVariable(
47 address, Common::AlignDown(cv_key, sizeof(u32)), tag, timeout);
48}
49
50Result WaitProcessWideKeyAtomic32(Core::System& system, u32 address, u32 cv_key, u32 tag,
51 u32 timeout_ns_low, u32 timeout_ns_high) {
52 const auto timeout_ns = static_cast<s64>(timeout_ns_low | (u64{timeout_ns_high} << 32));
53 return WaitProcessWideKeyAtomic(system, address, cv_key, tag, timeout_ns);
54}
55
56/// Signal process wide key
57void SignalProcessWideKey(Core::System& system, VAddr cv_key, s32 count) {
58 LOG_TRACE(Kernel_SVC, "called, cv_key=0x{:X}, count=0x{:08X}", cv_key, count);
59
60 // Signal the condition variable.
61 return system.Kernel().CurrentProcess()->SignalConditionVariable(
62 Common::AlignDown(cv_key, sizeof(u32)), count);
63}
64
65void SignalProcessWideKey32(Core::System& system, u32 cv_key, s32 count) {
66 SignalProcessWideKey(system, cv_key, count);
67}
68
69} // namespace Kernel::Svc
diff --git a/src/core/hle/kernel/svc/svc_debug.cpp b/src/core/hle/kernel/svc/svc_debug.cpp
new file mode 100644
index 000000000..299e22ae6
--- /dev/null
+++ b/src/core/hle/kernel/svc/svc_debug.cpp
@@ -0,0 +1,6 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "core/hle/kernel/svc.h"
5
6namespace Kernel::Svc {} // namespace Kernel::Svc
diff --git a/src/core/hle/kernel/svc/svc_debug_string.cpp b/src/core/hle/kernel/svc/svc_debug_string.cpp
new file mode 100644
index 000000000..486e62cc4
--- /dev/null
+++ b/src/core/hle/kernel/svc/svc_debug_string.cpp
@@ -0,0 +1,25 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "core/core.h"
5#include "core/hle/kernel/svc.h"
6#include "core/memory.h"
7
8namespace Kernel::Svc {
9
10/// Used to output a message on a debug hardware unit - does nothing on a retail unit
11void OutputDebugString(Core::System& system, VAddr address, u64 len) {
12 if (len == 0) {
13 return;
14 }
15
16 std::string str(len, '\0');
17 system.Memory().ReadBlock(address, str.data(), str.size());
18 LOG_DEBUG(Debug_Emulated, "{}", str);
19}
20
21void OutputDebugString32(Core::System& system, u32 address, u32 len) {
22 OutputDebugString(system, address, len);
23}
24
25} // namespace Kernel::Svc
diff --git a/src/core/hle/kernel/svc/svc_device_address_space.cpp b/src/core/hle/kernel/svc/svc_device_address_space.cpp
new file mode 100644
index 000000000..299e22ae6
--- /dev/null
+++ b/src/core/hle/kernel/svc/svc_device_address_space.cpp
@@ -0,0 +1,6 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "core/hle/kernel/svc.h"
5
6namespace Kernel::Svc {} // namespace Kernel::Svc
diff --git a/src/core/hle/kernel/svc/svc_event.cpp b/src/core/hle/kernel/svc/svc_event.cpp
new file mode 100644
index 000000000..885f02f50
--- /dev/null
+++ b/src/core/hle/kernel/svc/svc_event.cpp
@@ -0,0 +1,111 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "common/scope_exit.h"
5#include "core/core.h"
6#include "core/hle/kernel/k_event.h"
7#include "core/hle/kernel/k_process.h"
8#include "core/hle/kernel/k_scoped_resource_reservation.h"
9#include "core/hle/kernel/kernel.h"
10#include "core/hle/kernel/svc.h"
11
12namespace Kernel::Svc {
13
14Result SignalEvent(Core::System& system, Handle event_handle) {
15 LOG_DEBUG(Kernel_SVC, "called, event_handle=0x{:08X}", event_handle);
16
17 // Get the current handle table.
18 const KHandleTable& handle_table = system.Kernel().CurrentProcess()->GetHandleTable();
19
20 // Get the event.
21 KScopedAutoObject event = handle_table.GetObject<KEvent>(event_handle);
22 R_UNLESS(event.IsNotNull(), ResultInvalidHandle);
23
24 return event->Signal();
25}
26
27Result SignalEvent32(Core::System& system, Handle event_handle) {
28 return SignalEvent(system, event_handle);
29}
30
31Result ClearEvent(Core::System& system, Handle event_handle) {
32 LOG_TRACE(Kernel_SVC, "called, event_handle=0x{:08X}", event_handle);
33
34 // Get the current handle table.
35 const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable();
36
37 // Try to clear the writable event.
38 {
39 KScopedAutoObject event = handle_table.GetObject<KEvent>(event_handle);
40 if (event.IsNotNull()) {
41 return event->Clear();
42 }
43 }
44
45 // Try to clear the readable event.
46 {
47 KScopedAutoObject readable_event = handle_table.GetObject<KReadableEvent>(event_handle);
48 if (readable_event.IsNotNull()) {
49 return readable_event->Clear();
50 }
51 }
52
53 LOG_ERROR(Kernel_SVC, "Event handle does not exist, event_handle=0x{:08X}", event_handle);
54
55 return ResultInvalidHandle;
56}
57
58Result ClearEvent32(Core::System& system, Handle event_handle) {
59 return ClearEvent(system, event_handle);
60}
61
62Result CreateEvent(Core::System& system, Handle* out_write, Handle* out_read) {
63 LOG_DEBUG(Kernel_SVC, "called");
64
65 // Get the kernel reference and handle table.
66 auto& kernel = system.Kernel();
67 auto& handle_table = kernel.CurrentProcess()->GetHandleTable();
68
69 // Reserve a new event from the process resource limit
70 KScopedResourceReservation event_reservation(kernel.CurrentProcess(),
71 LimitableResource::EventCountMax);
72 R_UNLESS(event_reservation.Succeeded(), ResultLimitReached);
73
74 // Create a new event.
75 KEvent* event = KEvent::Create(kernel);
76 R_UNLESS(event != nullptr, ResultOutOfResource);
77
78 // Initialize the event.
79 event->Initialize(kernel.CurrentProcess());
80
81 // Commit the thread reservation.
82 event_reservation.Commit();
83
84 // Ensure that we clean up the event (and its only references are handle table) on function end.
85 SCOPE_EXIT({
86 event->GetReadableEvent().Close();
87 event->Close();
88 });
89
90 // Register the event.
91 KEvent::Register(kernel, event);
92
93 // Add the event to the handle table.
94 R_TRY(handle_table.Add(out_write, event));
95
96 // Ensure that we maintaing a clean handle state on exit.
97 auto handle_guard = SCOPE_GUARD({ handle_table.Remove(*out_write); });
98
99 // Add the readable event to the handle table.
100 R_TRY(handle_table.Add(out_read, std::addressof(event->GetReadableEvent())));
101
102 // We succeeded.
103 handle_guard.Cancel();
104 return ResultSuccess;
105}
106
107Result CreateEvent32(Core::System& system, Handle* out_write, Handle* out_read) {
108 return CreateEvent(system, out_write, out_read);
109}
110
111} // namespace Kernel::Svc
diff --git a/src/core/hle/kernel/svc/svc_exception.cpp b/src/core/hle/kernel/svc/svc_exception.cpp
new file mode 100644
index 000000000..fb9f133c1
--- /dev/null
+++ b/src/core/hle/kernel/svc/svc_exception.cpp
@@ -0,0 +1,121 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "core/core.h"
5#include "core/debugger/debugger.h"
6#include "core/hle/kernel/k_thread.h"
7#include "core/hle/kernel/svc.h"
8#include "core/hle/kernel/svc_types.h"
9#include "core/memory.h"
10#include "core/reporter.h"
11
12namespace Kernel::Svc {
13
14/// Break program execution
15void Break(Core::System& system, u32 reason, u64 info1, u64 info2) {
16 BreakReason break_reason =
17 static_cast<BreakReason>(reason & ~static_cast<u32>(BreakReason::NotificationOnlyFlag));
18 bool notification_only = (reason & static_cast<u32>(BreakReason::NotificationOnlyFlag)) != 0;
19
20 bool has_dumped_buffer{};
21 std::vector<u8> debug_buffer;
22
23 const auto handle_debug_buffer = [&](VAddr addr, u64 sz) {
24 if (sz == 0 || addr == 0 || has_dumped_buffer) {
25 return;
26 }
27
28 auto& memory = system.Memory();
29
30 // This typically is an error code so we're going to assume this is the case
31 if (sz == sizeof(u32)) {
32 LOG_CRITICAL(Debug_Emulated, "debug_buffer_err_code={:X}", memory.Read32(addr));
33 } else {
34 // We don't know what's in here so we'll hexdump it
35 debug_buffer.resize(sz);
36 memory.ReadBlock(addr, debug_buffer.data(), sz);
37 std::string hexdump;
38 for (std::size_t i = 0; i < debug_buffer.size(); i++) {
39 hexdump += fmt::format("{:02X} ", debug_buffer[i]);
40 if (i != 0 && i % 16 == 0) {
41 hexdump += '\n';
42 }
43 }
44 LOG_CRITICAL(Debug_Emulated, "debug_buffer=\n{}", hexdump);
45 }
46 has_dumped_buffer = true;
47 };
48 switch (break_reason) {
49 case BreakReason::Panic:
50 LOG_CRITICAL(Debug_Emulated, "Userspace PANIC! info1=0x{:016X}, info2=0x{:016X}", info1,
51 info2);
52 handle_debug_buffer(info1, info2);
53 break;
54 case BreakReason::Assert:
55 LOG_CRITICAL(Debug_Emulated, "Userspace Assertion failed! info1=0x{:016X}, info2=0x{:016X}",
56 info1, info2);
57 handle_debug_buffer(info1, info2);
58 break;
59 case BreakReason::User:
60 LOG_WARNING(Debug_Emulated, "Userspace Break! 0x{:016X} with size 0x{:016X}", info1, info2);
61 handle_debug_buffer(info1, info2);
62 break;
63 case BreakReason::PreLoadDll:
64 LOG_INFO(Debug_Emulated,
65 "Userspace Attempting to load an NRO at 0x{:016X} with size 0x{:016X}", info1,
66 info2);
67 break;
68 case BreakReason::PostLoadDll:
69 LOG_INFO(Debug_Emulated, "Userspace Loaded an NRO at 0x{:016X} with size 0x{:016X}", info1,
70 info2);
71 break;
72 case BreakReason::PreUnloadDll:
73 LOG_INFO(Debug_Emulated,
74 "Userspace Attempting to unload an NRO at 0x{:016X} with size 0x{:016X}", info1,
75 info2);
76 break;
77 case BreakReason::PostUnloadDll:
78 LOG_INFO(Debug_Emulated, "Userspace Unloaded an NRO at 0x{:016X} with size 0x{:016X}",
79 info1, info2);
80 break;
81 case BreakReason::CppException:
82 LOG_CRITICAL(Debug_Emulated, "Signalling debugger. Uncaught C++ exception encountered.");
83 break;
84 default:
85 LOG_WARNING(
86 Debug_Emulated,
87 "Signalling debugger, Unknown break reason {:#X}, info1=0x{:016X}, info2=0x{:016X}",
88 reason, info1, info2);
89 handle_debug_buffer(info1, info2);
90 break;
91 }
92
93 system.GetReporter().SaveSvcBreakReport(reason, notification_only, info1, info2,
94 has_dumped_buffer ? std::make_optional(debug_buffer)
95 : std::nullopt);
96
97 if (!notification_only) {
98 LOG_CRITICAL(
99 Debug_Emulated,
100 "Emulated program broke execution! reason=0x{:016X}, info1=0x{:016X}, info2=0x{:016X}",
101 reason, info1, info2);
102
103 handle_debug_buffer(info1, info2);
104
105 auto* const current_thread = GetCurrentThreadPointer(system.Kernel());
106 const auto thread_processor_id = current_thread->GetActiveCore();
107 system.ArmInterface(static_cast<std::size_t>(thread_processor_id)).LogBacktrace();
108 }
109
110 if (system.DebuggerEnabled()) {
111 auto* thread = system.Kernel().GetCurrentEmuThread();
112 system.GetDebugger().NotifyThreadStopped(thread);
113 thread->RequestSuspend(Kernel::SuspendType::Debug);
114 }
115}
116
117void Break32(Core::System& system, u32 reason, u32 info1, u32 info2) {
118 Break(system, reason, info1, info2);
119}
120
121} // namespace Kernel::Svc
diff --git a/src/core/hle/kernel/svc/svc_info.cpp b/src/core/hle/kernel/svc/svc_info.cpp
new file mode 100644
index 000000000..df5dd85a4
--- /dev/null
+++ b/src/core/hle/kernel/svc/svc_info.cpp
@@ -0,0 +1,282 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "core/core.h"
5#include "core/core_timing.h"
6#include "core/hle/kernel/k_process.h"
7#include "core/hle/kernel/k_resource_limit.h"
8#include "core/hle/kernel/svc.h"
9
10namespace Kernel::Svc {
11
12/// Gets system/memory information for the current process
13Result GetInfo(Core::System& system, u64* result, u64 info_id, Handle handle, u64 info_sub_id) {
14 LOG_TRACE(Kernel_SVC, "called info_id=0x{:X}, info_sub_id=0x{:X}, handle=0x{:08X}", info_id,
15 info_sub_id, handle);
16
17 const auto info_id_type = static_cast<InfoType>(info_id);
18
19 switch (info_id_type) {
20 case InfoType::CoreMask:
21 case InfoType::PriorityMask:
22 case InfoType::AliasRegionAddress:
23 case InfoType::AliasRegionSize:
24 case InfoType::HeapRegionAddress:
25 case InfoType::HeapRegionSize:
26 case InfoType::AslrRegionAddress:
27 case InfoType::AslrRegionSize:
28 case InfoType::StackRegionAddress:
29 case InfoType::StackRegionSize:
30 case InfoType::TotalMemorySize:
31 case InfoType::UsedMemorySize:
32 case InfoType::SystemResourceSizeTotal:
33 case InfoType::SystemResourceSizeUsed:
34 case InfoType::ProgramId:
35 case InfoType::UserExceptionContextAddress:
36 case InfoType::TotalNonSystemMemorySize:
37 case InfoType::UsedNonSystemMemorySize:
38 case InfoType::IsApplication:
39 case InfoType::FreeThreadCount: {
40 if (info_sub_id != 0) {
41 LOG_ERROR(Kernel_SVC, "Info sub id is non zero! info_id={}, info_sub_id={}", info_id,
42 info_sub_id);
43 return ResultInvalidEnumValue;
44 }
45
46 const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable();
47 KScopedAutoObject process = handle_table.GetObject<KProcess>(handle);
48 if (process.IsNull()) {
49 LOG_ERROR(Kernel_SVC, "Process is not valid! info_id={}, info_sub_id={}, handle={:08X}",
50 info_id, info_sub_id, handle);
51 return ResultInvalidHandle;
52 }
53
54 switch (info_id_type) {
55 case InfoType::CoreMask:
56 *result = process->GetCoreMask();
57 return ResultSuccess;
58
59 case InfoType::PriorityMask:
60 *result = process->GetPriorityMask();
61 return ResultSuccess;
62
63 case InfoType::AliasRegionAddress:
64 *result = process->PageTable().GetAliasRegionStart();
65 return ResultSuccess;
66
67 case InfoType::AliasRegionSize:
68 *result = process->PageTable().GetAliasRegionSize();
69 return ResultSuccess;
70
71 case InfoType::HeapRegionAddress:
72 *result = process->PageTable().GetHeapRegionStart();
73 return ResultSuccess;
74
75 case InfoType::HeapRegionSize:
76 *result = process->PageTable().GetHeapRegionSize();
77 return ResultSuccess;
78
79 case InfoType::AslrRegionAddress:
80 *result = process->PageTable().GetAliasCodeRegionStart();
81 return ResultSuccess;
82
83 case InfoType::AslrRegionSize:
84 *result = process->PageTable().GetAliasCodeRegionSize();
85 return ResultSuccess;
86
87 case InfoType::StackRegionAddress:
88 *result = process->PageTable().GetStackRegionStart();
89 return ResultSuccess;
90
91 case InfoType::StackRegionSize:
92 *result = process->PageTable().GetStackRegionSize();
93 return ResultSuccess;
94
95 case InfoType::TotalMemorySize:
96 *result = process->GetTotalPhysicalMemoryAvailable();
97 return ResultSuccess;
98
99 case InfoType::UsedMemorySize:
100 *result = process->GetTotalPhysicalMemoryUsed();
101 return ResultSuccess;
102
103 case InfoType::SystemResourceSizeTotal:
104 *result = process->GetSystemResourceSize();
105 return ResultSuccess;
106
107 case InfoType::SystemResourceSizeUsed:
108 LOG_WARNING(Kernel_SVC, "(STUBBED) Attempted to query system resource usage");
109 *result = process->GetSystemResourceUsage();
110 return ResultSuccess;
111
112 case InfoType::ProgramId:
113 *result = process->GetProgramID();
114 return ResultSuccess;
115
116 case InfoType::UserExceptionContextAddress:
117 *result = process->GetProcessLocalRegionAddress();
118 return ResultSuccess;
119
120 case InfoType::TotalNonSystemMemorySize:
121 *result = process->GetTotalPhysicalMemoryAvailableWithoutSystemResource();
122 return ResultSuccess;
123
124 case InfoType::UsedNonSystemMemorySize:
125 *result = process->GetTotalPhysicalMemoryUsedWithoutSystemResource();
126 return ResultSuccess;
127
128 case InfoType::FreeThreadCount:
129 *result = process->GetFreeThreadCount();
130 return ResultSuccess;
131
132 default:
133 break;
134 }
135
136 LOG_ERROR(Kernel_SVC, "Unimplemented svcGetInfo id=0x{:016X}", info_id);
137 return ResultInvalidEnumValue;
138 }
139
140 case InfoType::DebuggerAttached:
141 *result = 0;
142 return ResultSuccess;
143
144 case InfoType::ResourceLimit: {
145 if (handle != 0) {
146 LOG_ERROR(Kernel, "Handle is non zero! handle={:08X}", handle);
147 return ResultInvalidHandle;
148 }
149
150 if (info_sub_id != 0) {
151 LOG_ERROR(Kernel, "Info sub id is non zero! info_id={}, info_sub_id={}", info_id,
152 info_sub_id);
153 return ResultInvalidCombination;
154 }
155
156 KProcess* const current_process = system.Kernel().CurrentProcess();
157 KHandleTable& handle_table = current_process->GetHandleTable();
158 const auto resource_limit = current_process->GetResourceLimit();
159 if (!resource_limit) {
160 *result = Svc::InvalidHandle;
161 // Yes, the kernel considers this a successful operation.
162 return ResultSuccess;
163 }
164
165 Handle resource_handle{};
166 R_TRY(handle_table.Add(&resource_handle, resource_limit));
167
168 *result = resource_handle;
169 return ResultSuccess;
170 }
171
172 case InfoType::RandomEntropy:
173 if (handle != 0) {
174 LOG_ERROR(Kernel_SVC, "Process Handle is non zero, expected 0 result but got {:016X}",
175 handle);
176 return ResultInvalidHandle;
177 }
178
179 if (info_sub_id >= KProcess::RANDOM_ENTROPY_SIZE) {
180 LOG_ERROR(Kernel_SVC, "Entropy size is out of range, expected {} but got {}",
181 KProcess::RANDOM_ENTROPY_SIZE, info_sub_id);
182 return ResultInvalidCombination;
183 }
184
185 *result = system.Kernel().CurrentProcess()->GetRandomEntropy(info_sub_id);
186 return ResultSuccess;
187
188 case InfoType::InitialProcessIdRange:
189 LOG_WARNING(Kernel_SVC,
190 "(STUBBED) Attempted to query privileged process id bounds, returned 0");
191 *result = 0;
192 return ResultSuccess;
193
194 case InfoType::ThreadTickCount: {
195 constexpr u64 num_cpus = 4;
196 if (info_sub_id != 0xFFFFFFFFFFFFFFFF && info_sub_id >= num_cpus) {
197 LOG_ERROR(Kernel_SVC, "Core count is out of range, expected {} but got {}", num_cpus,
198 info_sub_id);
199 return ResultInvalidCombination;
200 }
201
202 KScopedAutoObject thread =
203 system.Kernel().CurrentProcess()->GetHandleTable().GetObject<KThread>(
204 static_cast<Handle>(handle));
205 if (thread.IsNull()) {
206 LOG_ERROR(Kernel_SVC, "Thread handle does not exist, handle=0x{:08X}",
207 static_cast<Handle>(handle));
208 return ResultInvalidHandle;
209 }
210
211 const auto& core_timing = system.CoreTiming();
212 const auto& scheduler = *system.Kernel().CurrentScheduler();
213 const auto* const current_thread = GetCurrentThreadPointer(system.Kernel());
214 const bool same_thread = current_thread == thread.GetPointerUnsafe();
215
216 const u64 prev_ctx_ticks = scheduler.GetLastContextSwitchTime();
217 u64 out_ticks = 0;
218 if (same_thread && info_sub_id == 0xFFFFFFFFFFFFFFFF) {
219 const u64 thread_ticks = current_thread->GetCpuTime();
220
221 out_ticks = thread_ticks + (core_timing.GetCPUTicks() - prev_ctx_ticks);
222 } else if (same_thread && info_sub_id == system.Kernel().CurrentPhysicalCoreIndex()) {
223 out_ticks = core_timing.GetCPUTicks() - prev_ctx_ticks;
224 }
225
226 *result = out_ticks;
227 return ResultSuccess;
228 }
229 case InfoType::IdleTickCount: {
230 // Verify the input handle is invalid.
231 R_UNLESS(handle == InvalidHandle, ResultInvalidHandle);
232
233 // Verify the requested core is valid.
234 const bool core_valid =
235 (info_sub_id == 0xFFFFFFFFFFFFFFFF) ||
236 (info_sub_id == static_cast<u64>(system.Kernel().CurrentPhysicalCoreIndex()));
237 R_UNLESS(core_valid, ResultInvalidCombination);
238
239 // Get the idle tick count.
240 *result = system.Kernel().CurrentScheduler()->GetIdleThread()->GetCpuTime();
241 return ResultSuccess;
242 }
243 case InfoType::MesosphereCurrentProcess: {
244 // Verify the input handle is invalid.
245 R_UNLESS(handle == InvalidHandle, ResultInvalidHandle);
246
247 // Verify the sub-type is valid.
248 R_UNLESS(info_sub_id == 0, ResultInvalidCombination);
249
250 // Get the handle table.
251 KProcess* current_process = system.Kernel().CurrentProcess();
252 KHandleTable& handle_table = current_process->GetHandleTable();
253
254 // Get a new handle for the current process.
255 Handle tmp;
256 R_TRY(handle_table.Add(&tmp, current_process));
257
258 // Set the output.
259 *result = tmp;
260
261 // We succeeded.
262 return ResultSuccess;
263 }
264 default:
265 LOG_ERROR(Kernel_SVC, "Unimplemented svcGetInfo id=0x{:016X}", info_id);
266 return ResultInvalidEnumValue;
267 }
268}
269
270Result GetInfo32(Core::System& system, u32* result_low, u32* result_high, u32 sub_id_low,
271 u32 info_id, u32 handle, u32 sub_id_high) {
272 const u64 sub_id{u64{sub_id_low} | (u64{sub_id_high} << 32)};
273 u64 res_value{};
274
275 const Result result{GetInfo(system, &res_value, info_id, handle, sub_id)};
276 *result_high = static_cast<u32>(res_value >> 32);
277 *result_low = static_cast<u32>(res_value & std::numeric_limits<u32>::max());
278
279 return result;
280}
281
282} // namespace Kernel::Svc
diff --git a/src/core/hle/kernel/svc/svc_interrupt_event.cpp b/src/core/hle/kernel/svc/svc_interrupt_event.cpp
new file mode 100644
index 000000000..299e22ae6
--- /dev/null
+++ b/src/core/hle/kernel/svc/svc_interrupt_event.cpp
@@ -0,0 +1,6 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "core/hle/kernel/svc.h"
5
6namespace Kernel::Svc {} // namespace Kernel::Svc
diff --git a/src/core/hle/kernel/svc/svc_io_pool.cpp b/src/core/hle/kernel/svc/svc_io_pool.cpp
new file mode 100644
index 000000000..299e22ae6
--- /dev/null
+++ b/src/core/hle/kernel/svc/svc_io_pool.cpp
@@ -0,0 +1,6 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "core/hle/kernel/svc.h"
5
6namespace Kernel::Svc {} // namespace Kernel::Svc
diff --git a/src/core/hle/kernel/svc/svc_ipc.cpp b/src/core/hle/kernel/svc/svc_ipc.cpp
new file mode 100644
index 000000000..dbb68e89a
--- /dev/null
+++ b/src/core/hle/kernel/svc/svc_ipc.cpp
@@ -0,0 +1,89 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "common/scope_exit.h"
5#include "core/core.h"
6#include "core/hle/kernel/k_client_session.h"
7#include "core/hle/kernel/k_process.h"
8#include "core/hle/kernel/k_server_session.h"
9#include "core/hle/kernel/svc.h"
10
11namespace Kernel::Svc {
12
13/// Makes a blocking IPC call to a service.
14Result SendSyncRequest(Core::System& system, Handle handle) {
15 auto& kernel = system.Kernel();
16
17 // Get the client session from its handle.
18 KScopedAutoObject session =
19 kernel.CurrentProcess()->GetHandleTable().GetObject<KClientSession>(handle);
20 R_UNLESS(session.IsNotNull(), ResultInvalidHandle);
21
22 LOG_TRACE(Kernel_SVC, "called handle=0x{:08X}({})", handle, session->GetName());
23
24 return session->SendSyncRequest();
25}
26
27Result SendSyncRequest32(Core::System& system, Handle handle) {
28 return SendSyncRequest(system, handle);
29}
30
31Result ReplyAndReceive(Core::System& system, s32* out_index, Handle* handles, s32 num_handles,
32 Handle reply_target, s64 timeout_ns) {
33 auto& kernel = system.Kernel();
34 auto& handle_table = GetCurrentThread(kernel).GetOwnerProcess()->GetHandleTable();
35
36 // Convert handle list to object table.
37 std::vector<KSynchronizationObject*> objs(num_handles);
38 R_UNLESS(
39 handle_table.GetMultipleObjects<KSynchronizationObject>(objs.data(), handles, num_handles),
40 ResultInvalidHandle);
41
42 // Ensure handles are closed when we're done.
43 SCOPE_EXIT({
44 for (auto i = 0; i < num_handles; ++i) {
45 objs[i]->Close();
46 }
47 });
48
49 // Reply to the target, if one is specified.
50 if (reply_target != InvalidHandle) {
51 KScopedAutoObject session = handle_table.GetObject<KServerSession>(reply_target);
52 R_UNLESS(session.IsNotNull(), ResultInvalidHandle);
53
54 // If we fail to reply, we want to set the output index to -1.
55 ON_RESULT_FAILURE {
56 *out_index = -1;
57 };
58
59 // Send the reply.
60 R_TRY(session->SendReply());
61 }
62
63 // Wait for a message.
64 while (true) {
65 // Wait for an object.
66 s32 index;
67 Result result = KSynchronizationObject::Wait(kernel, &index, objs.data(),
68 static_cast<s32>(objs.size()), timeout_ns);
69 if (result == ResultTimedOut) {
70 return result;
71 }
72
73 // Receive the request.
74 if (R_SUCCEEDED(result)) {
75 KServerSession* session = objs[index]->DynamicCast<KServerSession*>();
76 if (session != nullptr) {
77 result = session->ReceiveRequest();
78 if (result == ResultNotFound) {
79 continue;
80 }
81 }
82 }
83
84 *out_index = index;
85 return result;
86 }
87}
88
89} // namespace Kernel::Svc
diff --git a/src/core/hle/kernel/svc/svc_kernel_debug.cpp b/src/core/hle/kernel/svc/svc_kernel_debug.cpp
new file mode 100644
index 000000000..454255e7a
--- /dev/null
+++ b/src/core/hle/kernel/svc/svc_kernel_debug.cpp
@@ -0,0 +1,19 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "core/hle/kernel/svc.h"
5
6namespace Kernel::Svc {
7
8void KernelDebug([[maybe_unused]] Core::System& system, [[maybe_unused]] u32 kernel_debug_type,
9 [[maybe_unused]] u64 param1, [[maybe_unused]] u64 param2,
10 [[maybe_unused]] u64 param3) {
11 // Intentionally do nothing, as this does nothing in released kernel binaries.
12}
13
14void ChangeKernelTraceState([[maybe_unused]] Core::System& system,
15 [[maybe_unused]] u32 trace_state) {
16 // Intentionally do nothing, as this does nothing in released kernel binaries.
17}
18
19} // namespace Kernel::Svc
diff --git a/src/core/hle/kernel/svc/svc_light_ipc.cpp b/src/core/hle/kernel/svc/svc_light_ipc.cpp
new file mode 100644
index 000000000..299e22ae6
--- /dev/null
+++ b/src/core/hle/kernel/svc/svc_light_ipc.cpp
@@ -0,0 +1,6 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "core/hle/kernel/svc.h"
5
6namespace Kernel::Svc {} // namespace Kernel::Svc
diff --git a/src/core/hle/kernel/svc/svc_lock.cpp b/src/core/hle/kernel/svc/svc_lock.cpp
new file mode 100644
index 000000000..45f2a6553
--- /dev/null
+++ b/src/core/hle/kernel/svc/svc_lock.cpp
@@ -0,0 +1,57 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "core/core.h"
5#include "core/hle/kernel/k_memory_layout.h"
6#include "core/hle/kernel/k_process.h"
7#include "core/hle/kernel/svc.h"
8
9namespace Kernel::Svc {
10
11/// Attempts to locks a mutex
12Result ArbitrateLock(Core::System& system, Handle thread_handle, VAddr address, u32 tag) {
13 LOG_TRACE(Kernel_SVC, "called thread_handle=0x{:08X}, address=0x{:X}, tag=0x{:08X}",
14 thread_handle, address, tag);
15
16 // Validate the input address.
17 if (IsKernelAddress(address)) {
18 LOG_ERROR(Kernel_SVC, "Attempting to arbitrate a lock on a kernel address (address={:08X})",
19 address);
20 return ResultInvalidCurrentMemory;
21 }
22 if (!Common::IsAligned(address, sizeof(u32))) {
23 LOG_ERROR(Kernel_SVC, "Input address must be 4 byte aligned (address: {:08X})", address);
24 return ResultInvalidAddress;
25 }
26
27 return system.Kernel().CurrentProcess()->WaitForAddress(thread_handle, address, tag);
28}
29
30Result ArbitrateLock32(Core::System& system, Handle thread_handle, u32 address, u32 tag) {
31 return ArbitrateLock(system, thread_handle, address, tag);
32}
33
34/// Unlock a mutex
35Result ArbitrateUnlock(Core::System& system, VAddr address) {
36 LOG_TRACE(Kernel_SVC, "called address=0x{:X}", address);
37
38 // Validate the input address.
39 if (IsKernelAddress(address)) {
40 LOG_ERROR(Kernel_SVC,
41 "Attempting to arbitrate an unlock on a kernel address (address={:08X})",
42 address);
43 return ResultInvalidCurrentMemory;
44 }
45 if (!Common::IsAligned(address, sizeof(u32))) {
46 LOG_ERROR(Kernel_SVC, "Input address must be 4 byte aligned (address: {:08X})", address);
47 return ResultInvalidAddress;
48 }
49
50 return system.Kernel().CurrentProcess()->SignalToAddress(address);
51}
52
53Result ArbitrateUnlock32(Core::System& system, u32 address) {
54 return ArbitrateUnlock(system, address);
55}
56
57} // namespace Kernel::Svc
diff --git a/src/core/hle/kernel/svc/svc_memory.cpp b/src/core/hle/kernel/svc/svc_memory.cpp
new file mode 100644
index 000000000..f78b1239b
--- /dev/null
+++ b/src/core/hle/kernel/svc/svc_memory.cpp
@@ -0,0 +1,189 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "core/core.h"
5#include "core/hle/kernel/k_process.h"
6#include "core/hle/kernel/svc.h"
7
8namespace Kernel::Svc {
9namespace {
10
11constexpr bool IsValidSetMemoryPermission(MemoryPermission perm) {
12 switch (perm) {
13 case MemoryPermission::None:
14 case MemoryPermission::Read:
15 case MemoryPermission::ReadWrite:
16 return true;
17 default:
18 return false;
19 }
20}
21
22// Checks if address + size is greater than the given address
23// This can return false if the size causes an overflow of a 64-bit type
24// or if the given size is zero.
25constexpr bool IsValidAddressRange(VAddr address, u64 size) {
26 return address + size > address;
27}
28
29// Helper function that performs the common sanity checks for svcMapMemory
30// and svcUnmapMemory. This is doable, as both functions perform their sanitizing
31// in the same order.
32Result MapUnmapMemorySanityChecks(const KPageTable& manager, VAddr dst_addr, VAddr src_addr,
33 u64 size) {
34 if (!Common::Is4KBAligned(dst_addr)) {
35 LOG_ERROR(Kernel_SVC, "Destination address is not aligned to 4KB, 0x{:016X}", dst_addr);
36 return ResultInvalidAddress;
37 }
38
39 if (!Common::Is4KBAligned(src_addr)) {
40 LOG_ERROR(Kernel_SVC, "Source address is not aligned to 4KB, 0x{:016X}", src_addr);
41 return ResultInvalidSize;
42 }
43
44 if (size == 0) {
45 LOG_ERROR(Kernel_SVC, "Size is 0");
46 return ResultInvalidSize;
47 }
48
49 if (!Common::Is4KBAligned(size)) {
50 LOG_ERROR(Kernel_SVC, "Size is not aligned to 4KB, 0x{:016X}", size);
51 return ResultInvalidSize;
52 }
53
54 if (!IsValidAddressRange(dst_addr, size)) {
55 LOG_ERROR(Kernel_SVC,
56 "Destination is not a valid address range, addr=0x{:016X}, size=0x{:016X}",
57 dst_addr, size);
58 return ResultInvalidCurrentMemory;
59 }
60
61 if (!IsValidAddressRange(src_addr, size)) {
62 LOG_ERROR(Kernel_SVC, "Source is not a valid address range, addr=0x{:016X}, size=0x{:016X}",
63 src_addr, size);
64 return ResultInvalidCurrentMemory;
65 }
66
67 if (!manager.IsInsideAddressSpace(src_addr, size)) {
68 LOG_ERROR(Kernel_SVC,
69 "Source is not within the address space, addr=0x{:016X}, size=0x{:016X}",
70 src_addr, size);
71 return ResultInvalidCurrentMemory;
72 }
73
74 if (manager.IsOutsideStackRegion(dst_addr, size)) {
75 LOG_ERROR(Kernel_SVC,
76 "Destination is not within the stack region, addr=0x{:016X}, size=0x{:016X}",
77 dst_addr, size);
78 return ResultInvalidMemoryRegion;
79 }
80
81 if (manager.IsInsideHeapRegion(dst_addr, size)) {
82 LOG_ERROR(Kernel_SVC,
83 "Destination does not fit within the heap region, addr=0x{:016X}, "
84 "size=0x{:016X}",
85 dst_addr, size);
86 return ResultInvalidMemoryRegion;
87 }
88
89 if (manager.IsInsideAliasRegion(dst_addr, size)) {
90 LOG_ERROR(Kernel_SVC,
91 "Destination does not fit within the map region, addr=0x{:016X}, "
92 "size=0x{:016X}",
93 dst_addr, size);
94 return ResultInvalidMemoryRegion;
95 }
96
97 return ResultSuccess;
98}
99
100} // namespace
101
102Result SetMemoryPermission(Core::System& system, VAddr address, u64 size, MemoryPermission perm) {
103 LOG_DEBUG(Kernel_SVC, "called, address=0x{:016X}, size=0x{:X}, perm=0x{:08X", address, size,
104 perm);
105
106 // Validate address / size.
107 R_UNLESS(Common::IsAligned(address, PageSize), ResultInvalidAddress);
108 R_UNLESS(Common::IsAligned(size, PageSize), ResultInvalidSize);
109 R_UNLESS(size > 0, ResultInvalidSize);
110 R_UNLESS((address < address + size), ResultInvalidCurrentMemory);
111
112 // Validate the permission.
113 R_UNLESS(IsValidSetMemoryPermission(perm), ResultInvalidNewMemoryPermission);
114
115 // Validate that the region is in range for the current process.
116 auto& page_table = system.Kernel().CurrentProcess()->PageTable();
117 R_UNLESS(page_table.Contains(address, size), ResultInvalidCurrentMemory);
118
119 // Set the memory attribute.
120 return page_table.SetMemoryPermission(address, size, perm);
121}
122
123Result SetMemoryAttribute(Core::System& system, VAddr address, u64 size, u32 mask, u32 attr) {
124 LOG_DEBUG(Kernel_SVC,
125 "called, address=0x{:016X}, size=0x{:X}, mask=0x{:08X}, attribute=0x{:08X}", address,
126 size, mask, attr);
127
128 // Validate address / size.
129 R_UNLESS(Common::IsAligned(address, PageSize), ResultInvalidAddress);
130 R_UNLESS(Common::IsAligned(size, PageSize), ResultInvalidSize);
131 R_UNLESS(size > 0, ResultInvalidSize);
132 R_UNLESS((address < address + size), ResultInvalidCurrentMemory);
133
134 // Validate the attribute and mask.
135 constexpr u32 SupportedMask = static_cast<u32>(MemoryAttribute::Uncached);
136 R_UNLESS((mask | attr) == mask, ResultInvalidCombination);
137 R_UNLESS((mask | attr | SupportedMask) == SupportedMask, ResultInvalidCombination);
138
139 // Validate that the region is in range for the current process.
140 auto& page_table{system.Kernel().CurrentProcess()->PageTable()};
141 R_UNLESS(page_table.Contains(address, size), ResultInvalidCurrentMemory);
142
143 // Set the memory attribute.
144 return page_table.SetMemoryAttribute(address, size, mask, attr);
145}
146
147Result SetMemoryAttribute32(Core::System& system, u32 address, u32 size, u32 mask, u32 attr) {
148 return SetMemoryAttribute(system, address, size, mask, attr);
149}
150
151/// Maps a memory range into a different range.
152Result MapMemory(Core::System& system, VAddr dst_addr, VAddr src_addr, u64 size) {
153 LOG_TRACE(Kernel_SVC, "called, dst_addr=0x{:X}, src_addr=0x{:X}, size=0x{:X}", dst_addr,
154 src_addr, size);
155
156 auto& page_table{system.Kernel().CurrentProcess()->PageTable()};
157
158 if (const Result result{MapUnmapMemorySanityChecks(page_table, dst_addr, src_addr, size)};
159 result.IsError()) {
160 return result;
161 }
162
163 return page_table.MapMemory(dst_addr, src_addr, size);
164}
165
166Result MapMemory32(Core::System& system, u32 dst_addr, u32 src_addr, u32 size) {
167 return MapMemory(system, dst_addr, src_addr, size);
168}
169
170/// Unmaps a region that was previously mapped with svcMapMemory
171Result UnmapMemory(Core::System& system, VAddr dst_addr, VAddr src_addr, u64 size) {
172 LOG_TRACE(Kernel_SVC, "called, dst_addr=0x{:X}, src_addr=0x{:X}, size=0x{:X}", dst_addr,
173 src_addr, size);
174
175 auto& page_table{system.Kernel().CurrentProcess()->PageTable()};
176
177 if (const Result result{MapUnmapMemorySanityChecks(page_table, dst_addr, src_addr, size)};
178 result.IsError()) {
179 return result;
180 }
181
182 return page_table.UnmapMemory(dst_addr, src_addr, size);
183}
184
185Result UnmapMemory32(Core::System& system, u32 dst_addr, u32 src_addr, u32 size) {
186 return UnmapMemory(system, dst_addr, src_addr, size);
187}
188
189} // namespace Kernel::Svc
diff --git a/src/core/hle/kernel/svc/svc_physical_memory.cpp b/src/core/hle/kernel/svc/svc_physical_memory.cpp
new file mode 100644
index 000000000..0fc262203
--- /dev/null
+++ b/src/core/hle/kernel/svc/svc_physical_memory.cpp
@@ -0,0 +1,137 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "core/core.h"
5#include "core/hle/kernel/k_process.h"
6#include "core/hle/kernel/svc.h"
7
8namespace Kernel::Svc {
9
10/// Set the process heap to a given Size. It can both extend and shrink the heap.
11Result SetHeapSize(Core::System& system, VAddr* out_address, u64 size) {
12 LOG_TRACE(Kernel_SVC, "called, heap_size=0x{:X}", size);
13
14 // Validate size.
15 R_UNLESS(Common::IsAligned(size, HeapSizeAlignment), ResultInvalidSize);
16 R_UNLESS(size < MainMemorySizeMax, ResultInvalidSize);
17
18 // Set the heap size.
19 R_TRY(system.Kernel().CurrentProcess()->PageTable().SetHeapSize(out_address, size));
20
21 return ResultSuccess;
22}
23
24Result SetHeapSize32(Core::System& system, u32* heap_addr, u32 heap_size) {
25 VAddr temp_heap_addr{};
26 const Result result{SetHeapSize(system, &temp_heap_addr, heap_size)};
27 *heap_addr = static_cast<u32>(temp_heap_addr);
28 return result;
29}
30
31/// Maps memory at a desired address
32Result MapPhysicalMemory(Core::System& system, VAddr addr, u64 size) {
33 LOG_DEBUG(Kernel_SVC, "called, addr=0x{:016X}, size=0x{:X}", addr, size);
34
35 if (!Common::Is4KBAligned(addr)) {
36 LOG_ERROR(Kernel_SVC, "Address is not aligned to 4KB, 0x{:016X}", addr);
37 return ResultInvalidAddress;
38 }
39
40 if (!Common::Is4KBAligned(size)) {
41 LOG_ERROR(Kernel_SVC, "Size is not aligned to 4KB, 0x{:X}", size);
42 return ResultInvalidSize;
43 }
44
45 if (size == 0) {
46 LOG_ERROR(Kernel_SVC, "Size is zero");
47 return ResultInvalidSize;
48 }
49
50 if (!(addr < addr + size)) {
51 LOG_ERROR(Kernel_SVC, "Size causes 64-bit overflow of address");
52 return ResultInvalidMemoryRegion;
53 }
54
55 KProcess* const current_process{system.Kernel().CurrentProcess()};
56 auto& page_table{current_process->PageTable()};
57
58 if (current_process->GetSystemResourceSize() == 0) {
59 LOG_ERROR(Kernel_SVC, "System Resource Size is zero");
60 return ResultInvalidState;
61 }
62
63 if (!page_table.IsInsideAddressSpace(addr, size)) {
64 LOG_ERROR(Kernel_SVC,
65 "Address is not within the address space, addr=0x{:016X}, size=0x{:016X}", addr,
66 size);
67 return ResultInvalidMemoryRegion;
68 }
69
70 if (page_table.IsOutsideAliasRegion(addr, size)) {
71 LOG_ERROR(Kernel_SVC,
72 "Address is not within the alias region, addr=0x{:016X}, size=0x{:016X}", addr,
73 size);
74 return ResultInvalidMemoryRegion;
75 }
76
77 return page_table.MapPhysicalMemory(addr, size);
78}
79
80Result MapPhysicalMemory32(Core::System& system, u32 addr, u32 size) {
81 return MapPhysicalMemory(system, addr, size);
82}
83
84/// Unmaps memory previously mapped via MapPhysicalMemory
85Result UnmapPhysicalMemory(Core::System& system, VAddr addr, u64 size) {
86 LOG_DEBUG(Kernel_SVC, "called, addr=0x{:016X}, size=0x{:X}", addr, size);
87
88 if (!Common::Is4KBAligned(addr)) {
89 LOG_ERROR(Kernel_SVC, "Address is not aligned to 4KB, 0x{:016X}", addr);
90 return ResultInvalidAddress;
91 }
92
93 if (!Common::Is4KBAligned(size)) {
94 LOG_ERROR(Kernel_SVC, "Size is not aligned to 4KB, 0x{:X}", size);
95 return ResultInvalidSize;
96 }
97
98 if (size == 0) {
99 LOG_ERROR(Kernel_SVC, "Size is zero");
100 return ResultInvalidSize;
101 }
102
103 if (!(addr < addr + size)) {
104 LOG_ERROR(Kernel_SVC, "Size causes 64-bit overflow of address");
105 return ResultInvalidMemoryRegion;
106 }
107
108 KProcess* const current_process{system.Kernel().CurrentProcess()};
109 auto& page_table{current_process->PageTable()};
110
111 if (current_process->GetSystemResourceSize() == 0) {
112 LOG_ERROR(Kernel_SVC, "System Resource Size is zero");
113 return ResultInvalidState;
114 }
115
116 if (!page_table.IsInsideAddressSpace(addr, size)) {
117 LOG_ERROR(Kernel_SVC,
118 "Address is not within the address space, addr=0x{:016X}, size=0x{:016X}", addr,
119 size);
120 return ResultInvalidMemoryRegion;
121 }
122
123 if (page_table.IsOutsideAliasRegion(addr, size)) {
124 LOG_ERROR(Kernel_SVC,
125 "Address is not within the alias region, addr=0x{:016X}, size=0x{:016X}", addr,
126 size);
127 return ResultInvalidMemoryRegion;
128 }
129
130 return page_table.UnmapPhysicalMemory(addr, size);
131}
132
133Result UnmapPhysicalMemory32(Core::System& system, u32 addr, u32 size) {
134 return UnmapPhysicalMemory(system, addr, size);
135}
136
137} // namespace Kernel::Svc
diff --git a/src/core/hle/kernel/svc/svc_port.cpp b/src/core/hle/kernel/svc/svc_port.cpp
new file mode 100644
index 000000000..cdfe0dd16
--- /dev/null
+++ b/src/core/hle/kernel/svc/svc_port.cpp
@@ -0,0 +1,71 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "common/scope_exit.h"
5#include "core/core.h"
6#include "core/hle/kernel/k_client_port.h"
7#include "core/hle/kernel/k_client_session.h"
8#include "core/hle/kernel/k_port.h"
9#include "core/hle/kernel/k_process.h"
10#include "core/hle/kernel/svc.h"
11
12namespace Kernel::Svc {
13
14/// Connect to an OS service given the port name, returns the handle to the port to out
15Result ConnectToNamedPort(Core::System& system, Handle* out, VAddr port_name_address) {
16 auto& memory = system.Memory();
17 if (!memory.IsValidVirtualAddress(port_name_address)) {
18 LOG_ERROR(Kernel_SVC,
19 "Port Name Address is not a valid virtual address, port_name_address=0x{:016X}",
20 port_name_address);
21 return ResultNotFound;
22 }
23
24 static constexpr std::size_t PortNameMaxLength = 11;
25 // Read 1 char beyond the max allowed port name to detect names that are too long.
26 const std::string port_name = memory.ReadCString(port_name_address, PortNameMaxLength + 1);
27 if (port_name.size() > PortNameMaxLength) {
28 LOG_ERROR(Kernel_SVC, "Port name is too long, expected {} but got {}", PortNameMaxLength,
29 port_name.size());
30 return ResultOutOfRange;
31 }
32
33 LOG_TRACE(Kernel_SVC, "called port_name={}", port_name);
34
35 // Get the current handle table.
36 auto& kernel = system.Kernel();
37 auto& handle_table = kernel.CurrentProcess()->GetHandleTable();
38
39 // Find the client port.
40 auto port = kernel.CreateNamedServicePort(port_name);
41 if (!port) {
42 LOG_ERROR(Kernel_SVC, "tried to connect to unknown port: {}", port_name);
43 return ResultNotFound;
44 }
45
46 // Reserve a handle for the port.
47 // NOTE: Nintendo really does write directly to the output handle here.
48 R_TRY(handle_table.Reserve(out));
49 auto handle_guard = SCOPE_GUARD({ handle_table.Unreserve(*out); });
50
51 // Create a session.
52 KClientSession* session{};
53 R_TRY(port->CreateSession(std::addressof(session)));
54
55 kernel.RegisterNamedServiceHandler(port_name, &port->GetParent()->GetServerPort());
56
57 // Register the session in the table, close the extra reference.
58 handle_table.Register(*out, session);
59 session->Close();
60
61 // We succeeded.
62 handle_guard.Cancel();
63 return ResultSuccess;
64}
65
66Result ConnectToNamedPort32(Core::System& system, Handle* out_handle, u32 port_name_address) {
67
68 return ConnectToNamedPort(system, out_handle, port_name_address);
69}
70
71} // namespace Kernel::Svc
diff --git a/src/core/hle/kernel/svc/svc_power_management.cpp b/src/core/hle/kernel/svc/svc_power_management.cpp
new file mode 100644
index 000000000..299e22ae6
--- /dev/null
+++ b/src/core/hle/kernel/svc/svc_power_management.cpp
@@ -0,0 +1,6 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "core/hle/kernel/svc.h"
5
6namespace Kernel::Svc {} // namespace Kernel::Svc
diff --git a/src/core/hle/kernel/svc/svc_process.cpp b/src/core/hle/kernel/svc/svc_process.cpp
new file mode 100644
index 000000000..d6c8b4561
--- /dev/null
+++ b/src/core/hle/kernel/svc/svc_process.cpp
@@ -0,0 +1,124 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "core/core.h"
5#include "core/hle/kernel/k_process.h"
6#include "core/hle/kernel/svc.h"
7
8namespace Kernel::Svc {
9
10/// Exits the current process
11void ExitProcess(Core::System& system) {
12 auto* current_process = system.Kernel().CurrentProcess();
13
14 LOG_INFO(Kernel_SVC, "Process {} exiting", current_process->GetProcessID());
15 ASSERT_MSG(current_process->GetState() == KProcess::State::Running,
16 "Process has already exited");
17
18 system.Exit();
19}
20
21void ExitProcess32(Core::System& system) {
22 ExitProcess(system);
23}
24
25/// Gets the ID of the specified process or a specified thread's owning process.
26Result GetProcessId(Core::System& system, u64* out_process_id, Handle handle) {
27 LOG_DEBUG(Kernel_SVC, "called handle=0x{:08X}", handle);
28
29 // Get the object from the handle table.
30 KScopedAutoObject obj =
31 system.Kernel().CurrentProcess()->GetHandleTable().GetObject<KAutoObject>(
32 static_cast<Handle>(handle));
33 R_UNLESS(obj.IsNotNull(), ResultInvalidHandle);
34
35 // Get the process from the object.
36 KProcess* process = nullptr;
37 if (KProcess* p = obj->DynamicCast<KProcess*>(); p != nullptr) {
38 // The object is a process, so we can use it directly.
39 process = p;
40 } else if (KThread* t = obj->DynamicCast<KThread*>(); t != nullptr) {
41 // The object is a thread, so we want to use its parent.
42 process = reinterpret_cast<KThread*>(obj.GetPointerUnsafe())->GetOwnerProcess();
43 } else {
44 // TODO(bunnei): This should also handle debug objects before returning.
45 UNIMPLEMENTED_MSG("Debug objects not implemented");
46 }
47
48 // Make sure the target process exists.
49 R_UNLESS(process != nullptr, ResultInvalidHandle);
50
51 // Get the process id.
52 *out_process_id = process->GetId();
53
54 return ResultSuccess;
55}
56
57Result GetProcessId32(Core::System& system, u32* out_process_id_low, u32* out_process_id_high,
58 Handle handle) {
59 u64 out_process_id{};
60 const auto result = GetProcessId(system, &out_process_id, handle);
61 *out_process_id_low = static_cast<u32>(out_process_id);
62 *out_process_id_high = static_cast<u32>(out_process_id >> 32);
63 return result;
64}
65
66Result GetProcessList(Core::System& system, u32* out_num_processes, VAddr out_process_ids,
67 u32 out_process_ids_size) {
68 LOG_DEBUG(Kernel_SVC, "called. out_process_ids=0x{:016X}, out_process_ids_size={}",
69 out_process_ids, out_process_ids_size);
70
71 // If the supplied size is negative or greater than INT32_MAX / sizeof(u64), bail.
72 if ((out_process_ids_size & 0xF0000000) != 0) {
73 LOG_ERROR(Kernel_SVC,
74 "Supplied size outside [0, 0x0FFFFFFF] range. out_process_ids_size={}",
75 out_process_ids_size);
76 return ResultOutOfRange;
77 }
78
79 const auto& kernel = system.Kernel();
80 const auto total_copy_size = out_process_ids_size * sizeof(u64);
81
82 if (out_process_ids_size > 0 && !kernel.CurrentProcess()->PageTable().IsInsideAddressSpace(
83 out_process_ids, total_copy_size)) {
84 LOG_ERROR(Kernel_SVC, "Address range outside address space. begin=0x{:016X}, end=0x{:016X}",
85 out_process_ids, out_process_ids + total_copy_size);
86 return ResultInvalidCurrentMemory;
87 }
88
89 auto& memory = system.Memory();
90 const auto& process_list = kernel.GetProcessList();
91 const auto num_processes = process_list.size();
92 const auto copy_amount = std::min(std::size_t{out_process_ids_size}, num_processes);
93
94 for (std::size_t i = 0; i < copy_amount; ++i) {
95 memory.Write64(out_process_ids, process_list[i]->GetProcessID());
96 out_process_ids += sizeof(u64);
97 }
98
99 *out_num_processes = static_cast<u32>(num_processes);
100 return ResultSuccess;
101}
102
103Result GetProcessInfo(Core::System& system, u64* out, Handle process_handle, u32 type) {
104 LOG_DEBUG(Kernel_SVC, "called, handle=0x{:08X}, type=0x{:X}", process_handle, type);
105
106 const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable();
107 KScopedAutoObject process = handle_table.GetObject<KProcess>(process_handle);
108 if (process.IsNull()) {
109 LOG_ERROR(Kernel_SVC, "Process handle does not exist, process_handle=0x{:08X}",
110 process_handle);
111 return ResultInvalidHandle;
112 }
113
114 const auto info_type = static_cast<ProcessInfoType>(type);
115 if (info_type != ProcessInfoType::ProcessState) {
116 LOG_ERROR(Kernel_SVC, "Expected info_type to be ProcessState but got {} instead", type);
117 return ResultInvalidEnumValue;
118 }
119
120 *out = static_cast<u64>(process->GetState());
121 return ResultSuccess;
122}
123
124} // namespace Kernel::Svc
diff --git a/src/core/hle/kernel/svc/svc_process_memory.cpp b/src/core/hle/kernel/svc/svc_process_memory.cpp
new file mode 100644
index 000000000..b6ac43af2
--- /dev/null
+++ b/src/core/hle/kernel/svc/svc_process_memory.cpp
@@ -0,0 +1,274 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "core/core.h"
5#include "core/hle/kernel/k_process.h"
6#include "core/hle/kernel/svc.h"
7
8namespace Kernel::Svc {
9namespace {
10
11constexpr bool IsValidAddressRange(VAddr address, u64 size) {
12 return address + size > address;
13}
14
15constexpr bool IsValidProcessMemoryPermission(Svc::MemoryPermission perm) {
16 switch (perm) {
17 case Svc::MemoryPermission::None:
18 case Svc::MemoryPermission::Read:
19 case Svc::MemoryPermission::ReadWrite:
20 case Svc::MemoryPermission::ReadExecute:
21 return true;
22 default:
23 return false;
24 }
25}
26
27} // namespace
28
29Result SetProcessMemoryPermission(Core::System& system, Handle process_handle, VAddr address,
30 u64 size, Svc::MemoryPermission perm) {
31 LOG_TRACE(Kernel_SVC,
32 "called, process_handle=0x{:X}, addr=0x{:X}, size=0x{:X}, permissions=0x{:08X}",
33 process_handle, address, size, perm);
34
35 // Validate the address/size.
36 R_UNLESS(Common::IsAligned(address, PageSize), ResultInvalidAddress);
37 R_UNLESS(Common::IsAligned(size, PageSize), ResultInvalidSize);
38 R_UNLESS(size > 0, ResultInvalidSize);
39 R_UNLESS((address < address + size), ResultInvalidCurrentMemory);
40 R_UNLESS(address == static_cast<uintptr_t>(address), ResultInvalidCurrentMemory);
41 R_UNLESS(size == static_cast<size_t>(size), ResultInvalidCurrentMemory);
42
43 // Validate the memory permission.
44 R_UNLESS(IsValidProcessMemoryPermission(perm), ResultInvalidNewMemoryPermission);
45
46 // Get the process from its handle.
47 KScopedAutoObject process =
48 system.CurrentProcess()->GetHandleTable().GetObject<KProcess>(process_handle);
49 R_UNLESS(process.IsNotNull(), ResultInvalidHandle);
50
51 // Validate that the address is in range.
52 auto& page_table = process->PageTable();
53 R_UNLESS(page_table.Contains(address, size), ResultInvalidCurrentMemory);
54
55 // Set the memory permission.
56 return page_table.SetProcessMemoryPermission(address, size, perm);
57}
58
59Result MapProcessMemory(Core::System& system, VAddr dst_address, Handle process_handle,
60 VAddr src_address, u64 size) {
61 LOG_TRACE(Kernel_SVC,
62 "called, dst_address=0x{:X}, process_handle=0x{:X}, src_address=0x{:X}, size=0x{:X}",
63 dst_address, process_handle, src_address, size);
64
65 // Validate the address/size.
66 R_UNLESS(Common::IsAligned(dst_address, PageSize), ResultInvalidAddress);
67 R_UNLESS(Common::IsAligned(src_address, PageSize), ResultInvalidAddress);
68 R_UNLESS(Common::IsAligned(size, PageSize), ResultInvalidSize);
69 R_UNLESS(size > 0, ResultInvalidSize);
70 R_UNLESS((dst_address < dst_address + size), ResultInvalidCurrentMemory);
71 R_UNLESS((src_address < src_address + size), ResultInvalidCurrentMemory);
72
73 // Get the processes.
74 KProcess* dst_process = system.CurrentProcess();
75 KScopedAutoObject src_process =
76 dst_process->GetHandleTable().GetObjectWithoutPseudoHandle<KProcess>(process_handle);
77 R_UNLESS(src_process.IsNotNull(), ResultInvalidHandle);
78
79 // Get the page tables.
80 auto& dst_pt = dst_process->PageTable();
81 auto& src_pt = src_process->PageTable();
82
83 // Validate that the mapping is in range.
84 R_UNLESS(src_pt.Contains(src_address, size), ResultInvalidCurrentMemory);
85 R_UNLESS(dst_pt.CanContain(dst_address, size, KMemoryState::SharedCode),
86 ResultInvalidMemoryRegion);
87
88 // Create a new page group.
89 KPageGroup pg{system.Kernel(), dst_pt.GetBlockInfoManager()};
90 R_TRY(src_pt.MakeAndOpenPageGroup(
91 std::addressof(pg), src_address, size / PageSize, KMemoryState::FlagCanMapProcess,
92 KMemoryState::FlagCanMapProcess, KMemoryPermission::None, KMemoryPermission::None,
93 KMemoryAttribute::All, KMemoryAttribute::None));
94
95 // Map the group.
96 R_TRY(dst_pt.MapPageGroup(dst_address, pg, KMemoryState::SharedCode,
97 KMemoryPermission::UserReadWrite));
98
99 return ResultSuccess;
100}
101
102Result UnmapProcessMemory(Core::System& system, VAddr dst_address, Handle process_handle,
103 VAddr src_address, u64 size) {
104 LOG_TRACE(Kernel_SVC,
105 "called, dst_address=0x{:X}, process_handle=0x{:X}, src_address=0x{:X}, size=0x{:X}",
106 dst_address, process_handle, src_address, size);
107
108 // Validate the address/size.
109 R_UNLESS(Common::IsAligned(dst_address, PageSize), ResultInvalidAddress);
110 R_UNLESS(Common::IsAligned(src_address, PageSize), ResultInvalidAddress);
111 R_UNLESS(Common::IsAligned(size, PageSize), ResultInvalidSize);
112 R_UNLESS(size > 0, ResultInvalidSize);
113 R_UNLESS((dst_address < dst_address + size), ResultInvalidCurrentMemory);
114 R_UNLESS((src_address < src_address + size), ResultInvalidCurrentMemory);
115
116 // Get the processes.
117 KProcess* dst_process = system.CurrentProcess();
118 KScopedAutoObject src_process =
119 dst_process->GetHandleTable().GetObjectWithoutPseudoHandle<KProcess>(process_handle);
120 R_UNLESS(src_process.IsNotNull(), ResultInvalidHandle);
121
122 // Get the page tables.
123 auto& dst_pt = dst_process->PageTable();
124 auto& src_pt = src_process->PageTable();
125
126 // Validate that the mapping is in range.
127 R_UNLESS(src_pt.Contains(src_address, size), ResultInvalidCurrentMemory);
128 R_UNLESS(dst_pt.CanContain(dst_address, size, KMemoryState::SharedCode),
129 ResultInvalidMemoryRegion);
130
131 // Unmap the memory.
132 R_TRY(dst_pt.UnmapProcessMemory(dst_address, size, src_pt, src_address));
133
134 return ResultSuccess;
135}
136
137Result MapProcessCodeMemory(Core::System& system, Handle process_handle, u64 dst_address,
138 u64 src_address, u64 size) {
139 LOG_DEBUG(Kernel_SVC,
140 "called. process_handle=0x{:08X}, dst_address=0x{:016X}, "
141 "src_address=0x{:016X}, size=0x{:016X}",
142 process_handle, dst_address, src_address, size);
143
144 if (!Common::Is4KBAligned(src_address)) {
145 LOG_ERROR(Kernel_SVC, "src_address is not page-aligned (src_address=0x{:016X}).",
146 src_address);
147 return ResultInvalidAddress;
148 }
149
150 if (!Common::Is4KBAligned(dst_address)) {
151 LOG_ERROR(Kernel_SVC, "dst_address is not page-aligned (dst_address=0x{:016X}).",
152 dst_address);
153 return ResultInvalidAddress;
154 }
155
156 if (size == 0 || !Common::Is4KBAligned(size)) {
157 LOG_ERROR(Kernel_SVC, "Size is zero or not page-aligned (size=0x{:016X})", size);
158 return ResultInvalidSize;
159 }
160
161 if (!IsValidAddressRange(dst_address, size)) {
162 LOG_ERROR(Kernel_SVC,
163 "Destination address range overflows the address space (dst_address=0x{:016X}, "
164 "size=0x{:016X}).",
165 dst_address, size);
166 return ResultInvalidCurrentMemory;
167 }
168
169 if (!IsValidAddressRange(src_address, size)) {
170 LOG_ERROR(Kernel_SVC,
171 "Source address range overflows the address space (src_address=0x{:016X}, "
172 "size=0x{:016X}).",
173 src_address, size);
174 return ResultInvalidCurrentMemory;
175 }
176
177 const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable();
178 KScopedAutoObject process = handle_table.GetObject<KProcess>(process_handle);
179 if (process.IsNull()) {
180 LOG_ERROR(Kernel_SVC, "Invalid process handle specified (handle=0x{:08X}).",
181 process_handle);
182 return ResultInvalidHandle;
183 }
184
185 auto& page_table = process->PageTable();
186 if (!page_table.IsInsideAddressSpace(src_address, size)) {
187 LOG_ERROR(Kernel_SVC,
188 "Source address range is not within the address space (src_address=0x{:016X}, "
189 "size=0x{:016X}).",
190 src_address, size);
191 return ResultInvalidCurrentMemory;
192 }
193
194 if (!page_table.IsInsideASLRRegion(dst_address, size)) {
195 LOG_ERROR(Kernel_SVC,
196 "Destination address range is not within the ASLR region (dst_address=0x{:016X}, "
197 "size=0x{:016X}).",
198 dst_address, size);
199 return ResultInvalidMemoryRegion;
200 }
201
202 return page_table.MapCodeMemory(dst_address, src_address, size);
203}
204
205Result UnmapProcessCodeMemory(Core::System& system, Handle process_handle, u64 dst_address,
206 u64 src_address, u64 size) {
207 LOG_DEBUG(Kernel_SVC,
208 "called. process_handle=0x{:08X}, dst_address=0x{:016X}, src_address=0x{:016X}, "
209 "size=0x{:016X}",
210 process_handle, dst_address, src_address, size);
211
212 if (!Common::Is4KBAligned(dst_address)) {
213 LOG_ERROR(Kernel_SVC, "dst_address is not page-aligned (dst_address=0x{:016X}).",
214 dst_address);
215 return ResultInvalidAddress;
216 }
217
218 if (!Common::Is4KBAligned(src_address)) {
219 LOG_ERROR(Kernel_SVC, "src_address is not page-aligned (src_address=0x{:016X}).",
220 src_address);
221 return ResultInvalidAddress;
222 }
223
224 if (size == 0 || !Common::Is4KBAligned(size)) {
225 LOG_ERROR(Kernel_SVC, "Size is zero or not page-aligned (size=0x{:016X}).", size);
226 return ResultInvalidSize;
227 }
228
229 if (!IsValidAddressRange(dst_address, size)) {
230 LOG_ERROR(Kernel_SVC,
231 "Destination address range overflows the address space (dst_address=0x{:016X}, "
232 "size=0x{:016X}).",
233 dst_address, size);
234 return ResultInvalidCurrentMemory;
235 }
236
237 if (!IsValidAddressRange(src_address, size)) {
238 LOG_ERROR(Kernel_SVC,
239 "Source address range overflows the address space (src_address=0x{:016X}, "
240 "size=0x{:016X}).",
241 src_address, size);
242 return ResultInvalidCurrentMemory;
243 }
244
245 const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable();
246 KScopedAutoObject process = handle_table.GetObject<KProcess>(process_handle);
247 if (process.IsNull()) {
248 LOG_ERROR(Kernel_SVC, "Invalid process handle specified (handle=0x{:08X}).",
249 process_handle);
250 return ResultInvalidHandle;
251 }
252
253 auto& page_table = process->PageTable();
254 if (!page_table.IsInsideAddressSpace(src_address, size)) {
255 LOG_ERROR(Kernel_SVC,
256 "Source address range is not within the address space (src_address=0x{:016X}, "
257 "size=0x{:016X}).",
258 src_address, size);
259 return ResultInvalidCurrentMemory;
260 }
261
262 if (!page_table.IsInsideASLRRegion(dst_address, size)) {
263 LOG_ERROR(Kernel_SVC,
264 "Destination address range is not within the ASLR region (dst_address=0x{:016X}, "
265 "size=0x{:016X}).",
266 dst_address, size);
267 return ResultInvalidMemoryRegion;
268 }
269
270 return page_table.UnmapCodeMemory(dst_address, src_address, size,
271 KPageTable::ICacheInvalidationStrategy::InvalidateAll);
272}
273
274} // namespace Kernel::Svc
diff --git a/src/core/hle/kernel/svc/svc_processor.cpp b/src/core/hle/kernel/svc/svc_processor.cpp
new file mode 100644
index 000000000..8561cf74f
--- /dev/null
+++ b/src/core/hle/kernel/svc/svc_processor.cpp
@@ -0,0 +1,21 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "common/logging/log.h"
5#include "core/core.h"
6#include "core/hle/kernel/physical_core.h"
7#include "core/hle/kernel/svc.h"
8
9namespace Kernel::Svc {
10
11/// Get which CPU core is executing the current thread
12u32 GetCurrentProcessorNumber(Core::System& system) {
13 LOG_TRACE(Kernel_SVC, "called");
14 return static_cast<u32>(system.CurrentPhysicalCore().CoreIndex());
15}
16
17u32 GetCurrentProcessorNumber32(Core::System& system) {
18 return GetCurrentProcessorNumber(system);
19}
20
21} // namespace Kernel::Svc
diff --git a/src/core/hle/kernel/svc/svc_query_memory.cpp b/src/core/hle/kernel/svc/svc_query_memory.cpp
new file mode 100644
index 000000000..aac3b2eca
--- /dev/null
+++ b/src/core/hle/kernel/svc/svc_query_memory.cpp
@@ -0,0 +1,55 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "core/core.h"
5#include "core/hle/kernel/k_process.h"
6#include "core/hle/kernel/svc.h"
7
8namespace Kernel::Svc {
9
10Result QueryMemory(Core::System& system, VAddr memory_info_address, VAddr page_info_address,
11 VAddr query_address) {
12 LOG_TRACE(Kernel_SVC,
13 "called, memory_info_address=0x{:016X}, page_info_address=0x{:016X}, "
14 "query_address=0x{:016X}",
15 memory_info_address, page_info_address, query_address);
16
17 return QueryProcessMemory(system, memory_info_address, page_info_address, CurrentProcess,
18 query_address);
19}
20
21Result QueryMemory32(Core::System& system, u32 memory_info_address, u32 page_info_address,
22 u32 query_address) {
23 return QueryMemory(system, memory_info_address, page_info_address, query_address);
24}
25
26Result QueryProcessMemory(Core::System& system, VAddr memory_info_address, VAddr page_info_address,
27 Handle process_handle, VAddr address) {
28 LOG_TRACE(Kernel_SVC, "called process=0x{:08X} address={:X}", process_handle, address);
29 const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable();
30 KScopedAutoObject process = handle_table.GetObject<KProcess>(process_handle);
31 if (process.IsNull()) {
32 LOG_ERROR(Kernel_SVC, "Process handle does not exist, process_handle=0x{:08X}",
33 process_handle);
34 return ResultInvalidHandle;
35 }
36
37 auto& memory{system.Memory()};
38 const auto memory_info{process->PageTable().QueryInfo(address).GetSvcMemoryInfo()};
39
40 memory.Write64(memory_info_address + 0x00, memory_info.base_address);
41 memory.Write64(memory_info_address + 0x08, memory_info.size);
42 memory.Write32(memory_info_address + 0x10, static_cast<u32>(memory_info.state) & 0xff);
43 memory.Write32(memory_info_address + 0x14, static_cast<u32>(memory_info.attribute));
44 memory.Write32(memory_info_address + 0x18, static_cast<u32>(memory_info.permission));
45 memory.Write32(memory_info_address + 0x1c, memory_info.ipc_count);
46 memory.Write32(memory_info_address + 0x20, memory_info.device_count);
47 memory.Write32(memory_info_address + 0x24, 0);
48
49 // Page info appears to be currently unused by the kernel and is always set to zero.
50 memory.Write32(page_info_address, 0);
51
52 return ResultSuccess;
53}
54
55} // namespace Kernel::Svc
diff --git a/src/core/hle/kernel/svc/svc_register.cpp b/src/core/hle/kernel/svc/svc_register.cpp
new file mode 100644
index 000000000..299e22ae6
--- /dev/null
+++ b/src/core/hle/kernel/svc/svc_register.cpp
@@ -0,0 +1,6 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "core/hle/kernel/svc.h"
5
6namespace Kernel::Svc {} // namespace Kernel::Svc
diff --git a/src/core/hle/kernel/svc/svc_resource_limit.cpp b/src/core/hle/kernel/svc/svc_resource_limit.cpp
new file mode 100644
index 000000000..679ba10fa
--- /dev/null
+++ b/src/core/hle/kernel/svc/svc_resource_limit.cpp
@@ -0,0 +1,95 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "common/scope_exit.h"
5#include "core/core.h"
6#include "core/hle/kernel/k_process.h"
7#include "core/hle/kernel/k_resource_limit.h"
8#include "core/hle/kernel/svc.h"
9
10namespace Kernel::Svc {
11
12Result CreateResourceLimit(Core::System& system, Handle* out_handle) {
13 LOG_DEBUG(Kernel_SVC, "called");
14
15 // Create a new resource limit.
16 auto& kernel = system.Kernel();
17 KResourceLimit* resource_limit = KResourceLimit::Create(kernel);
18 R_UNLESS(resource_limit != nullptr, ResultOutOfResource);
19
20 // Ensure we don't leak a reference to the limit.
21 SCOPE_EXIT({ resource_limit->Close(); });
22
23 // Initialize the resource limit.
24 resource_limit->Initialize(&system.CoreTiming());
25
26 // Register the limit.
27 KResourceLimit::Register(kernel, resource_limit);
28
29 // Add the limit to the handle table.
30 R_TRY(kernel.CurrentProcess()->GetHandleTable().Add(out_handle, resource_limit));
31
32 return ResultSuccess;
33}
34
35Result GetResourceLimitLimitValue(Core::System& system, u64* out_limit_value,
36 Handle resource_limit_handle, LimitableResource which) {
37 LOG_DEBUG(Kernel_SVC, "called, resource_limit_handle={:08X}, which={}", resource_limit_handle,
38 which);
39
40 // Validate the resource.
41 R_UNLESS(IsValidResourceType(which), ResultInvalidEnumValue);
42
43 // Get the resource limit.
44 auto& kernel = system.Kernel();
45 KScopedAutoObject resource_limit =
46 kernel.CurrentProcess()->GetHandleTable().GetObject<KResourceLimit>(resource_limit_handle);
47 R_UNLESS(resource_limit.IsNotNull(), ResultInvalidHandle);
48
49 // Get the limit value.
50 *out_limit_value = resource_limit->GetLimitValue(which);
51
52 return ResultSuccess;
53}
54
55Result GetResourceLimitCurrentValue(Core::System& system, u64* out_current_value,
56 Handle resource_limit_handle, LimitableResource which) {
57 LOG_DEBUG(Kernel_SVC, "called, resource_limit_handle={:08X}, which={}", resource_limit_handle,
58 which);
59
60 // Validate the resource.
61 R_UNLESS(IsValidResourceType(which), ResultInvalidEnumValue);
62
63 // Get the resource limit.
64 auto& kernel = system.Kernel();
65 KScopedAutoObject resource_limit =
66 kernel.CurrentProcess()->GetHandleTable().GetObject<KResourceLimit>(resource_limit_handle);
67 R_UNLESS(resource_limit.IsNotNull(), ResultInvalidHandle);
68
69 // Get the current value.
70 *out_current_value = resource_limit->GetCurrentValue(which);
71
72 return ResultSuccess;
73}
74
75Result SetResourceLimitLimitValue(Core::System& system, Handle resource_limit_handle,
76 LimitableResource which, u64 limit_value) {
77 LOG_DEBUG(Kernel_SVC, "called, resource_limit_handle={:08X}, which={}, limit_value={}",
78 resource_limit_handle, which, limit_value);
79
80 // Validate the resource.
81 R_UNLESS(IsValidResourceType(which), ResultInvalidEnumValue);
82
83 // Get the resource limit.
84 auto& kernel = system.Kernel();
85 KScopedAutoObject resource_limit =
86 kernel.CurrentProcess()->GetHandleTable().GetObject<KResourceLimit>(resource_limit_handle);
87 R_UNLESS(resource_limit.IsNotNull(), ResultInvalidHandle);
88
89 // Set the limit value.
90 R_TRY(resource_limit->SetLimitValue(which, limit_value));
91
92 return ResultSuccess;
93}
94
95} // namespace Kernel::Svc
diff --git a/src/core/hle/kernel/svc/svc_secure_monitor_call.cpp b/src/core/hle/kernel/svc/svc_secure_monitor_call.cpp
new file mode 100644
index 000000000..299e22ae6
--- /dev/null
+++ b/src/core/hle/kernel/svc/svc_secure_monitor_call.cpp
@@ -0,0 +1,6 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "core/hle/kernel/svc.h"
5
6namespace Kernel::Svc {} // namespace Kernel::Svc
diff --git a/src/core/hle/kernel/svc/svc_session.cpp b/src/core/hle/kernel/svc/svc_session.cpp
new file mode 100644
index 000000000..dac8ce33c
--- /dev/null
+++ b/src/core/hle/kernel/svc/svc_session.cpp
@@ -0,0 +1,103 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "common/scope_exit.h"
5#include "core/core.h"
6#include "core/hle/kernel/k_process.h"
7#include "core/hle/kernel/k_scoped_resource_reservation.h"
8#include "core/hle/kernel/k_session.h"
9#include "core/hle/kernel/svc.h"
10
11namespace Kernel::Svc {
12namespace {
13
14template <typename T>
15Result CreateSession(Core::System& system, Handle* out_server, Handle* out_client, u64 name) {
16 auto& process = *system.CurrentProcess();
17 auto& handle_table = process.GetHandleTable();
18
19 // Declare the session we're going to allocate.
20 T* session;
21
22 // Reserve a new session from the process resource limit.
23 // FIXME: LimitableResource_SessionCountMax
24 KScopedResourceReservation session_reservation(&process, LimitableResource::SessionCountMax);
25 if (session_reservation.Succeeded()) {
26 session = T::Create(system.Kernel());
27 } else {
28 return ResultLimitReached;
29
30 // // We couldn't reserve a session. Check that we support dynamically expanding the
31 // // resource limit.
32 // R_UNLESS(process.GetResourceLimit() ==
33 // &system.Kernel().GetSystemResourceLimit(), ResultLimitReached);
34 // R_UNLESS(KTargetSystem::IsDynamicResourceLimitsEnabled(), ResultLimitReached());
35
36 // // Try to allocate a session from unused slab memory.
37 // session = T::CreateFromUnusedSlabMemory();
38 // R_UNLESS(session != nullptr, ResultLimitReached);
39 // ON_RESULT_FAILURE { session->Close(); };
40
41 // // If we're creating a KSession, we want to add two KSessionRequests to the heap, to
42 // // prevent request exhaustion.
43 // // NOTE: Nintendo checks if session->DynamicCast<KSession *>() != nullptr, but there's
44 // // no reason to not do this statically.
45 // if constexpr (std::same_as<T, KSession>) {
46 // for (size_t i = 0; i < 2; i++) {
47 // KSessionRequest* request = KSessionRequest::CreateFromUnusedSlabMemory();
48 // R_UNLESS(request != nullptr, ResultLimitReached);
49 // request->Close();
50 // }
51 // }
52
53 // We successfully allocated a session, so add the object we allocated to the resource
54 // limit.
55 // system.Kernel().GetSystemResourceLimit().Reserve(LimitableResource::SessionCountMax, 1);
56 }
57
58 // Check that we successfully created a session.
59 R_UNLESS(session != nullptr, ResultOutOfResource);
60
61 // Initialize the session.
62 session->Initialize(nullptr, fmt::format("{}", name));
63
64 // Commit the session reservation.
65 session_reservation.Commit();
66
67 // Ensure that we clean up the session (and its only references are handle table) on function
68 // end.
69 SCOPE_EXIT({
70 session->GetClientSession().Close();
71 session->GetServerSession().Close();
72 });
73
74 // Register the session.
75 T::Register(system.Kernel(), session);
76
77 // Add the server session to the handle table.
78 R_TRY(handle_table.Add(out_server, &session->GetServerSession()));
79
80 // Add the client session to the handle table.
81 const auto result = handle_table.Add(out_client, &session->GetClientSession());
82
83 if (!R_SUCCEEDED(result)) {
84 // Ensure that we maintaing a clean handle state on exit.
85 handle_table.Remove(*out_server);
86 }
87
88 return result;
89}
90
91} // namespace
92
93Result CreateSession(Core::System& system, Handle* out_server, Handle* out_client, u32 is_light,
94 u64 name) {
95 if (is_light) {
96 // return CreateSession<KLightSession>(system, out_server, out_client, name);
97 return ResultUnknown;
98 } else {
99 return CreateSession<KSession>(system, out_server, out_client, name);
100 }
101}
102
103} // namespace Kernel::Svc
diff --git a/src/core/hle/kernel/svc/svc_shared_memory.cpp b/src/core/hle/kernel/svc/svc_shared_memory.cpp
new file mode 100644
index 000000000..d465bcbe7
--- /dev/null
+++ b/src/core/hle/kernel/svc/svc_shared_memory.cpp
@@ -0,0 +1,106 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "common/scope_exit.h"
5#include "core/core.h"
6#include "core/hle/kernel/k_process.h"
7#include "core/hle/kernel/k_shared_memory.h"
8#include "core/hle/kernel/svc.h"
9
10namespace Kernel::Svc {
11namespace {
12
13constexpr bool IsValidSharedMemoryPermission(MemoryPermission perm) {
14 switch (perm) {
15 case MemoryPermission::Read:
16 case MemoryPermission::ReadWrite:
17 return true;
18 default:
19 return false;
20 }
21}
22
23[[maybe_unused]] constexpr bool IsValidRemoteSharedMemoryPermission(MemoryPermission perm) {
24 return IsValidSharedMemoryPermission(perm) || perm == MemoryPermission::DontCare;
25}
26
27} // namespace
28
29Result MapSharedMemory(Core::System& system, Handle shmem_handle, VAddr address, u64 size,
30 Svc::MemoryPermission map_perm) {
31 LOG_TRACE(Kernel_SVC,
32 "called, shared_memory_handle=0x{:X}, addr=0x{:X}, size=0x{:X}, permissions=0x{:08X}",
33 shmem_handle, address, size, map_perm);
34
35 // Validate the address/size.
36 R_UNLESS(Common::IsAligned(address, PageSize), ResultInvalidAddress);
37 R_UNLESS(Common::IsAligned(size, PageSize), ResultInvalidSize);
38 R_UNLESS(size > 0, ResultInvalidSize);
39 R_UNLESS((address < address + size), ResultInvalidCurrentMemory);
40
41 // Validate the permission.
42 R_UNLESS(IsValidSharedMemoryPermission(map_perm), ResultInvalidNewMemoryPermission);
43
44 // Get the current process.
45 auto& process = *system.Kernel().CurrentProcess();
46 auto& page_table = process.PageTable();
47
48 // Get the shared memory.
49 KScopedAutoObject shmem = process.GetHandleTable().GetObject<KSharedMemory>(shmem_handle);
50 R_UNLESS(shmem.IsNotNull(), ResultInvalidHandle);
51
52 // Verify that the mapping is in range.
53 R_UNLESS(page_table.CanContain(address, size, KMemoryState::Shared), ResultInvalidMemoryRegion);
54
55 // Add the shared memory to the process.
56 R_TRY(process.AddSharedMemory(shmem.GetPointerUnsafe(), address, size));
57
58 // Ensure that we clean up the shared memory if we fail to map it.
59 auto guard =
60 SCOPE_GUARD({ process.RemoveSharedMemory(shmem.GetPointerUnsafe(), address, size); });
61
62 // Map the shared memory.
63 R_TRY(shmem->Map(process, address, size, map_perm));
64
65 // We succeeded.
66 guard.Cancel();
67 return ResultSuccess;
68}
69
70Result MapSharedMemory32(Core::System& system, Handle shmem_handle, u32 address, u32 size,
71 Svc::MemoryPermission map_perm) {
72 return MapSharedMemory(system, shmem_handle, address, size, map_perm);
73}
74
75Result UnmapSharedMemory(Core::System& system, Handle shmem_handle, VAddr address, u64 size) {
76 // Validate the address/size.
77 R_UNLESS(Common::IsAligned(address, PageSize), ResultInvalidAddress);
78 R_UNLESS(Common::IsAligned(size, PageSize), ResultInvalidSize);
79 R_UNLESS(size > 0, ResultInvalidSize);
80 R_UNLESS((address < address + size), ResultInvalidCurrentMemory);
81
82 // Get the current process.
83 auto& process = *system.Kernel().CurrentProcess();
84 auto& page_table = process.PageTable();
85
86 // Get the shared memory.
87 KScopedAutoObject shmem = process.GetHandleTable().GetObject<KSharedMemory>(shmem_handle);
88 R_UNLESS(shmem.IsNotNull(), ResultInvalidHandle);
89
90 // Verify that the mapping is in range.
91 R_UNLESS(page_table.CanContain(address, size, KMemoryState::Shared), ResultInvalidMemoryRegion);
92
93 // Unmap the shared memory.
94 R_TRY(shmem->Unmap(process, address, size));
95
96 // Remove the shared memory from the process.
97 process.RemoveSharedMemory(shmem.GetPointerUnsafe(), address, size);
98
99 return ResultSuccess;
100}
101
102Result UnmapSharedMemory32(Core::System& system, Handle shmem_handle, u32 address, u32 size) {
103 return UnmapSharedMemory(system, shmem_handle, address, size);
104}
105
106} // namespace Kernel::Svc
diff --git a/src/core/hle/kernel/svc/svc_synchronization.cpp b/src/core/hle/kernel/svc/svc_synchronization.cpp
new file mode 100644
index 000000000..1bf6a612a
--- /dev/null
+++ b/src/core/hle/kernel/svc/svc_synchronization.cpp
@@ -0,0 +1,139 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "common/scope_exit.h"
5#include "core/core.h"
6#include "core/hle/kernel/k_process.h"
7#include "core/hle/kernel/k_readable_event.h"
8#include "core/hle/kernel/svc.h"
9
10namespace Kernel::Svc {
11
12/// Close a handle
13Result CloseHandle(Core::System& system, Handle handle) {
14 LOG_TRACE(Kernel_SVC, "Closing handle 0x{:08X}", handle);
15
16 // Remove the handle.
17 R_UNLESS(system.Kernel().CurrentProcess()->GetHandleTable().Remove(handle),
18 ResultInvalidHandle);
19
20 return ResultSuccess;
21}
22
23Result CloseHandle32(Core::System& system, Handle handle) {
24 return CloseHandle(system, handle);
25}
26
27/// Clears the signaled state of an event or process.
28Result ResetSignal(Core::System& system, Handle handle) {
29 LOG_DEBUG(Kernel_SVC, "called handle 0x{:08X}", handle);
30
31 // Get the current handle table.
32 const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable();
33
34 // Try to reset as readable event.
35 {
36 KScopedAutoObject readable_event = handle_table.GetObject<KReadableEvent>(handle);
37 if (readable_event.IsNotNull()) {
38 return readable_event->Reset();
39 }
40 }
41
42 // Try to reset as process.
43 {
44 KScopedAutoObject process = handle_table.GetObject<KProcess>(handle);
45 if (process.IsNotNull()) {
46 return process->Reset();
47 }
48 }
49
50 LOG_ERROR(Kernel_SVC, "invalid handle (0x{:08X})", handle);
51
52 return ResultInvalidHandle;
53}
54
55Result ResetSignal32(Core::System& system, Handle handle) {
56 return ResetSignal(system, handle);
57}
58
59/// Wait for the given handles to synchronize, timeout after the specified nanoseconds
60Result WaitSynchronization(Core::System& system, s32* index, VAddr handles_address, s32 num_handles,
61 s64 nano_seconds) {
62 LOG_TRACE(Kernel_SVC, "called handles_address=0x{:X}, num_handles={}, nano_seconds={}",
63 handles_address, num_handles, nano_seconds);
64
65 // Ensure number of handles is valid.
66 R_UNLESS(0 <= num_handles && num_handles <= ArgumentHandleCountMax, ResultOutOfRange);
67
68 auto& kernel = system.Kernel();
69 std::vector<KSynchronizationObject*> objs(num_handles);
70 const auto& handle_table = kernel.CurrentProcess()->GetHandleTable();
71 Handle* handles = system.Memory().GetPointer<Handle>(handles_address);
72
73 // Copy user handles.
74 if (num_handles > 0) {
75 // Convert the handles to objects.
76 R_UNLESS(handle_table.GetMultipleObjects<KSynchronizationObject>(objs.data(), handles,
77 num_handles),
78 ResultInvalidHandle);
79 for (const auto& obj : objs) {
80 kernel.RegisterInUseObject(obj);
81 }
82 }
83
84 // Ensure handles are closed when we're done.
85 SCOPE_EXIT({
86 for (s32 i = 0; i < num_handles; ++i) {
87 kernel.UnregisterInUseObject(objs[i]);
88 objs[i]->Close();
89 }
90 });
91
92 return KSynchronizationObject::Wait(kernel, index, objs.data(), static_cast<s32>(objs.size()),
93 nano_seconds);
94}
95
96Result WaitSynchronization32(Core::System& system, u32 timeout_low, u32 handles_address,
97 s32 num_handles, u32 timeout_high, s32* index) {
98 const s64 nano_seconds{(static_cast<s64>(timeout_high) << 32) | static_cast<s64>(timeout_low)};
99 return WaitSynchronization(system, index, handles_address, num_handles, nano_seconds);
100}
101
102/// Resumes a thread waiting on WaitSynchronization
103Result CancelSynchronization(Core::System& system, Handle handle) {
104 LOG_TRACE(Kernel_SVC, "called handle=0x{:X}", handle);
105
106 // Get the thread from its handle.
107 KScopedAutoObject thread =
108 system.Kernel().CurrentProcess()->GetHandleTable().GetObject<KThread>(handle);
109 R_UNLESS(thread.IsNotNull(), ResultInvalidHandle);
110
111 // Cancel the thread's wait.
112 thread->WaitCancel();
113 return ResultSuccess;
114}
115
116Result CancelSynchronization32(Core::System& system, Handle handle) {
117 return CancelSynchronization(system, handle);
118}
119
120void SynchronizePreemptionState(Core::System& system) {
121 auto& kernel = system.Kernel();
122
123 // Lock the scheduler.
124 KScopedSchedulerLock sl{kernel};
125
126 // If the current thread is pinned, unpin it.
127 KProcess* cur_process = system.Kernel().CurrentProcess();
128 const auto core_id = GetCurrentCoreId(kernel);
129
130 if (cur_process->GetPinnedThread(core_id) == GetCurrentThreadPointer(kernel)) {
131 // Clear the current thread's interrupt flag.
132 GetCurrentThread(kernel).ClearInterruptFlag();
133
134 // Unpin the current thread.
135 cur_process->UnpinCurrentThread(core_id);
136 }
137}
138
139} // namespace Kernel::Svc
diff --git a/src/core/hle/kernel/svc/svc_thread.cpp b/src/core/hle/kernel/svc/svc_thread.cpp
new file mode 100644
index 000000000..dd9f8e8b1
--- /dev/null
+++ b/src/core/hle/kernel/svc/svc_thread.cpp
@@ -0,0 +1,396 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "common/scope_exit.h"
5#include "core/core.h"
6#include "core/core_timing.h"
7#include "core/hle/kernel/k_process.h"
8#include "core/hle/kernel/k_scoped_resource_reservation.h"
9#include "core/hle/kernel/k_thread.h"
10#include "core/hle/kernel/svc.h"
11
12namespace Kernel::Svc {
13namespace {
14
15constexpr bool IsValidVirtualCoreId(int32_t core_id) {
16 return (0 <= core_id && core_id < static_cast<int32_t>(Core::Hardware::NUM_CPU_CORES));
17}
18
19} // Anonymous namespace
20
21/// Creates a new thread
22Result CreateThread(Core::System& system, Handle* out_handle, VAddr entry_point, u64 arg,
23 VAddr stack_bottom, u32 priority, s32 core_id) {
24 LOG_DEBUG(Kernel_SVC,
25 "called entry_point=0x{:08X}, arg=0x{:08X}, stack_bottom=0x{:08X}, "
26 "priority=0x{:08X}, core_id=0x{:08X}",
27 entry_point, arg, stack_bottom, priority, core_id);
28
29 // Adjust core id, if it's the default magic.
30 auto& kernel = system.Kernel();
31 auto& process = *kernel.CurrentProcess();
32 if (core_id == IdealCoreUseProcessValue) {
33 core_id = process.GetIdealCoreId();
34 }
35
36 // Validate arguments.
37 if (!IsValidVirtualCoreId(core_id)) {
38 LOG_ERROR(Kernel_SVC, "Invalid Core ID specified (id={})", core_id);
39 return ResultInvalidCoreId;
40 }
41 if (((1ULL << core_id) & process.GetCoreMask()) == 0) {
42 LOG_ERROR(Kernel_SVC, "Core ID doesn't fall within allowable cores (id={})", core_id);
43 return ResultInvalidCoreId;
44 }
45
46 if (HighestThreadPriority > priority || priority > LowestThreadPriority) {
47 LOG_ERROR(Kernel_SVC, "Invalid priority specified (priority={})", priority);
48 return ResultInvalidPriority;
49 }
50 if (!process.CheckThreadPriority(priority)) {
51 LOG_ERROR(Kernel_SVC, "Invalid allowable thread priority (priority={})", priority);
52 return ResultInvalidPriority;
53 }
54
55 // Reserve a new thread from the process resource limit (waiting up to 100ms).
56 KScopedResourceReservation thread_reservation(
57 kernel.CurrentProcess(), LimitableResource::ThreadCountMax, 1,
58 system.CoreTiming().GetGlobalTimeNs().count() + 100000000);
59 if (!thread_reservation.Succeeded()) {
60 LOG_ERROR(Kernel_SVC, "Could not reserve a new thread");
61 return ResultLimitReached;
62 }
63
64 // Create the thread.
65 KThread* thread = KThread::Create(kernel);
66 if (!thread) {
67 LOG_ERROR(Kernel_SVC, "Unable to create new threads. Thread creation limit reached.");
68 return ResultOutOfResource;
69 }
70 SCOPE_EXIT({ thread->Close(); });
71
72 // Initialize the thread.
73 {
74 KScopedLightLock lk{process.GetStateLock()};
75 R_TRY(KThread::InitializeUserThread(system, thread, entry_point, arg, stack_bottom,
76 priority, core_id, &process));
77 }
78
79 // Set the thread name for debugging purposes.
80 thread->SetName(fmt::format("thread[entry_point={:X}, handle={:X}]", entry_point, *out_handle));
81
82 // Commit the thread reservation.
83 thread_reservation.Commit();
84
85 // Register the new thread.
86 KThread::Register(kernel, thread);
87
88 // Add the thread to the handle table.
89 R_TRY(process.GetHandleTable().Add(out_handle, thread));
90
91 return ResultSuccess;
92}
93
94Result CreateThread32(Core::System& system, Handle* out_handle, u32 priority, u32 entry_point,
95 u32 arg, u32 stack_top, s32 processor_id) {
96 return CreateThread(system, out_handle, entry_point, arg, stack_top, priority, processor_id);
97}
98
99/// Starts the thread for the provided handle
100Result StartThread(Core::System& system, Handle thread_handle) {
101 LOG_DEBUG(Kernel_SVC, "called thread=0x{:08X}", thread_handle);
102
103 // Get the thread from its handle.
104 KScopedAutoObject thread =
105 system.Kernel().CurrentProcess()->GetHandleTable().GetObject<KThread>(thread_handle);
106 R_UNLESS(thread.IsNotNull(), ResultInvalidHandle);
107
108 // Try to start the thread.
109 R_TRY(thread->Run());
110
111 // If we succeeded, persist a reference to the thread.
112 thread->Open();
113 system.Kernel().RegisterInUseObject(thread.GetPointerUnsafe());
114
115 return ResultSuccess;
116}
117
118Result StartThread32(Core::System& system, Handle thread_handle) {
119 return StartThread(system, thread_handle);
120}
121
122/// Called when a thread exits
123void ExitThread(Core::System& system) {
124 LOG_DEBUG(Kernel_SVC, "called, pc=0x{:08X}", system.CurrentArmInterface().GetPC());
125
126 auto* const current_thread = GetCurrentThreadPointer(system.Kernel());
127 system.GlobalSchedulerContext().RemoveThread(current_thread);
128 current_thread->Exit();
129 system.Kernel().UnregisterInUseObject(current_thread);
130}
131
132void ExitThread32(Core::System& system) {
133 ExitThread(system);
134}
135
136/// Sleep the current thread
137void SleepThread(Core::System& system, s64 nanoseconds) {
138 auto& kernel = system.Kernel();
139 const auto yield_type = static_cast<Svc::YieldType>(nanoseconds);
140
141 LOG_TRACE(Kernel_SVC, "called nanoseconds={}", nanoseconds);
142
143 // When the input tick is positive, sleep.
144 if (nanoseconds > 0) {
145 // Convert the timeout from nanoseconds to ticks.
146 // NOTE: Nintendo does not use this conversion logic in WaitSynchronization...
147
148 // Sleep.
149 // NOTE: Nintendo does not check the result of this sleep.
150 static_cast<void>(GetCurrentThread(kernel).Sleep(nanoseconds));
151 } else if (yield_type == Svc::YieldType::WithoutCoreMigration) {
152 KScheduler::YieldWithoutCoreMigration(kernel);
153 } else if (yield_type == Svc::YieldType::WithCoreMigration) {
154 KScheduler::YieldWithCoreMigration(kernel);
155 } else if (yield_type == Svc::YieldType::ToAnyThread) {
156 KScheduler::YieldToAnyThread(kernel);
157 } else {
158 // Nintendo does nothing at all if an otherwise invalid value is passed.
159 ASSERT_MSG(false, "Unimplemented sleep yield type '{:016X}'!", nanoseconds);
160 }
161}
162
163void SleepThread32(Core::System& system, u32 nanoseconds_low, u32 nanoseconds_high) {
164 const auto nanoseconds = static_cast<s64>(u64{nanoseconds_low} | (u64{nanoseconds_high} << 32));
165 SleepThread(system, nanoseconds);
166}
167
168/// Gets the thread context
169Result GetThreadContext(Core::System& system, VAddr out_context, Handle thread_handle) {
170 LOG_DEBUG(Kernel_SVC, "called, out_context=0x{:08X}, thread_handle=0x{:X}", out_context,
171 thread_handle);
172
173 auto& kernel = system.Kernel();
174
175 // Get the thread from its handle.
176 KScopedAutoObject thread =
177 kernel.CurrentProcess()->GetHandleTable().GetObject<KThread>(thread_handle);
178 R_UNLESS(thread.IsNotNull(), ResultInvalidHandle);
179
180 // Require the handle be to a non-current thread in the current process.
181 const auto* current_process = kernel.CurrentProcess();
182 R_UNLESS(current_process == thread->GetOwnerProcess(), ResultInvalidId);
183
184 // Verify that the thread isn't terminated.
185 R_UNLESS(thread->GetState() != ThreadState::Terminated, ResultTerminationRequested);
186
187 /// Check that the thread is not the current one.
188 /// NOTE: Nintendo does not check this, and thus the following loop will deadlock.
189 R_UNLESS(thread.GetPointerUnsafe() != GetCurrentThreadPointer(kernel), ResultInvalidId);
190
191 // Try to get the thread context until the thread isn't current on any core.
192 while (true) {
193 KScopedSchedulerLock sl{kernel};
194
195 // TODO(bunnei): Enforce that thread is suspended for debug here.
196
197 // If the thread's raw state isn't runnable, check if it's current on some core.
198 if (thread->GetRawState() != ThreadState::Runnable) {
199 bool current = false;
200 for (auto i = 0; i < static_cast<s32>(Core::Hardware::NUM_CPU_CORES); ++i) {
201 if (thread.GetPointerUnsafe() == kernel.Scheduler(i).GetSchedulerCurrentThread()) {
202 current = true;
203 break;
204 }
205 }
206
207 // If the thread is current, retry until it isn't.
208 if (current) {
209 continue;
210 }
211 }
212
213 // Get the thread context.
214 std::vector<u8> context;
215 R_TRY(thread->GetThreadContext3(context));
216
217 // Copy the thread context to user space.
218 system.Memory().WriteBlock(out_context, context.data(), context.size());
219
220 return ResultSuccess;
221 }
222
223 return ResultSuccess;
224}
225
226Result GetThreadContext32(Core::System& system, u32 out_context, Handle thread_handle) {
227 return GetThreadContext(system, out_context, thread_handle);
228}
229
230/// Gets the priority for the specified thread
231Result GetThreadPriority(Core::System& system, u32* out_priority, Handle handle) {
232 LOG_TRACE(Kernel_SVC, "called");
233
234 // Get the thread from its handle.
235 KScopedAutoObject thread =
236 system.Kernel().CurrentProcess()->GetHandleTable().GetObject<KThread>(handle);
237 R_UNLESS(thread.IsNotNull(), ResultInvalidHandle);
238
239 // Get the thread's priority.
240 *out_priority = thread->GetPriority();
241 return ResultSuccess;
242}
243
244Result GetThreadPriority32(Core::System& system, u32* out_priority, Handle handle) {
245 return GetThreadPriority(system, out_priority, handle);
246}
247
248/// Sets the priority for the specified thread
249Result SetThreadPriority(Core::System& system, Handle thread_handle, u32 priority) {
250 // Get the current process.
251 KProcess& process = *system.Kernel().CurrentProcess();
252
253 // Validate the priority.
254 R_UNLESS(HighestThreadPriority <= priority && priority <= LowestThreadPriority,
255 ResultInvalidPriority);
256 R_UNLESS(process.CheckThreadPriority(priority), ResultInvalidPriority);
257
258 // Get the thread from its handle.
259 KScopedAutoObject thread = process.GetHandleTable().GetObject<KThread>(thread_handle);
260 R_UNLESS(thread.IsNotNull(), ResultInvalidHandle);
261
262 // Set the thread priority.
263 thread->SetBasePriority(priority);
264 return ResultSuccess;
265}
266
267Result SetThreadPriority32(Core::System& system, Handle thread_handle, u32 priority) {
268 return SetThreadPriority(system, thread_handle, priority);
269}
270
271Result GetThreadList(Core::System& system, u32* out_num_threads, VAddr out_thread_ids,
272 u32 out_thread_ids_size, Handle debug_handle) {
273 // TODO: Handle this case when debug events are supported.
274 UNIMPLEMENTED_IF(debug_handle != InvalidHandle);
275
276 LOG_DEBUG(Kernel_SVC, "called. out_thread_ids=0x{:016X}, out_thread_ids_size={}",
277 out_thread_ids, out_thread_ids_size);
278
279 // If the size is negative or larger than INT32_MAX / sizeof(u64)
280 if ((out_thread_ids_size & 0xF0000000) != 0) {
281 LOG_ERROR(Kernel_SVC, "Supplied size outside [0, 0x0FFFFFFF] range. size={}",
282 out_thread_ids_size);
283 return ResultOutOfRange;
284 }
285
286 auto* const current_process = system.Kernel().CurrentProcess();
287 const auto total_copy_size = out_thread_ids_size * sizeof(u64);
288
289 if (out_thread_ids_size > 0 &&
290 !current_process->PageTable().IsInsideAddressSpace(out_thread_ids, total_copy_size)) {
291 LOG_ERROR(Kernel_SVC, "Address range outside address space. begin=0x{:016X}, end=0x{:016X}",
292 out_thread_ids, out_thread_ids + total_copy_size);
293 return ResultInvalidCurrentMemory;
294 }
295
296 auto& memory = system.Memory();
297 const auto& thread_list = current_process->GetThreadList();
298 const auto num_threads = thread_list.size();
299 const auto copy_amount = std::min(std::size_t{out_thread_ids_size}, num_threads);
300
301 auto list_iter = thread_list.cbegin();
302 for (std::size_t i = 0; i < copy_amount; ++i, ++list_iter) {
303 memory.Write64(out_thread_ids, (*list_iter)->GetThreadID());
304 out_thread_ids += sizeof(u64);
305 }
306
307 *out_num_threads = static_cast<u32>(num_threads);
308 return ResultSuccess;
309}
310
311Result GetThreadCoreMask(Core::System& system, Handle thread_handle, s32* out_core_id,
312 u64* out_affinity_mask) {
313 LOG_TRACE(Kernel_SVC, "called, handle=0x{:08X}", thread_handle);
314
315 // Get the thread from its handle.
316 KScopedAutoObject thread =
317 system.Kernel().CurrentProcess()->GetHandleTable().GetObject<KThread>(thread_handle);
318 R_UNLESS(thread.IsNotNull(), ResultInvalidHandle);
319
320 // Get the core mask.
321 R_TRY(thread->GetCoreMask(out_core_id, out_affinity_mask));
322
323 return ResultSuccess;
324}
325
326Result GetThreadCoreMask32(Core::System& system, Handle thread_handle, s32* out_core_id,
327 u32* out_affinity_mask_low, u32* out_affinity_mask_high) {
328 u64 out_affinity_mask{};
329 const auto result = GetThreadCoreMask(system, thread_handle, out_core_id, &out_affinity_mask);
330 *out_affinity_mask_high = static_cast<u32>(out_affinity_mask >> 32);
331 *out_affinity_mask_low = static_cast<u32>(out_affinity_mask);
332 return result;
333}
334
335Result SetThreadCoreMask(Core::System& system, Handle thread_handle, s32 core_id,
336 u64 affinity_mask) {
337 // Determine the core id/affinity mask.
338 if (core_id == IdealCoreUseProcessValue) {
339 core_id = system.Kernel().CurrentProcess()->GetIdealCoreId();
340 affinity_mask = (1ULL << core_id);
341 } else {
342 // Validate the affinity mask.
343 const u64 process_core_mask = system.Kernel().CurrentProcess()->GetCoreMask();
344 R_UNLESS((affinity_mask | process_core_mask) == process_core_mask, ResultInvalidCoreId);
345 R_UNLESS(affinity_mask != 0, ResultInvalidCombination);
346
347 // Validate the core id.
348 if (IsValidVirtualCoreId(core_id)) {
349 R_UNLESS(((1ULL << core_id) & affinity_mask) != 0, ResultInvalidCombination);
350 } else {
351 R_UNLESS(core_id == IdealCoreNoUpdate || core_id == IdealCoreDontCare,
352 ResultInvalidCoreId);
353 }
354 }
355
356 // Get the thread from its handle.
357 KScopedAutoObject thread =
358 system.Kernel().CurrentProcess()->GetHandleTable().GetObject<KThread>(thread_handle);
359 R_UNLESS(thread.IsNotNull(), ResultInvalidHandle);
360
361 // Set the core mask.
362 R_TRY(thread->SetCoreMask(core_id, affinity_mask));
363
364 return ResultSuccess;
365}
366
367Result SetThreadCoreMask32(Core::System& system, Handle thread_handle, s32 core_id,
368 u32 affinity_mask_low, u32 affinity_mask_high) {
369 const auto affinity_mask = u64{affinity_mask_low} | (u64{affinity_mask_high} << 32);
370 return SetThreadCoreMask(system, thread_handle, core_id, affinity_mask);
371}
372
373/// Get the ID for the specified thread.
374Result GetThreadId(Core::System& system, u64* out_thread_id, Handle thread_handle) {
375 // Get the thread from its handle.
376 KScopedAutoObject thread =
377 system.Kernel().CurrentProcess()->GetHandleTable().GetObject<KThread>(thread_handle);
378 R_UNLESS(thread.IsNotNull(), ResultInvalidHandle);
379
380 // Get the thread's id.
381 *out_thread_id = thread->GetId();
382 return ResultSuccess;
383}
384
385Result GetThreadId32(Core::System& system, u32* out_thread_id_low, u32* out_thread_id_high,
386 Handle thread_handle) {
387 u64 out_thread_id{};
388 const Result result{GetThreadId(system, &out_thread_id, thread_handle)};
389
390 *out_thread_id_low = static_cast<u32>(out_thread_id >> 32);
391 *out_thread_id_high = static_cast<u32>(out_thread_id & std::numeric_limits<u32>::max());
392
393 return result;
394}
395
396} // namespace Kernel::Svc
diff --git a/src/core/hle/kernel/svc/svc_thread_profiler.cpp b/src/core/hle/kernel/svc/svc_thread_profiler.cpp
new file mode 100644
index 000000000..299e22ae6
--- /dev/null
+++ b/src/core/hle/kernel/svc/svc_thread_profiler.cpp
@@ -0,0 +1,6 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "core/hle/kernel/svc.h"
5
6namespace Kernel::Svc {} // namespace Kernel::Svc
diff --git a/src/core/hle/kernel/svc/svc_tick.cpp b/src/core/hle/kernel/svc/svc_tick.cpp
new file mode 100644
index 000000000..e9b4fd5a6
--- /dev/null
+++ b/src/core/hle/kernel/svc/svc_tick.cpp
@@ -0,0 +1,33 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "core/core.h"
5#include "core/core_timing.h"
6#include "core/hle/kernel/kernel.h"
7#include "core/hle/kernel/svc.h"
8
9namespace Kernel::Svc {
10
11/// This returns the total CPU ticks elapsed since the CPU was powered-on
12u64 GetSystemTick(Core::System& system) {
13 LOG_TRACE(Kernel_SVC, "called");
14
15 auto& core_timing = system.CoreTiming();
16
17 // Returns the value of cntpct_el0 (https://switchbrew.org/wiki/SVC#svcGetSystemTick)
18 const u64 result{core_timing.GetClockTicks()};
19
20 if (!system.Kernel().IsMulticore()) {
21 core_timing.AddTicks(400U);
22 }
23
24 return result;
25}
26
27void GetSystemTick32(Core::System& system, u32* time_low, u32* time_high) {
28 const auto time = GetSystemTick(system);
29 *time_low = static_cast<u32>(time);
30 *time_high = static_cast<u32>(time >> 32);
31}
32
33} // namespace Kernel::Svc
diff --git a/src/core/hle/kernel/svc/svc_transfer_memory.cpp b/src/core/hle/kernel/svc/svc_transfer_memory.cpp
new file mode 100644
index 000000000..b14ae24a1
--- /dev/null
+++ b/src/core/hle/kernel/svc/svc_transfer_memory.cpp
@@ -0,0 +1,79 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "common/scope_exit.h"
5#include "core/core.h"
6#include "core/hle/kernel/k_process.h"
7#include "core/hle/kernel/k_scoped_resource_reservation.h"
8#include "core/hle/kernel/k_transfer_memory.h"
9#include "core/hle/kernel/svc.h"
10
11namespace Kernel::Svc {
12namespace {
13
14constexpr bool IsValidTransferMemoryPermission(MemoryPermission perm) {
15 switch (perm) {
16 case MemoryPermission::None:
17 case MemoryPermission::Read:
18 case MemoryPermission::ReadWrite:
19 return true;
20 default:
21 return false;
22 }
23}
24
25} // Anonymous namespace
26
27/// Creates a TransferMemory object
28Result CreateTransferMemory(Core::System& system, Handle* out, VAddr address, u64 size,
29 MemoryPermission map_perm) {
30 auto& kernel = system.Kernel();
31
32 // Validate the size.
33 R_UNLESS(Common::IsAligned(address, PageSize), ResultInvalidAddress);
34 R_UNLESS(Common::IsAligned(size, PageSize), ResultInvalidSize);
35 R_UNLESS(size > 0, ResultInvalidSize);
36 R_UNLESS((address < address + size), ResultInvalidCurrentMemory);
37
38 // Validate the permissions.
39 R_UNLESS(IsValidTransferMemoryPermission(map_perm), ResultInvalidNewMemoryPermission);
40
41 // Get the current process and handle table.
42 auto& process = *kernel.CurrentProcess();
43 auto& handle_table = process.GetHandleTable();
44
45 // Reserve a new transfer memory from the process resource limit.
46 KScopedResourceReservation trmem_reservation(kernel.CurrentProcess(),
47 LimitableResource::TransferMemoryCountMax);
48 R_UNLESS(trmem_reservation.Succeeded(), ResultLimitReached);
49
50 // Create the transfer memory.
51 KTransferMemory* trmem = KTransferMemory::Create(kernel);
52 R_UNLESS(trmem != nullptr, ResultOutOfResource);
53
54 // Ensure the only reference is in the handle table when we're done.
55 SCOPE_EXIT({ trmem->Close(); });
56
57 // Ensure that the region is in range.
58 R_UNLESS(process.PageTable().Contains(address, size), ResultInvalidCurrentMemory);
59
60 // Initialize the transfer memory.
61 R_TRY(trmem->Initialize(address, size, map_perm));
62
63 // Commit the reservation.
64 trmem_reservation.Commit();
65
66 // Register the transfer memory.
67 KTransferMemory::Register(kernel, trmem);
68
69 // Add the transfer memory to the handle table.
70 R_TRY(handle_table.Add(out, trmem));
71
72 return ResultSuccess;
73}
74
75Result CreateTransferMemory32(Core::System& system, Handle* out, u32 address, u32 size,
76 MemoryPermission map_perm) {
77 return CreateTransferMemory(system, out, address, size, map_perm);
78}
79} // namespace Kernel::Svc
diff --git a/src/core/hle/kernel/svc_types.h b/src/core/hle/kernel/svc_types.h
index 33eebcef6..e90c35601 100644
--- a/src/core/hle/kernel/svc_types.h
+++ b/src/core/hle/kernel/svc_types.h
@@ -3,6 +3,9 @@
3 3
4#pragma once 4#pragma once
5 5
6#include <bitset>
7
8#include "common/bit_field.h"
6#include "common/common_funcs.h" 9#include "common/common_funcs.h"
7#include "common/common_types.h" 10#include "common/common_types.h"
8 11
@@ -496,6 +499,19 @@ enum class MemoryMapping : u32 {
496 Memory = 2, 499 Memory = 2,
497}; 500};
498 501
502enum class MapDeviceAddressSpaceFlag : u32 {
503 None = (0U << 0),
504 NotIoRegister = (1U << 0),
505};
506DECLARE_ENUM_FLAG_OPERATORS(MapDeviceAddressSpaceFlag);
507
508union MapDeviceAddressSpaceOption {
509 u32 raw;
510 BitField<0, 16, MemoryPermission> permission;
511 BitField<16, 1, MapDeviceAddressSpaceFlag> flags;
512 BitField<17, 15, u32> reserved;
513};
514
499enum class KernelDebugType : u32 { 515enum class KernelDebugType : u32 {
500 Thread = 0, 516 Thread = 0,
501 ThreadCallStack = 1, 517 ThreadCallStack = 1,
@@ -592,4 +608,7 @@ struct CreateProcessParameter {
592}; 608};
593static_assert(sizeof(CreateProcessParameter) == 0x30); 609static_assert(sizeof(CreateProcessParameter) == 0x30);
594 610
611constexpr size_t NumSupervisorCalls = 0xC0;
612using SvcAccessFlagSet = std::bitset<NumSupervisorCalls>;
613
595} // namespace Kernel::Svc 614} // namespace Kernel::Svc
diff --git a/src/core/hle/kernel/svc_version.h b/src/core/hle/kernel/svc_version.h
new file mode 100644
index 000000000..e4f47b34b
--- /dev/null
+++ b/src/core/hle/kernel/svc_version.h
@@ -0,0 +1,58 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include "common/bit_field.h"
7#include "common/common_types.h"
8#include "common/literals.h"
9
10namespace Kernel::Svc {
11
12constexpr inline u32 ConvertToSvcMajorVersion(u32 sdk) {
13 return sdk + 4;
14}
15constexpr inline u32 ConvertToSdkMajorVersion(u32 svc) {
16 return svc - 4;
17}
18
19constexpr inline u32 ConvertToSvcMinorVersion(u32 sdk) {
20 return sdk;
21}
22constexpr inline u32 ConvertToSdkMinorVersion(u32 svc) {
23 return svc;
24}
25
26union KernelVersion {
27 u32 value;
28 BitField<0, 4, u32> minor_version;
29 BitField<4, 13, u32> major_version;
30};
31
32constexpr inline u32 EncodeKernelVersion(u32 major, u32 minor) {
33 return decltype(KernelVersion::minor_version)::FormatValue(minor) |
34 decltype(KernelVersion::major_version)::FormatValue(major);
35}
36
37constexpr inline u32 GetKernelMajorVersion(u32 encoded) {
38 return std::bit_cast<decltype(KernelVersion::major_version)>(encoded).Value();
39}
40
41constexpr inline u32 GetKernelMinorVersion(u32 encoded) {
42 return std::bit_cast<decltype(KernelVersion::minor_version)>(encoded).Value();
43}
44
45// Nintendo doesn't support programs targeting SVC versions < 3.0.
46constexpr inline u32 RequiredKernelMajorVersion = 3;
47constexpr inline u32 RequiredKernelMinorVersion = 0;
48constexpr inline u32 RequiredKernelVersion =
49 EncodeKernelVersion(RequiredKernelMajorVersion, RequiredKernelMinorVersion);
50
51// This is the highest SVC version supported, to be updated on new kernel releases.
52// NOTE: Official kernel versions have SVC major = SDK major + 4, SVC minor = SDK minor.
53constexpr inline u32 SupportedKernelMajorVersion = ConvertToSvcMajorVersion(15);
54constexpr inline u32 SupportedKernelMinorVersion = ConvertToSvcMinorVersion(3);
55constexpr inline u32 SupportedKernelVersion =
56 EncodeKernelVersion(SupportedKernelMajorVersion, SupportedKernelMinorVersion);
57
58} // namespace Kernel::Svc
diff --git a/src/core/hle/kernel/svc_wrap.h b/src/core/hle/kernel/svc_wrap.h
index 1ea8c7fbc..052be40dd 100644
--- a/src/core/hle/kernel/svc_wrap.h
+++ b/src/core/hle/kernel/svc_wrap.h
@@ -172,11 +172,11 @@ void SvcWrap64(Core::System& system) {
172} 172}
173 173
174// Used by GetResourceLimitLimitValue. 174// Used by GetResourceLimitLimitValue.
175template <Result func(Core::System&, u64*, Handle, LimitableResource)> 175template <Result func(Core::System&, u64*, Handle, Svc::LimitableResource)>
176void SvcWrap64(Core::System& system) { 176void SvcWrap64(Core::System& system) {
177 u64 param_1 = 0; 177 u64 param_1 = 0;
178 const u32 retval = func(system, &param_1, static_cast<Handle>(Param(system, 1)), 178 const u32 retval = func(system, &param_1, static_cast<Handle>(Param(system, 1)),
179 static_cast<LimitableResource>(Param(system, 2))) 179 static_cast<Svc::LimitableResource>(Param(system, 2)))
180 .raw; 180 .raw;
181 181
182 system.CurrentArmInterface().SetReg(1, param_1); 182 system.CurrentArmInterface().SetReg(1, param_1);
@@ -189,10 +189,10 @@ void SvcWrap64(Core::System& system) {
189} 189}
190 190
191// Used by SetResourceLimitLimitValue 191// Used by SetResourceLimitLimitValue
192template <Result func(Core::System&, Handle, LimitableResource, u64)> 192template <Result func(Core::System&, Handle, Svc::LimitableResource, u64)>
193void SvcWrap64(Core::System& system) { 193void SvcWrap64(Core::System& system) {
194 FuncReturn(system, func(system, static_cast<Handle>(Param(system, 0)), 194 FuncReturn(system, func(system, static_cast<Handle>(Param(system, 0)),
195 static_cast<LimitableResource>(Param(system, 1)), Param(system, 2)) 195 static_cast<Svc::LimitableResource>(Param(system, 1)), Param(system, 2))
196 .raw); 196 .raw);
197} 197}
198 198
diff --git a/src/core/hle/service/am/am.cpp b/src/core/hle/service/am/am.cpp
index 22999c942..ebcf6e164 100644
--- a/src/core/hle/service/am/am.cpp
+++ b/src/core/hle/service/am/am.cpp
@@ -1124,7 +1124,7 @@ void IStorageAccessor::Write(Kernel::HLERequestContext& ctx) {
1124 IPC::RequestParser rp{ctx}; 1124 IPC::RequestParser rp{ctx};
1125 1125
1126 const u64 offset{rp.Pop<u64>()}; 1126 const u64 offset{rp.Pop<u64>()};
1127 const std::vector<u8> data{ctx.ReadBuffer()}; 1127 const auto data{ctx.ReadBuffer()};
1128 const std::size_t size{std::min<u64>(data.size(), backing.GetSize() - offset)}; 1128 const std::size_t size{std::min<u64>(data.size(), backing.GetSize() - offset)};
1129 1129
1130 LOG_DEBUG(Service_AM, "called, offset={}, size={}", offset, size); 1130 LOG_DEBUG(Service_AM, "called, offset={}, size={}", offset, size);
diff --git a/src/core/hle/service/audio/audren_u.cpp b/src/core/hle/service/audio/audren_u.cpp
index 3a1c231b6..0ee28752c 100644
--- a/src/core/hle/service/audio/audren_u.cpp
+++ b/src/core/hle/service/audio/audren_u.cpp
@@ -112,7 +112,7 @@ private:
112 void RequestUpdate(Kernel::HLERequestContext& ctx) { 112 void RequestUpdate(Kernel::HLERequestContext& ctx) {
113 LOG_TRACE(Service_Audio, "called"); 113 LOG_TRACE(Service_Audio, "called");
114 114
115 std::vector<u8> input{ctx.ReadBuffer(0)}; 115 const auto input{ctx.ReadBuffer(0)};
116 116
117 // These buffers are written manually to avoid an issue with WriteBuffer throwing errors for 117 // These buffers are written manually to avoid an issue with WriteBuffer throwing errors for
118 // checking size 0. Performance size is 0 for most games. 118 // checking size 0. Performance size is 0 for most games.
diff --git a/src/core/hle/service/audio/hwopus.cpp b/src/core/hle/service/audio/hwopus.cpp
index 825fb8bcc..e01f87356 100644
--- a/src/core/hle/service/audio/hwopus.cpp
+++ b/src/core/hle/service/audio/hwopus.cpp
@@ -93,7 +93,7 @@ private:
93 ctx.WriteBuffer(samples); 93 ctx.WriteBuffer(samples);
94 } 94 }
95 95
96 bool DecodeOpusData(u32& consumed, u32& sample_count, const std::vector<u8>& input, 96 bool DecodeOpusData(u32& consumed, u32& sample_count, std::span<const u8> input,
97 std::vector<opus_int16>& output, u64* out_performance_time) const { 97 std::vector<opus_int16>& output, u64* out_performance_time) const {
98 const auto start_time = std::chrono::steady_clock::now(); 98 const auto start_time = std::chrono::steady_clock::now();
99 const std::size_t raw_output_sz = output.size() * sizeof(opus_int16); 99 const std::size_t raw_output_sz = output.size() * sizeof(opus_int16);
diff --git a/src/core/hle/service/es/es.cpp b/src/core/hle/service/es/es.cpp
index d183e5829..fb8686859 100644
--- a/src/core/hle/service/es/es.cpp
+++ b/src/core/hle/service/es/es.cpp
@@ -122,7 +122,7 @@ private:
122 122
123 void ImportTicket(Kernel::HLERequestContext& ctx) { 123 void ImportTicket(Kernel::HLERequestContext& ctx) {
124 const auto ticket = ctx.ReadBuffer(); 124 const auto ticket = ctx.ReadBuffer();
125 const auto cert = ctx.ReadBuffer(1); 125 [[maybe_unused]] const auto cert = ctx.ReadBuffer(1);
126 126
127 if (ticket.size() < sizeof(Core::Crypto::Ticket)) { 127 if (ticket.size() < sizeof(Core::Crypto::Ticket)) {
128 LOG_ERROR(Service_ETicket, "The input buffer is not large enough!"); 128 LOG_ERROR(Service_ETicket, "The input buffer is not large enough!");
diff --git a/src/core/hle/service/filesystem/fsp_srv.cpp b/src/core/hle/service/filesystem/fsp_srv.cpp
index fbb16a7da..447d624e1 100644
--- a/src/core/hle/service/filesystem/fsp_srv.cpp
+++ b/src/core/hle/service/filesystem/fsp_srv.cpp
@@ -190,7 +190,7 @@ private:
190 return; 190 return;
191 } 191 }
192 192
193 const std::vector<u8> data = ctx.ReadBuffer(); 193 const auto data = ctx.ReadBuffer();
194 194
195 ASSERT_MSG( 195 ASSERT_MSG(
196 static_cast<s64>(data.size()) <= length, 196 static_cast<s64>(data.size()) <= length,
@@ -401,11 +401,8 @@ public:
401 } 401 }
402 402
403 void RenameFile(Kernel::HLERequestContext& ctx) { 403 void RenameFile(Kernel::HLERequestContext& ctx) {
404 std::vector<u8> buffer = ctx.ReadBuffer(0); 404 const std::string src_name = Common::StringFromBuffer(ctx.ReadBuffer(0));
405 const std::string src_name = Common::StringFromBuffer(buffer); 405 const std::string dst_name = Common::StringFromBuffer(ctx.ReadBuffer(1));
406
407 buffer = ctx.ReadBuffer(1);
408 const std::string dst_name = Common::StringFromBuffer(buffer);
409 406
410 LOG_DEBUG(Service_FS, "called. file '{}' to file '{}'", src_name, dst_name); 407 LOG_DEBUG(Service_FS, "called. file '{}' to file '{}'", src_name, dst_name);
411 408
@@ -1086,7 +1083,7 @@ void FSP_SRV::GetGlobalAccessLogMode(Kernel::HLERequestContext& ctx) {
1086} 1083}
1087 1084
1088void FSP_SRV::OutputAccessLogToSdCard(Kernel::HLERequestContext& ctx) { 1085void FSP_SRV::OutputAccessLogToSdCard(Kernel::HLERequestContext& ctx) {
1089 const auto raw = ctx.ReadBuffer(); 1086 const auto raw = ctx.ReadBufferCopy();
1090 auto log = Common::StringFromFixedZeroTerminatedBuffer( 1087 auto log = Common::StringFromFixedZeroTerminatedBuffer(
1091 reinterpret_cast<const char*>(raw.data()), raw.size()); 1088 reinterpret_cast<const char*>(raw.data()), raw.size());
1092 1089
diff --git a/src/core/hle/service/glue/arp.cpp b/src/core/hle/service/glue/arp.cpp
index 49b6d45fe..ce21b69e3 100644
--- a/src/core/hle/service/glue/arp.cpp
+++ b/src/core/hle/service/glue/arp.cpp
@@ -228,7 +228,8 @@ private:
228 return; 228 return;
229 } 229 }
230 230
231 control = ctx.ReadBuffer(); 231 // TODO: Can this be a span?
232 control = ctx.ReadBufferCopy();
232 233
233 IPC::ResponseBuilder rb{ctx, 2}; 234 IPC::ResponseBuilder rb{ctx, 2};
234 rb.Push(ResultSuccess); 235 rb.Push(ResultSuccess);
diff --git a/src/core/hle/service/hid/controllers/npad.cpp b/src/core/hle/service/hid/controllers/npad.cpp
index 2f871de31..513ea485a 100644
--- a/src/core/hle/service/hid/controllers/npad.cpp
+++ b/src/core/hle/service/hid/controllers/npad.cpp
@@ -272,6 +272,8 @@ void Controller_NPad::InitNewlyAddedController(Core::HID::NpadIdType npad_id) {
272 } 272 }
273 break; 273 break;
274 case Core::HID::NpadStyleIndex::JoyconLeft: 274 case Core::HID::NpadStyleIndex::JoyconLeft:
275 shared_memory->fullkey_color.attribute = ColorAttribute::Ok;
276 shared_memory->fullkey_color.fullkey = body_colors.left;
275 shared_memory->joycon_color.attribute = ColorAttribute::Ok; 277 shared_memory->joycon_color.attribute = ColorAttribute::Ok;
276 shared_memory->joycon_color.left = body_colors.left; 278 shared_memory->joycon_color.left = body_colors.left;
277 shared_memory->battery_level_dual = battery_level.left.battery_level; 279 shared_memory->battery_level_dual = battery_level.left.battery_level;
@@ -285,6 +287,8 @@ void Controller_NPad::InitNewlyAddedController(Core::HID::NpadIdType npad_id) {
285 shared_memory->sixaxis_left_properties.is_newly_assigned.Assign(1); 287 shared_memory->sixaxis_left_properties.is_newly_assigned.Assign(1);
286 break; 288 break;
287 case Core::HID::NpadStyleIndex::JoyconRight: 289 case Core::HID::NpadStyleIndex::JoyconRight:
290 shared_memory->fullkey_color.attribute = ColorAttribute::Ok;
291 shared_memory->fullkey_color.fullkey = body_colors.right;
288 shared_memory->joycon_color.attribute = ColorAttribute::Ok; 292 shared_memory->joycon_color.attribute = ColorAttribute::Ok;
289 shared_memory->joycon_color.right = body_colors.right; 293 shared_memory->joycon_color.right = body_colors.right;
290 shared_memory->battery_level_right = battery_level.right.battery_level; 294 shared_memory->battery_level_right = battery_level.right.battery_level;
@@ -332,6 +336,20 @@ void Controller_NPad::InitNewlyAddedController(Core::HID::NpadIdType npad_id) {
332 336
333 controller.is_connected = true; 337 controller.is_connected = true;
334 controller.device->Connect(); 338 controller.device->Connect();
339 controller.device->SetLedPattern();
340 if (controller_type == Core::HID::NpadStyleIndex::JoyconDual) {
341 if (controller.is_dual_left_connected) {
342 controller.device->SetPollingMode(Core::HID::EmulatedDeviceIndex::LeftIndex,
343 Common::Input::PollingMode::Active);
344 }
345 if (controller.is_dual_right_connected) {
346 controller.device->SetPollingMode(Core::HID::EmulatedDeviceIndex::RightIndex,
347 Common::Input::PollingMode::Active);
348 }
349 } else {
350 controller.device->SetPollingMode(Core::HID::EmulatedDeviceIndex::AllDevices,
351 Common::Input::PollingMode::Active);
352 }
335 SignalStyleSetChangedEvent(npad_id); 353 SignalStyleSetChangedEvent(npad_id);
336 WriteEmptyEntry(controller.shared_memory); 354 WriteEmptyEntry(controller.shared_memory);
337} 355}
@@ -410,6 +428,9 @@ void Controller_NPad::RequestPadStateUpdate(Core::HID::NpadIdType npad_id) {
410 return; 428 return;
411 } 429 }
412 430
431 // This function is unique to yuzu for the turbo buttons to work properly
432 controller.device->TurboButtonUpdate();
433
413 auto& pad_entry = controller.npad_pad_state; 434 auto& pad_entry = controller.npad_pad_state;
414 auto& trigger_entry = controller.npad_trigger_state; 435 auto& trigger_entry = controller.npad_trigger_state;
415 const auto button_state = controller.device->GetNpadButtons(); 436 const auto button_state = controller.device->GetNpadButtons();
@@ -737,11 +758,12 @@ Core::HID::NpadStyleTag Controller_NPad::GetSupportedStyleSet() const {
737 return hid_core.GetSupportedStyleTag(); 758 return hid_core.GetSupportedStyleTag();
738} 759}
739 760
740void Controller_NPad::SetSupportedNpadIdTypes(u8* data, std::size_t length) { 761void Controller_NPad::SetSupportedNpadIdTypes(std::span<const u8> data) {
762 const auto length = data.size();
741 ASSERT(length > 0 && (length % sizeof(u32)) == 0); 763 ASSERT(length > 0 && (length % sizeof(u32)) == 0);
742 supported_npad_id_types.clear(); 764 supported_npad_id_types.clear();
743 supported_npad_id_types.resize(length / sizeof(u32)); 765 supported_npad_id_types.resize(length / sizeof(u32));
744 std::memcpy(supported_npad_id_types.data(), data, length); 766 std::memcpy(supported_npad_id_types.data(), data.data(), length);
745} 767}
746 768
747void Controller_NPad::GetSupportedNpadIdTypes(u32* data, std::size_t max_length) { 769void Controller_NPad::GetSupportedNpadIdTypes(u32* data, std::size_t max_length) {
diff --git a/src/core/hle/service/hid/controllers/npad.h b/src/core/hle/service/hid/controllers/npad.h
index 1a589cca2..1f7d33459 100644
--- a/src/core/hle/service/hid/controllers/npad.h
+++ b/src/core/hle/service/hid/controllers/npad.h
@@ -6,6 +6,7 @@
6#include <array> 6#include <array>
7#include <atomic> 7#include <atomic>
8#include <mutex> 8#include <mutex>
9#include <span>
9 10
10#include "common/bit_field.h" 11#include "common/bit_field.h"
11#include "common/common_types.h" 12#include "common/common_types.h"
@@ -95,7 +96,7 @@ public:
95 void SetSupportedStyleSet(Core::HID::NpadStyleTag style_set); 96 void SetSupportedStyleSet(Core::HID::NpadStyleTag style_set);
96 Core::HID::NpadStyleTag GetSupportedStyleSet() const; 97 Core::HID::NpadStyleTag GetSupportedStyleSet() const;
97 98
98 void SetSupportedNpadIdTypes(u8* data, std::size_t length); 99 void SetSupportedNpadIdTypes(std::span<const u8> data);
99 void GetSupportedNpadIdTypes(u32* data, std::size_t max_length); 100 void GetSupportedNpadIdTypes(u32* data, std::size_t max_length);
100 std::size_t GetSupportedNpadIdTypesSize() const; 101 std::size_t GetSupportedNpadIdTypesSize() const;
101 102
diff --git a/src/core/hle/service/hid/hid.cpp b/src/core/hle/service/hid/hid.cpp
index bf28440c6..f15f1a6bb 100644
--- a/src/core/hle/service/hid/hid.cpp
+++ b/src/core/hle/service/hid/hid.cpp
@@ -1026,7 +1026,7 @@ void Hid::SetSupportedNpadIdType(Kernel::HLERequestContext& ctx) {
1026 const auto applet_resource_user_id{rp.Pop<u64>()}; 1026 const auto applet_resource_user_id{rp.Pop<u64>()};
1027 1027
1028 applet_resource->GetController<Controller_NPad>(HidController::NPad) 1028 applet_resource->GetController<Controller_NPad>(HidController::NPad)
1029 .SetSupportedNpadIdTypes(ctx.ReadBuffer().data(), ctx.GetReadBufferSize()); 1029 .SetSupportedNpadIdTypes(ctx.ReadBuffer());
1030 1030
1031 LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id); 1031 LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id);
1032 1032
@@ -2104,7 +2104,7 @@ void Hid::WritePalmaRgbLedPatternEntry(Kernel::HLERequestContext& ctx) {
2104 const auto connection_handle{rp.PopRaw<Controller_Palma::PalmaConnectionHandle>()}; 2104 const auto connection_handle{rp.PopRaw<Controller_Palma::PalmaConnectionHandle>()};
2105 const auto unknown{rp.Pop<u64>()}; 2105 const auto unknown{rp.Pop<u64>()};
2106 2106
2107 const auto buffer = ctx.ReadBuffer(); 2107 [[maybe_unused]] const auto buffer = ctx.ReadBuffer();
2108 2108
2109 LOG_WARNING(Service_HID, "(STUBBED) called, connection_handle={}, unknown={}", 2109 LOG_WARNING(Service_HID, "(STUBBED) called, connection_handle={}, unknown={}",
2110 connection_handle.npad_id, unknown); 2110 connection_handle.npad_id, unknown);
diff --git a/src/core/hle/service/hid/hidbus.cpp b/src/core/hle/service/hid/hidbus.cpp
index e5e50845f..17252a84a 100644
--- a/src/core/hle/service/hid/hidbus.cpp
+++ b/src/core/hle/service/hid/hidbus.cpp
@@ -297,13 +297,13 @@ void HidBus::EnableExternalDevice(Kernel::HLERequestContext& ctx) {
297 297
298 const auto parameters{rp.PopRaw<Parameters>()}; 298 const auto parameters{rp.PopRaw<Parameters>()};
299 299
300 LOG_INFO(Service_HID, 300 LOG_DEBUG(Service_HID,
301 "called, enable={}, abstracted_pad_id={}, bus_type={}, internal_index={}, " 301 "called, enable={}, abstracted_pad_id={}, bus_type={}, internal_index={}, "
302 "player_number={}, is_valid={}, inval={}, applet_resource_user_id{}", 302 "player_number={}, is_valid={}, inval={}, applet_resource_user_id{}",
303 parameters.enable, parameters.bus_handle.abstracted_pad_id, 303 parameters.enable, parameters.bus_handle.abstracted_pad_id,
304 parameters.bus_handle.bus_type, parameters.bus_handle.internal_index, 304 parameters.bus_handle.bus_type, parameters.bus_handle.internal_index,
305 parameters.bus_handle.player_number, parameters.bus_handle.is_valid, parameters.inval, 305 parameters.bus_handle.player_number, parameters.bus_handle.is_valid, parameters.inval,
306 parameters.applet_resource_user_id); 306 parameters.applet_resource_user_id);
307 307
308 const auto device_index = GetDeviceIndexFromHandle(parameters.bus_handle); 308 const auto device_index = GetDeviceIndexFromHandle(parameters.bus_handle);
309 309
@@ -326,11 +326,11 @@ void HidBus::GetExternalDeviceId(Kernel::HLERequestContext& ctx) {
326 IPC::RequestParser rp{ctx}; 326 IPC::RequestParser rp{ctx};
327 const auto bus_handle_{rp.PopRaw<BusHandle>()}; 327 const auto bus_handle_{rp.PopRaw<BusHandle>()};
328 328
329 LOG_INFO(Service_HID, 329 LOG_DEBUG(Service_HID,
330 "called, abstracted_pad_id={}, bus_type={}, internal_index={}, player_number={}, " 330 "called, abstracted_pad_id={}, bus_type={}, internal_index={}, player_number={}, "
331 "is_valid={}", 331 "is_valid={}",
332 bus_handle_.abstracted_pad_id, bus_handle_.bus_type, bus_handle_.internal_index, 332 bus_handle_.abstracted_pad_id, bus_handle_.bus_type, bus_handle_.internal_index,
333 bus_handle_.player_number, bus_handle_.is_valid); 333 bus_handle_.player_number, bus_handle_.is_valid);
334 334
335 const auto device_index = GetDeviceIndexFromHandle(bus_handle_); 335 const auto device_index = GetDeviceIndexFromHandle(bus_handle_);
336 336
diff --git a/src/core/hle/service/hid/hidbus/hidbus_base.h b/src/core/hle/service/hid/hidbus/hidbus_base.h
index d3960f506..65e301137 100644
--- a/src/core/hle/service/hid/hidbus/hidbus_base.h
+++ b/src/core/hle/service/hid/hidbus/hidbus_base.h
@@ -4,6 +4,7 @@
4#pragma once 4#pragma once
5 5
6#include <array> 6#include <array>
7#include <span>
7#include "common/common_types.h" 8#include "common/common_types.h"
8#include "core/hle/result.h" 9#include "core/hle/result.h"
9 10
@@ -150,7 +151,7 @@ public:
150 } 151 }
151 152
152 // Assigns a command from data 153 // Assigns a command from data
153 virtual bool SetCommand(const std::vector<u8>& data) { 154 virtual bool SetCommand(std::span<const u8> data) {
154 return {}; 155 return {};
155 } 156 }
156 157
diff --git a/src/core/hle/service/hid/hidbus/ringcon.cpp b/src/core/hle/service/hid/hidbus/ringcon.cpp
index 57f1a2a26..35847cbdd 100644
--- a/src/core/hle/service/hid/hidbus/ringcon.cpp
+++ b/src/core/hle/service/hid/hidbus/ringcon.cpp
@@ -1,7 +1,7 @@
1// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project 1// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later 2// SPDX-License-Identifier: GPL-2.0-or-later
3 3
4#include "core/hid/emulated_devices.h" 4#include "core/hid/emulated_controller.h"
5#include "core/hid/hid_core.h" 5#include "core/hid/hid_core.h"
6#include "core/hle/kernel/k_event.h" 6#include "core/hle/kernel/k_event.h"
7#include "core/hle/kernel/k_readable_event.h" 7#include "core/hle/kernel/k_readable_event.h"
@@ -12,16 +12,20 @@ namespace Service::HID {
12RingController::RingController(Core::HID::HIDCore& hid_core_, 12RingController::RingController(Core::HID::HIDCore& hid_core_,
13 KernelHelpers::ServiceContext& service_context_) 13 KernelHelpers::ServiceContext& service_context_)
14 : HidbusBase(service_context_) { 14 : HidbusBase(service_context_) {
15 input = hid_core_.GetEmulatedDevices(); 15 input = hid_core_.GetEmulatedController(Core::HID::NpadIdType::Player1);
16} 16}
17 17
18RingController::~RingController() = default; 18RingController::~RingController() = default;
19 19
20void RingController::OnInit() { 20void RingController::OnInit() {
21 input->SetPollingMode(Core::HID::EmulatedDeviceIndex::RightIndex,
22 Common::Input::PollingMode::Ring);
21 return; 23 return;
22} 24}
23 25
24void RingController::OnRelease() { 26void RingController::OnRelease() {
27 input->SetPollingMode(Core::HID::EmulatedDeviceIndex::RightIndex,
28 Common::Input::PollingMode::Active);
25 return; 29 return;
26}; 30};
27 31
@@ -112,7 +116,7 @@ std::vector<u8> RingController::GetReply() const {
112 } 116 }
113} 117}
114 118
115bool RingController::SetCommand(const std::vector<u8>& data) { 119bool RingController::SetCommand(std::span<const u8> data) {
116 if (data.size() < 4) { 120 if (data.size() < 4) {
117 LOG_ERROR(Service_HID, "Command size not supported {}", data.size()); 121 LOG_ERROR(Service_HID, "Command size not supported {}", data.size());
118 command = RingConCommands::Error; 122 command = RingConCommands::Error;
diff --git a/src/core/hle/service/hid/hidbus/ringcon.h b/src/core/hle/service/hid/hidbus/ringcon.h
index b37df50ac..c2fb386b1 100644
--- a/src/core/hle/service/hid/hidbus/ringcon.h
+++ b/src/core/hle/service/hid/hidbus/ringcon.h
@@ -4,12 +4,13 @@
4#pragma once 4#pragma once
5 5
6#include <array> 6#include <array>
7#include <span>
7 8
8#include "common/common_types.h" 9#include "common/common_types.h"
9#include "core/hle/service/hid/hidbus/hidbus_base.h" 10#include "core/hle/service/hid/hidbus/hidbus_base.h"
10 11
11namespace Core::HID { 12namespace Core::HID {
12class EmulatedDevices; 13class EmulatedController;
13} // namespace Core::HID 14} // namespace Core::HID
14 15
15namespace Service::HID { 16namespace Service::HID {
@@ -31,7 +32,7 @@ public:
31 u8 GetDeviceId() const override; 32 u8 GetDeviceId() const override;
32 33
33 // Assigns a command from data 34 // Assigns a command from data
34 bool SetCommand(const std::vector<u8>& data) override; 35 bool SetCommand(std::span<const u8> data) override;
35 36
36 // Returns a reply from a command 37 // Returns a reply from a command
37 std::vector<u8> GetReply() const override; 38 std::vector<u8> GetReply() const override;
@@ -248,6 +249,6 @@ private:
248 .zero = {.value = idle_value, .crc = 225}, 249 .zero = {.value = idle_value, .crc = 225},
249 }; 250 };
250 251
251 Core::HID::EmulatedDevices* input; 252 Core::HID::EmulatedController* input;
252}; 253};
253} // namespace Service::HID 254} // namespace Service::HID
diff --git a/src/core/hle/service/hid/hidbus/starlink.cpp b/src/core/hle/service/hid/hidbus/starlink.cpp
index dd439f60a..d0e760314 100644
--- a/src/core/hle/service/hid/hidbus/starlink.cpp
+++ b/src/core/hle/service/hid/hidbus/starlink.cpp
@@ -42,7 +42,7 @@ std::vector<u8> Starlink::GetReply() const {
42 return {}; 42 return {};
43} 43}
44 44
45bool Starlink::SetCommand(const std::vector<u8>& data) { 45bool Starlink::SetCommand(std::span<const u8> data) {
46 LOG_ERROR(Service_HID, "Command not implemented"); 46 LOG_ERROR(Service_HID, "Command not implemented");
47 return false; 47 return false;
48} 48}
diff --git a/src/core/hle/service/hid/hidbus/starlink.h b/src/core/hle/service/hid/hidbus/starlink.h
index 0b1b7ba49..07c800e6e 100644
--- a/src/core/hle/service/hid/hidbus/starlink.h
+++ b/src/core/hle/service/hid/hidbus/starlink.h
@@ -29,7 +29,7 @@ public:
29 u8 GetDeviceId() const override; 29 u8 GetDeviceId() const override;
30 30
31 // Assigns a command from data 31 // Assigns a command from data
32 bool SetCommand(const std::vector<u8>& data) override; 32 bool SetCommand(std::span<const u8> data) override;
33 33
34 // Returns a reply from a command 34 // Returns a reply from a command
35 std::vector<u8> GetReply() const override; 35 std::vector<u8> GetReply() const override;
diff --git a/src/core/hle/service/hid/hidbus/stubbed.cpp b/src/core/hle/service/hid/hidbus/stubbed.cpp
index e477443e3..07632c872 100644
--- a/src/core/hle/service/hid/hidbus/stubbed.cpp
+++ b/src/core/hle/service/hid/hidbus/stubbed.cpp
@@ -43,7 +43,7 @@ std::vector<u8> HidbusStubbed::GetReply() const {
43 return {}; 43 return {};
44} 44}
45 45
46bool HidbusStubbed::SetCommand(const std::vector<u8>& data) { 46bool HidbusStubbed::SetCommand(std::span<const u8> data) {
47 LOG_ERROR(Service_HID, "Command not implemented"); 47 LOG_ERROR(Service_HID, "Command not implemented");
48 return false; 48 return false;
49} 49}
diff --git a/src/core/hle/service/hid/hidbus/stubbed.h b/src/core/hle/service/hid/hidbus/stubbed.h
index 91165ceff..38eaa0ecc 100644
--- a/src/core/hle/service/hid/hidbus/stubbed.h
+++ b/src/core/hle/service/hid/hidbus/stubbed.h
@@ -29,7 +29,7 @@ public:
29 u8 GetDeviceId() const override; 29 u8 GetDeviceId() const override;
30 30
31 // Assigns a command from data 31 // Assigns a command from data
32 bool SetCommand(const std::vector<u8>& data) override; 32 bool SetCommand(std::span<const u8> data) override;
33 33
34 // Returns a reply from a command 34 // Returns a reply from a command
35 std::vector<u8> GetReply() const override; 35 std::vector<u8> GetReply() const override;
diff --git a/src/core/hle/service/hid/irs.cpp b/src/core/hle/service/hid/irs.cpp
index 6a3453457..52f402c56 100644
--- a/src/core/hle/service/hid/irs.cpp
+++ b/src/core/hle/service/hid/irs.cpp
@@ -108,6 +108,8 @@ void IRS::StopImageProcessor(Kernel::HLERequestContext& ctx) {
108 auto result = IsIrCameraHandleValid(parameters.camera_handle); 108 auto result = IsIrCameraHandleValid(parameters.camera_handle);
109 if (result.IsSuccess()) { 109 if (result.IsSuccess()) {
110 // TODO: Stop Image processor 110 // TODO: Stop Image processor
111 npad_device->SetPollingMode(Core::HID::EmulatedDeviceIndex::RightIndex,
112 Common::Input::PollingMode::Active);
111 result = ResultSuccess; 113 result = ResultSuccess;
112 } 114 }
113 115
@@ -139,6 +141,8 @@ void IRS::RunMomentProcessor(Kernel::HLERequestContext& ctx) {
139 MakeProcessor<MomentProcessor>(parameters.camera_handle, device); 141 MakeProcessor<MomentProcessor>(parameters.camera_handle, device);
140 auto& image_transfer_processor = GetProcessor<MomentProcessor>(parameters.camera_handle); 142 auto& image_transfer_processor = GetProcessor<MomentProcessor>(parameters.camera_handle);
141 image_transfer_processor.SetConfig(parameters.processor_config); 143 image_transfer_processor.SetConfig(parameters.processor_config);
144 npad_device->SetPollingMode(Core::HID::EmulatedDeviceIndex::RightIndex,
145 Common::Input::PollingMode::IR);
142 } 146 }
143 147
144 IPC::ResponseBuilder rb{ctx, 2}; 148 IPC::ResponseBuilder rb{ctx, 2};
@@ -170,6 +174,8 @@ void IRS::RunClusteringProcessor(Kernel::HLERequestContext& ctx) {
170 auto& image_transfer_processor = 174 auto& image_transfer_processor =
171 GetProcessor<ClusteringProcessor>(parameters.camera_handle); 175 GetProcessor<ClusteringProcessor>(parameters.camera_handle);
172 image_transfer_processor.SetConfig(parameters.processor_config); 176 image_transfer_processor.SetConfig(parameters.processor_config);
177 npad_device->SetPollingMode(Core::HID::EmulatedDeviceIndex::RightIndex,
178 Common::Input::PollingMode::IR);
173 } 179 }
174 180
175 IPC::ResponseBuilder rb{ctx, 2}; 181 IPC::ResponseBuilder rb{ctx, 2};
@@ -219,6 +225,8 @@ void IRS::RunImageTransferProcessor(Kernel::HLERequestContext& ctx) {
219 GetProcessor<ImageTransferProcessor>(parameters.camera_handle); 225 GetProcessor<ImageTransferProcessor>(parameters.camera_handle);
220 image_transfer_processor.SetConfig(parameters.processor_config); 226 image_transfer_processor.SetConfig(parameters.processor_config);
221 image_transfer_processor.SetTransferMemoryPointer(transfer_memory); 227 image_transfer_processor.SetTransferMemoryPointer(transfer_memory);
228 npad_device->SetPollingMode(Core::HID::EmulatedDeviceIndex::RightIndex,
229 Common::Input::PollingMode::IR);
222 } 230 }
223 231
224 IPC::ResponseBuilder rb{ctx, 2}; 232 IPC::ResponseBuilder rb{ctx, 2};
@@ -294,6 +302,8 @@ void IRS::RunTeraPluginProcessor(Kernel::HLERequestContext& ctx) {
294 auto& image_transfer_processor = 302 auto& image_transfer_processor =
295 GetProcessor<TeraPluginProcessor>(parameters.camera_handle); 303 GetProcessor<TeraPluginProcessor>(parameters.camera_handle);
296 image_transfer_processor.SetConfig(parameters.processor_config); 304 image_transfer_processor.SetConfig(parameters.processor_config);
305 npad_device->SetPollingMode(Core::HID::EmulatedDeviceIndex::RightIndex,
306 Common::Input::PollingMode::IR);
297 } 307 }
298 308
299 IPC::ResponseBuilder rb{ctx, 2}; 309 IPC::ResponseBuilder rb{ctx, 2};
@@ -343,6 +353,8 @@ void IRS::RunPointingProcessor(Kernel::HLERequestContext& ctx) {
343 MakeProcessor<PointingProcessor>(camera_handle, device); 353 MakeProcessor<PointingProcessor>(camera_handle, device);
344 auto& image_transfer_processor = GetProcessor<PointingProcessor>(camera_handle); 354 auto& image_transfer_processor = GetProcessor<PointingProcessor>(camera_handle);
345 image_transfer_processor.SetConfig(processor_config); 355 image_transfer_processor.SetConfig(processor_config);
356 npad_device->SetPollingMode(Core::HID::EmulatedDeviceIndex::RightIndex,
357 Common::Input::PollingMode::IR);
346 } 358 }
347 359
348 IPC::ResponseBuilder rb{ctx, 2}; 360 IPC::ResponseBuilder rb{ctx, 2};
@@ -453,6 +465,8 @@ void IRS::RunImageTransferExProcessor(Kernel::HLERequestContext& ctx) {
453 GetProcessor<ImageTransferProcessor>(parameters.camera_handle); 465 GetProcessor<ImageTransferProcessor>(parameters.camera_handle);
454 image_transfer_processor.SetConfig(parameters.processor_config); 466 image_transfer_processor.SetConfig(parameters.processor_config);
455 image_transfer_processor.SetTransferMemoryPointer(transfer_memory); 467 image_transfer_processor.SetTransferMemoryPointer(transfer_memory);
468 npad_device->SetPollingMode(Core::HID::EmulatedDeviceIndex::RightIndex,
469 Common::Input::PollingMode::IR);
456 } 470 }
457 471
458 IPC::ResponseBuilder rb{ctx, 2}; 472 IPC::ResponseBuilder rb{ctx, 2};
@@ -479,6 +493,8 @@ void IRS::RunIrLedProcessor(Kernel::HLERequestContext& ctx) {
479 MakeProcessor<IrLedProcessor>(camera_handle, device); 493 MakeProcessor<IrLedProcessor>(camera_handle, device);
480 auto& image_transfer_processor = GetProcessor<IrLedProcessor>(camera_handle); 494 auto& image_transfer_processor = GetProcessor<IrLedProcessor>(camera_handle);
481 image_transfer_processor.SetConfig(processor_config); 495 image_transfer_processor.SetConfig(processor_config);
496 npad_device->SetPollingMode(Core::HID::EmulatedDeviceIndex::RightIndex,
497 Common::Input::PollingMode::IR);
482 } 498 }
483 499
484 IPC::ResponseBuilder rb{ctx, 2}; 500 IPC::ResponseBuilder rb{ctx, 2};
@@ -504,6 +520,8 @@ void IRS::StopImageProcessorAsync(Kernel::HLERequestContext& ctx) {
504 auto result = IsIrCameraHandleValid(parameters.camera_handle); 520 auto result = IsIrCameraHandleValid(parameters.camera_handle);
505 if (result.IsSuccess()) { 521 if (result.IsSuccess()) {
506 // TODO: Stop image processor async 522 // TODO: Stop image processor async
523 npad_device->SetPollingMode(Core::HID::EmulatedDeviceIndex::RightIndex,
524 Common::Input::PollingMode::Active);
507 result = ResultSuccess; 525 result = ResultSuccess;
508 } 526 }
509 527
diff --git a/src/core/hle/service/jit/jit.cpp b/src/core/hle/service/jit/jit.cpp
index 8f2920c51..1295a44c7 100644
--- a/src/core/hle/service/jit/jit.cpp
+++ b/src/core/hle/service/jit/jit.cpp
@@ -62,7 +62,7 @@ public:
62 const auto parameters{rp.PopRaw<InputParameters>()}; 62 const auto parameters{rp.PopRaw<InputParameters>()};
63 63
64 // Optional input/output buffers 64 // Optional input/output buffers
65 std::vector<u8> input_buffer{ctx.CanReadBuffer() ? ctx.ReadBuffer() : std::vector<u8>()}; 65 const auto input_buffer{ctx.CanReadBuffer() ? ctx.ReadBuffer() : std::span<const u8>()};
66 std::vector<u8> output_buffer(ctx.CanWriteBuffer() ? ctx.GetWriteBufferSize() : 0); 66 std::vector<u8> output_buffer(ctx.CanWriteBuffer() ? ctx.GetWriteBufferSize() : 0);
67 67
68 // Function call prototype: 68 // Function call prototype:
@@ -132,7 +132,7 @@ public:
132 const auto command{rp.PopRaw<u64>()}; 132 const auto command{rp.PopRaw<u64>()};
133 133
134 // Optional input/output buffers 134 // Optional input/output buffers
135 std::vector<u8> input_buffer{ctx.CanReadBuffer() ? ctx.ReadBuffer() : std::vector<u8>()}; 135 const auto input_buffer{ctx.CanReadBuffer() ? ctx.ReadBuffer() : std::span<const u8>()};
136 std::vector<u8> output_buffer(ctx.CanWriteBuffer() ? ctx.GetWriteBufferSize() : 0); 136 std::vector<u8> output_buffer(ctx.CanWriteBuffer() ? ctx.GetWriteBufferSize() : 0);
137 137
138 // Function call prototype: 138 // Function call prototype:
diff --git a/src/core/hle/service/ldn/ldn.cpp b/src/core/hle/service/ldn/ldn.cpp
index c49c61cff..e5099d61f 100644
--- a/src/core/hle/service/ldn/ldn.cpp
+++ b/src/core/hle/service/ldn/ldn.cpp
@@ -412,7 +412,7 @@ public:
412 } 412 }
413 413
414 void SetAdvertiseData(Kernel::HLERequestContext& ctx) { 414 void SetAdvertiseData(Kernel::HLERequestContext& ctx) {
415 std::vector<u8> read_buffer = ctx.ReadBuffer(); 415 const auto read_buffer = ctx.ReadBuffer();
416 416
417 IPC::ResponseBuilder rb{ctx, 2}; 417 IPC::ResponseBuilder rb{ctx, 2};
418 rb.Push(lan_discovery.SetAdvertiseData(read_buffer)); 418 rb.Push(lan_discovery.SetAdvertiseData(read_buffer));
@@ -464,7 +464,7 @@ public:
464 parameters.security_config.passphrase_size, 464 parameters.security_config.passphrase_size,
465 parameters.security_config.security_mode, parameters.local_communication_version); 465 parameters.security_config.security_mode, parameters.local_communication_version);
466 466
467 const std::vector<u8> read_buffer = ctx.ReadBuffer(); 467 const auto read_buffer = ctx.ReadBuffer();
468 if (read_buffer.size() != sizeof(NetworkInfo)) { 468 if (read_buffer.size() != sizeof(NetworkInfo)) {
469 LOG_ERROR(Frontend, "NetworkInfo doesn't match read_buffer size!"); 469 LOG_ERROR(Frontend, "NetworkInfo doesn't match read_buffer size!");
470 IPC::ResponseBuilder rb{ctx, 2}; 470 IPC::ResponseBuilder rb{ctx, 2};
diff --git a/src/core/hle/service/nfc/nfc_device.cpp b/src/core/hle/service/nfc/nfc_device.cpp
index 78578f723..9a3234e8c 100644
--- a/src/core/hle/service/nfc/nfc_device.cpp
+++ b/src/core/hle/service/nfc/nfc_device.cpp
@@ -130,7 +130,9 @@ Result NfcDevice::StartDetection(NFP::TagProtocol allowed_protocol) {
130 return WrongDeviceState; 130 return WrongDeviceState;
131 } 131 }
132 132
133 if (!npad_device->SetPollingMode(Common::Input::PollingMode::NFC)) { 133 if (npad_device->SetPollingMode(Core::HID::EmulatedDeviceIndex::RightIndex,
134 Common::Input::PollingMode::NFC) !=
135 Common::Input::DriverResult::Success) {
134 LOG_ERROR(Service_NFC, "Nfc not supported"); 136 LOG_ERROR(Service_NFC, "Nfc not supported");
135 return NfcDisabled; 137 return NfcDisabled;
136 } 138 }
@@ -141,7 +143,8 @@ Result NfcDevice::StartDetection(NFP::TagProtocol allowed_protocol) {
141} 143}
142 144
143Result NfcDevice::StopDetection() { 145Result NfcDevice::StopDetection() {
144 npad_device->SetPollingMode(Common::Input::PollingMode::Active); 146 npad_device->SetPollingMode(Core::HID::EmulatedDeviceIndex::RightIndex,
147 Common::Input::PollingMode::Active);
145 148
146 if (device_state == NFP::DeviceState::Initialized) { 149 if (device_state == NFP::DeviceState::Initialized) {
147 return ResultSuccess; 150 return ResultSuccess;
diff --git a/src/core/hle/service/nfp/nfp_device.cpp b/src/core/hle/service/nfp/nfp_device.cpp
index c860fd1a1..e67a76f55 100644
--- a/src/core/hle/service/nfp/nfp_device.cpp
+++ b/src/core/hle/service/nfp/nfp_device.cpp
@@ -152,7 +152,9 @@ Result NfpDevice::StartDetection(TagProtocol allowed_protocol) {
152 return WrongDeviceState; 152 return WrongDeviceState;
153 } 153 }
154 154
155 if (!npad_device->SetPollingMode(Common::Input::PollingMode::NFC)) { 155 if (npad_device->SetPollingMode(Core::HID::EmulatedDeviceIndex::RightIndex,
156 Common::Input::PollingMode::NFC) !=
157 Common::Input::DriverResult::Success) {
156 LOG_ERROR(Service_NFP, "Nfc not supported"); 158 LOG_ERROR(Service_NFP, "Nfc not supported");
157 return NfcDisabled; 159 return NfcDisabled;
158 } 160 }
@@ -163,7 +165,8 @@ Result NfpDevice::StartDetection(TagProtocol allowed_protocol) {
163} 165}
164 166
165Result NfpDevice::StopDetection() { 167Result NfpDevice::StopDetection() {
166 npad_device->SetPollingMode(Common::Input::PollingMode::Active); 168 npad_device->SetPollingMode(Core::HID::EmulatedDeviceIndex::RightIndex,
169 Common::Input::PollingMode::Active);
167 170
168 if (device_state == DeviceState::Initialized) { 171 if (device_state == DeviceState::Initialized) {
169 return ResultSuccess; 172 return ResultSuccess;
diff --git a/src/core/hle/service/nvdrv/devices/nvdevice.h b/src/core/hle/service/nvdrv/devices/nvdevice.h
index 204b0e757..c562e04d2 100644
--- a/src/core/hle/service/nvdrv/devices/nvdevice.h
+++ b/src/core/hle/service/nvdrv/devices/nvdevice.h
@@ -3,7 +3,9 @@
3 3
4#pragma once 4#pragma once
5 5
6#include <span>
6#include <vector> 7#include <vector>
8
7#include "common/common_types.h" 9#include "common/common_types.h"
8#include "core/hle/service/nvdrv/nvdata.h" 10#include "core/hle/service/nvdrv/nvdata.h"
9 11
@@ -31,7 +33,7 @@ public:
31 * @param output A buffer where the output data will be written to. 33 * @param output A buffer where the output data will be written to.
32 * @returns The result code of the ioctl. 34 * @returns The result code of the ioctl.
33 */ 35 */
34 virtual NvResult Ioctl1(DeviceFD fd, Ioctl command, const std::vector<u8>& input, 36 virtual NvResult Ioctl1(DeviceFD fd, Ioctl command, std::span<const u8> input,
35 std::vector<u8>& output) = 0; 37 std::vector<u8>& output) = 0;
36 38
37 /** 39 /**
@@ -42,8 +44,8 @@ public:
42 * @param output A buffer where the output data will be written to. 44 * @param output A buffer where the output data will be written to.
43 * @returns The result code of the ioctl. 45 * @returns The result code of the ioctl.
44 */ 46 */
45 virtual NvResult Ioctl2(DeviceFD fd, Ioctl command, const std::vector<u8>& input, 47 virtual NvResult Ioctl2(DeviceFD fd, Ioctl command, std::span<const u8> input,
46 const std::vector<u8>& inline_input, std::vector<u8>& output) = 0; 48 std::span<const u8> inline_input, std::vector<u8>& output) = 0;
47 49
48 /** 50 /**
49 * Handles an ioctl3 request. 51 * Handles an ioctl3 request.
@@ -53,7 +55,7 @@ public:
53 * @param inline_output A buffer where the inlined output data will be written to. 55 * @param inline_output A buffer where the inlined output data will be written to.
54 * @returns The result code of the ioctl. 56 * @returns The result code of the ioctl.
55 */ 57 */
56 virtual NvResult Ioctl3(DeviceFD fd, Ioctl command, const std::vector<u8>& input, 58 virtual NvResult Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> input,
57 std::vector<u8>& output, std::vector<u8>& inline_output) = 0; 59 std::vector<u8>& output, std::vector<u8>& inline_output) = 0;
58 60
59 /** 61 /**
diff --git a/src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp b/src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp
index 4122fc98d..5a5b2e305 100644
--- a/src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp
@@ -17,19 +17,19 @@ nvdisp_disp0::nvdisp_disp0(Core::System& system_, NvCore::Container& core)
17 : nvdevice{system_}, container{core}, nvmap{core.GetNvMapFile()} {} 17 : nvdevice{system_}, container{core}, nvmap{core.GetNvMapFile()} {}
18nvdisp_disp0::~nvdisp_disp0() = default; 18nvdisp_disp0::~nvdisp_disp0() = default;
19 19
20NvResult nvdisp_disp0::Ioctl1(DeviceFD fd, Ioctl command, const std::vector<u8>& input, 20NvResult nvdisp_disp0::Ioctl1(DeviceFD fd, Ioctl command, std::span<const u8> input,
21 std::vector<u8>& output) { 21 std::vector<u8>& output) {
22 UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw); 22 UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);
23 return NvResult::NotImplemented; 23 return NvResult::NotImplemented;
24} 24}
25 25
26NvResult nvdisp_disp0::Ioctl2(DeviceFD fd, Ioctl command, const std::vector<u8>& input, 26NvResult nvdisp_disp0::Ioctl2(DeviceFD fd, Ioctl command, std::span<const u8> input,
27 const std::vector<u8>& inline_input, std::vector<u8>& output) { 27 std::span<const u8> inline_input, std::vector<u8>& output) {
28 UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw); 28 UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);
29 return NvResult::NotImplemented; 29 return NvResult::NotImplemented;
30} 30}
31 31
32NvResult nvdisp_disp0::Ioctl3(DeviceFD fd, Ioctl command, const std::vector<u8>& input, 32NvResult nvdisp_disp0::Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> input,
33 std::vector<u8>& output, std::vector<u8>& inline_output) { 33 std::vector<u8>& output, std::vector<u8>& inline_output) {
34 UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw); 34 UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);
35 return NvResult::NotImplemented; 35 return NvResult::NotImplemented;
diff --git a/src/core/hle/service/nvdrv/devices/nvdisp_disp0.h b/src/core/hle/service/nvdrv/devices/nvdisp_disp0.h
index 04217ab12..81bd7960a 100644
--- a/src/core/hle/service/nvdrv/devices/nvdisp_disp0.h
+++ b/src/core/hle/service/nvdrv/devices/nvdisp_disp0.h
@@ -25,12 +25,12 @@ public:
25 explicit nvdisp_disp0(Core::System& system_, NvCore::Container& core); 25 explicit nvdisp_disp0(Core::System& system_, NvCore::Container& core);
26 ~nvdisp_disp0() override; 26 ~nvdisp_disp0() override;
27 27
28 NvResult Ioctl1(DeviceFD fd, Ioctl command, const std::vector<u8>& input, 28 NvResult Ioctl1(DeviceFD fd, Ioctl command, std::span<const u8> input,
29 std::vector<u8>& output) override; 29 std::vector<u8>& output) override;
30 NvResult Ioctl2(DeviceFD fd, Ioctl command, const std::vector<u8>& input, 30 NvResult Ioctl2(DeviceFD fd, Ioctl command, std::span<const u8> input,
31 const std::vector<u8>& inline_input, std::vector<u8>& output) override; 31 std::span<const u8> inline_input, std::vector<u8>& output) override;
32 NvResult Ioctl3(DeviceFD fd, Ioctl command, const std::vector<u8>& input, 32 NvResult Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> input, std::vector<u8>& output,
33 std::vector<u8>& output, std::vector<u8>& inline_output) override; 33 std::vector<u8>& inline_output) override;
34 34
35 void OnOpen(DeviceFD fd) override; 35 void OnOpen(DeviceFD fd) override;
36 void OnClose(DeviceFD fd) override; 36 void OnClose(DeviceFD fd) override;
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp b/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp
index b635e6ed1..681bd0867 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp
@@ -27,7 +27,7 @@ nvhost_as_gpu::nvhost_as_gpu(Core::System& system_, Module& module_, NvCore::Con
27 27
28nvhost_as_gpu::~nvhost_as_gpu() = default; 28nvhost_as_gpu::~nvhost_as_gpu() = default;
29 29
30NvResult nvhost_as_gpu::Ioctl1(DeviceFD fd, Ioctl command, const std::vector<u8>& input, 30NvResult nvhost_as_gpu::Ioctl1(DeviceFD fd, Ioctl command, std::span<const u8> input,
31 std::vector<u8>& output) { 31 std::vector<u8>& output) {
32 switch (command.group) { 32 switch (command.group) {
33 case 'A': 33 case 'A':
@@ -60,13 +60,13 @@ NvResult nvhost_as_gpu::Ioctl1(DeviceFD fd, Ioctl command, const std::vector<u8>
60 return NvResult::NotImplemented; 60 return NvResult::NotImplemented;
61} 61}
62 62
63NvResult nvhost_as_gpu::Ioctl2(DeviceFD fd, Ioctl command, const std::vector<u8>& input, 63NvResult nvhost_as_gpu::Ioctl2(DeviceFD fd, Ioctl command, std::span<const u8> input,
64 const std::vector<u8>& inline_input, std::vector<u8>& output) { 64 std::span<const u8> inline_input, std::vector<u8>& output) {
65 UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw); 65 UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);
66 return NvResult::NotImplemented; 66 return NvResult::NotImplemented;
67} 67}
68 68
69NvResult nvhost_as_gpu::Ioctl3(DeviceFD fd, Ioctl command, const std::vector<u8>& input, 69NvResult nvhost_as_gpu::Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> input,
70 std::vector<u8>& output, std::vector<u8>& inline_output) { 70 std::vector<u8>& output, std::vector<u8>& inline_output) {
71 switch (command.group) { 71 switch (command.group) {
72 case 'A': 72 case 'A':
@@ -87,7 +87,7 @@ NvResult nvhost_as_gpu::Ioctl3(DeviceFD fd, Ioctl command, const std::vector<u8>
87void nvhost_as_gpu::OnOpen(DeviceFD fd) {} 87void nvhost_as_gpu::OnOpen(DeviceFD fd) {}
88void nvhost_as_gpu::OnClose(DeviceFD fd) {} 88void nvhost_as_gpu::OnClose(DeviceFD fd) {}
89 89
90NvResult nvhost_as_gpu::AllocAsEx(const std::vector<u8>& input, std::vector<u8>& output) { 90NvResult nvhost_as_gpu::AllocAsEx(std::span<const u8> input, std::vector<u8>& output) {
91 IoctlAllocAsEx params{}; 91 IoctlAllocAsEx params{};
92 std::memcpy(&params, input.data(), input.size()); 92 std::memcpy(&params, input.data(), input.size());
93 93
@@ -141,7 +141,7 @@ NvResult nvhost_as_gpu::AllocAsEx(const std::vector<u8>& input, std::vector<u8>&
141 return NvResult::Success; 141 return NvResult::Success;
142} 142}
143 143
144NvResult nvhost_as_gpu::AllocateSpace(const std::vector<u8>& input, std::vector<u8>& output) { 144NvResult nvhost_as_gpu::AllocateSpace(std::span<const u8> input, std::vector<u8>& output) {
145 IoctlAllocSpace params{}; 145 IoctlAllocSpace params{};
146 std::memcpy(&params, input.data(), input.size()); 146 std::memcpy(&params, input.data(), input.size());
147 147
@@ -220,7 +220,7 @@ void nvhost_as_gpu::FreeMappingLocked(u64 offset) {
220 mapping_map.erase(offset); 220 mapping_map.erase(offset);
221} 221}
222 222
223NvResult nvhost_as_gpu::FreeSpace(const std::vector<u8>& input, std::vector<u8>& output) { 223NvResult nvhost_as_gpu::FreeSpace(std::span<const u8> input, std::vector<u8>& output) {
224 IoctlFreeSpace params{}; 224 IoctlFreeSpace params{};
225 std::memcpy(&params, input.data(), input.size()); 225 std::memcpy(&params, input.data(), input.size());
226 226
@@ -266,7 +266,7 @@ NvResult nvhost_as_gpu::FreeSpace(const std::vector<u8>& input, std::vector<u8>&
266 return NvResult::Success; 266 return NvResult::Success;
267} 267}
268 268
269NvResult nvhost_as_gpu::Remap(const std::vector<u8>& input, std::vector<u8>& output) { 269NvResult nvhost_as_gpu::Remap(std::span<const u8> input, std::vector<u8>& output) {
270 const auto num_entries = input.size() / sizeof(IoctlRemapEntry); 270 const auto num_entries = input.size() / sizeof(IoctlRemapEntry);
271 271
272 LOG_DEBUG(Service_NVDRV, "called, num_entries=0x{:X}", num_entries); 272 LOG_DEBUG(Service_NVDRV, "called, num_entries=0x{:X}", num_entries);
@@ -320,7 +320,7 @@ NvResult nvhost_as_gpu::Remap(const std::vector<u8>& input, std::vector<u8>& out
320 return NvResult::Success; 320 return NvResult::Success;
321} 321}
322 322
323NvResult nvhost_as_gpu::MapBufferEx(const std::vector<u8>& input, std::vector<u8>& output) { 323NvResult nvhost_as_gpu::MapBufferEx(std::span<const u8> input, std::vector<u8>& output) {
324 IoctlMapBufferEx params{}; 324 IoctlMapBufferEx params{};
325 std::memcpy(&params, input.data(), input.size()); 325 std::memcpy(&params, input.data(), input.size());
326 326
@@ -424,7 +424,7 @@ NvResult nvhost_as_gpu::MapBufferEx(const std::vector<u8>& input, std::vector<u8
424 return NvResult::Success; 424 return NvResult::Success;
425} 425}
426 426
427NvResult nvhost_as_gpu::UnmapBuffer(const std::vector<u8>& input, std::vector<u8>& output) { 427NvResult nvhost_as_gpu::UnmapBuffer(std::span<const u8> input, std::vector<u8>& output) {
428 IoctlUnmapBuffer params{}; 428 IoctlUnmapBuffer params{};
429 std::memcpy(&params, input.data(), input.size()); 429 std::memcpy(&params, input.data(), input.size());
430 430
@@ -463,7 +463,7 @@ NvResult nvhost_as_gpu::UnmapBuffer(const std::vector<u8>& input, std::vector<u8
463 return NvResult::Success; 463 return NvResult::Success;
464} 464}
465 465
466NvResult nvhost_as_gpu::BindChannel(const std::vector<u8>& input, std::vector<u8>& output) { 466NvResult nvhost_as_gpu::BindChannel(std::span<const u8> input, std::vector<u8>& output) {
467 IoctlBindChannel params{}; 467 IoctlBindChannel params{};
468 std::memcpy(&params, input.data(), input.size()); 468 std::memcpy(&params, input.data(), input.size());
469 LOG_DEBUG(Service_NVDRV, "called, fd={:X}", params.fd); 469 LOG_DEBUG(Service_NVDRV, "called, fd={:X}", params.fd);
@@ -492,7 +492,7 @@ void nvhost_as_gpu::GetVARegionsImpl(IoctlGetVaRegions& params) {
492 }; 492 };
493} 493}
494 494
495NvResult nvhost_as_gpu::GetVARegions(const std::vector<u8>& input, std::vector<u8>& output) { 495NvResult nvhost_as_gpu::GetVARegions(std::span<const u8> input, std::vector<u8>& output) {
496 IoctlGetVaRegions params{}; 496 IoctlGetVaRegions params{};
497 std::memcpy(&params, input.data(), input.size()); 497 std::memcpy(&params, input.data(), input.size());
498 498
@@ -511,7 +511,7 @@ NvResult nvhost_as_gpu::GetVARegions(const std::vector<u8>& input, std::vector<u
511 return NvResult::Success; 511 return NvResult::Success;
512} 512}
513 513
514NvResult nvhost_as_gpu::GetVARegions(const std::vector<u8>& input, std::vector<u8>& output, 514NvResult nvhost_as_gpu::GetVARegions(std::span<const u8> input, std::vector<u8>& output,
515 std::vector<u8>& inline_output) { 515 std::vector<u8>& inline_output) {
516 IoctlGetVaRegions params{}; 516 IoctlGetVaRegions params{};
517 std::memcpy(&params, input.data(), input.size()); 517 std::memcpy(&params, input.data(), input.size());
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.h b/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.h
index 86fe71c75..1aba8d579 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.h
+++ b/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.h
@@ -47,12 +47,12 @@ public:
47 explicit nvhost_as_gpu(Core::System& system_, Module& module, NvCore::Container& core); 47 explicit nvhost_as_gpu(Core::System& system_, Module& module, NvCore::Container& core);
48 ~nvhost_as_gpu() override; 48 ~nvhost_as_gpu() override;
49 49
50 NvResult Ioctl1(DeviceFD fd, Ioctl command, const std::vector<u8>& input, 50 NvResult Ioctl1(DeviceFD fd, Ioctl command, std::span<const u8> input,
51 std::vector<u8>& output) override; 51 std::vector<u8>& output) override;
52 NvResult Ioctl2(DeviceFD fd, Ioctl command, const std::vector<u8>& input, 52 NvResult Ioctl2(DeviceFD fd, Ioctl command, std::span<const u8> input,
53 const std::vector<u8>& inline_input, std::vector<u8>& output) override; 53 std::span<const u8> inline_input, std::vector<u8>& output) override;
54 NvResult Ioctl3(DeviceFD fd, Ioctl command, const std::vector<u8>& input, 54 NvResult Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> input, std::vector<u8>& output,
55 std::vector<u8>& output, std::vector<u8>& inline_output) override; 55 std::vector<u8>& inline_output) override;
56 56
57 void OnOpen(DeviceFD fd) override; 57 void OnOpen(DeviceFD fd) override;
58 void OnClose(DeviceFD fd) override; 58 void OnClose(DeviceFD fd) override;
@@ -138,17 +138,17 @@ private:
138 static_assert(sizeof(IoctlGetVaRegions) == 16 + sizeof(VaRegion) * 2, 138 static_assert(sizeof(IoctlGetVaRegions) == 16 + sizeof(VaRegion) * 2,
139 "IoctlGetVaRegions is incorrect size"); 139 "IoctlGetVaRegions is incorrect size");
140 140
141 NvResult AllocAsEx(const std::vector<u8>& input, std::vector<u8>& output); 141 NvResult AllocAsEx(std::span<const u8> input, std::vector<u8>& output);
142 NvResult AllocateSpace(const std::vector<u8>& input, std::vector<u8>& output); 142 NvResult AllocateSpace(std::span<const u8> input, std::vector<u8>& output);
143 NvResult Remap(const std::vector<u8>& input, std::vector<u8>& output); 143 NvResult Remap(std::span<const u8> input, std::vector<u8>& output);
144 NvResult MapBufferEx(const std::vector<u8>& input, std::vector<u8>& output); 144 NvResult MapBufferEx(std::span<const u8> input, std::vector<u8>& output);
145 NvResult UnmapBuffer(const std::vector<u8>& input, std::vector<u8>& output); 145 NvResult UnmapBuffer(std::span<const u8> input, std::vector<u8>& output);
146 NvResult FreeSpace(const std::vector<u8>& input, std::vector<u8>& output); 146 NvResult FreeSpace(std::span<const u8> input, std::vector<u8>& output);
147 NvResult BindChannel(const std::vector<u8>& input, std::vector<u8>& output); 147 NvResult BindChannel(std::span<const u8> input, std::vector<u8>& output);
148 148
149 void GetVARegionsImpl(IoctlGetVaRegions& params); 149 void GetVARegionsImpl(IoctlGetVaRegions& params);
150 NvResult GetVARegions(const std::vector<u8>& input, std::vector<u8>& output); 150 NvResult GetVARegions(std::span<const u8> input, std::vector<u8>& output);
151 NvResult GetVARegions(const std::vector<u8>& input, std::vector<u8>& output, 151 NvResult GetVARegions(std::span<const u8> input, std::vector<u8>& output,
152 std::vector<u8>& inline_output); 152 std::vector<u8>& inline_output);
153 153
154 void FreeMappingLocked(u64 offset); 154 void FreeMappingLocked(u64 offset);
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp b/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp
index eee11fab8..0cdde82a7 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp
@@ -34,7 +34,7 @@ nvhost_ctrl::~nvhost_ctrl() {
34 } 34 }
35} 35}
36 36
37NvResult nvhost_ctrl::Ioctl1(DeviceFD fd, Ioctl command, const std::vector<u8>& input, 37NvResult nvhost_ctrl::Ioctl1(DeviceFD fd, Ioctl command, std::span<const u8> input,
38 std::vector<u8>& output) { 38 std::vector<u8>& output) {
39 switch (command.group) { 39 switch (command.group) {
40 case 0x0: 40 case 0x0:
@@ -63,13 +63,13 @@ NvResult nvhost_ctrl::Ioctl1(DeviceFD fd, Ioctl command, const std::vector<u8>&
63 return NvResult::NotImplemented; 63 return NvResult::NotImplemented;
64} 64}
65 65
66NvResult nvhost_ctrl::Ioctl2(DeviceFD fd, Ioctl command, const std::vector<u8>& input, 66NvResult nvhost_ctrl::Ioctl2(DeviceFD fd, Ioctl command, std::span<const u8> input,
67 const std::vector<u8>& inline_input, std::vector<u8>& output) { 67 std::span<const u8> inline_input, std::vector<u8>& output) {
68 UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw); 68 UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);
69 return NvResult::NotImplemented; 69 return NvResult::NotImplemented;
70} 70}
71 71
72NvResult nvhost_ctrl::Ioctl3(DeviceFD fd, Ioctl command, const std::vector<u8>& input, 72NvResult nvhost_ctrl::Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> input,
73 std::vector<u8>& output, std::vector<u8>& inline_outpu) { 73 std::vector<u8>& output, std::vector<u8>& inline_outpu) {
74 UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw); 74 UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);
75 return NvResult::NotImplemented; 75 return NvResult::NotImplemented;
@@ -79,7 +79,7 @@ void nvhost_ctrl::OnOpen(DeviceFD fd) {}
79 79
80void nvhost_ctrl::OnClose(DeviceFD fd) {} 80void nvhost_ctrl::OnClose(DeviceFD fd) {}
81 81
82NvResult nvhost_ctrl::NvOsGetConfigU32(const std::vector<u8>& input, std::vector<u8>& output) { 82NvResult nvhost_ctrl::NvOsGetConfigU32(std::span<const u8> input, std::vector<u8>& output) {
83 IocGetConfigParams params{}; 83 IocGetConfigParams params{};
84 std::memcpy(&params, input.data(), sizeof(params)); 84 std::memcpy(&params, input.data(), sizeof(params));
85 LOG_TRACE(Service_NVDRV, "called, setting={}!{}", params.domain_str.data(), 85 LOG_TRACE(Service_NVDRV, "called, setting={}!{}", params.domain_str.data(),
@@ -87,7 +87,7 @@ NvResult nvhost_ctrl::NvOsGetConfigU32(const std::vector<u8>& input, std::vector
87 return NvResult::ConfigVarNotFound; // Returns error on production mode 87 return NvResult::ConfigVarNotFound; // Returns error on production mode
88} 88}
89 89
90NvResult nvhost_ctrl::IocCtrlEventWait(const std::vector<u8>& input, std::vector<u8>& output, 90NvResult nvhost_ctrl::IocCtrlEventWait(std::span<const u8> input, std::vector<u8>& output,
91 bool is_allocation) { 91 bool is_allocation) {
92 IocCtrlEventWaitParams params{}; 92 IocCtrlEventWaitParams params{};
93 std::memcpy(&params, input.data(), sizeof(params)); 93 std::memcpy(&params, input.data(), sizeof(params));
@@ -231,7 +231,7 @@ NvResult nvhost_ctrl::FreeEvent(u32 slot) {
231 return NvResult::Success; 231 return NvResult::Success;
232} 232}
233 233
234NvResult nvhost_ctrl::IocCtrlEventRegister(const std::vector<u8>& input, std::vector<u8>& output) { 234NvResult nvhost_ctrl::IocCtrlEventRegister(std::span<const u8> input, std::vector<u8>& output) {
235 IocCtrlEventRegisterParams params{}; 235 IocCtrlEventRegisterParams params{};
236 std::memcpy(&params, input.data(), sizeof(params)); 236 std::memcpy(&params, input.data(), sizeof(params));
237 const u32 event_id = params.user_event_id; 237 const u32 event_id = params.user_event_id;
@@ -252,8 +252,7 @@ NvResult nvhost_ctrl::IocCtrlEventRegister(const std::vector<u8>& input, std::ve
252 return NvResult::Success; 252 return NvResult::Success;
253} 253}
254 254
255NvResult nvhost_ctrl::IocCtrlEventUnregister(const std::vector<u8>& input, 255NvResult nvhost_ctrl::IocCtrlEventUnregister(std::span<const u8> input, std::vector<u8>& output) {
256 std::vector<u8>& output) {
257 IocCtrlEventUnregisterParams params{}; 256 IocCtrlEventUnregisterParams params{};
258 std::memcpy(&params, input.data(), sizeof(params)); 257 std::memcpy(&params, input.data(), sizeof(params));
259 const u32 event_id = params.user_event_id & 0x00FF; 258 const u32 event_id = params.user_event_id & 0x00FF;
@@ -263,7 +262,7 @@ NvResult nvhost_ctrl::IocCtrlEventUnregister(const std::vector<u8>& input,
263 return FreeEvent(event_id); 262 return FreeEvent(event_id);
264} 263}
265 264
266NvResult nvhost_ctrl::IocCtrlEventUnregisterBatch(const std::vector<u8>& input, 265NvResult nvhost_ctrl::IocCtrlEventUnregisterBatch(std::span<const u8> input,
267 std::vector<u8>& output) { 266 std::vector<u8>& output) {
268 IocCtrlEventUnregisterBatchParams params{}; 267 IocCtrlEventUnregisterBatchParams params{};
269 std::memcpy(&params, input.data(), sizeof(params)); 268 std::memcpy(&params, input.data(), sizeof(params));
@@ -282,7 +281,7 @@ NvResult nvhost_ctrl::IocCtrlEventUnregisterBatch(const std::vector<u8>& input,
282 return NvResult::Success; 281 return NvResult::Success;
283} 282}
284 283
285NvResult nvhost_ctrl::IocCtrlClearEventWait(const std::vector<u8>& input, std::vector<u8>& output) { 284NvResult nvhost_ctrl::IocCtrlClearEventWait(std::span<const u8> input, std::vector<u8>& output) {
286 IocCtrlEventClearParams params{}; 285 IocCtrlEventClearParams params{};
287 std::memcpy(&params, input.data(), sizeof(params)); 286 std::memcpy(&params, input.data(), sizeof(params));
288 287
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_ctrl.h b/src/core/hle/service/nvdrv/devices/nvhost_ctrl.h
index 0b56d7070..dd2e7888a 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_ctrl.h
+++ b/src/core/hle/service/nvdrv/devices/nvhost_ctrl.h
@@ -25,12 +25,12 @@ public:
25 NvCore::Container& core); 25 NvCore::Container& core);
26 ~nvhost_ctrl() override; 26 ~nvhost_ctrl() override;
27 27
28 NvResult Ioctl1(DeviceFD fd, Ioctl command, const std::vector<u8>& input, 28 NvResult Ioctl1(DeviceFD fd, Ioctl command, std::span<const u8> input,
29 std::vector<u8>& output) override; 29 std::vector<u8>& output) override;
30 NvResult Ioctl2(DeviceFD fd, Ioctl command, const std::vector<u8>& input, 30 NvResult Ioctl2(DeviceFD fd, Ioctl command, std::span<const u8> input,
31 const std::vector<u8>& inline_input, std::vector<u8>& output) override; 31 std::span<const u8> inline_input, std::vector<u8>& output) override;
32 NvResult Ioctl3(DeviceFD fd, Ioctl command, const std::vector<u8>& input, 32 NvResult Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> input, std::vector<u8>& output,
33 std::vector<u8>& output, std::vector<u8>& inline_output) override; 33 std::vector<u8>& inline_output) override;
34 34
35 void OnOpen(DeviceFD fd) override; 35 void OnOpen(DeviceFD fd) override;
36 void OnClose(DeviceFD fd) override; 36 void OnClose(DeviceFD fd) override;
@@ -186,13 +186,13 @@ private:
186 static_assert(sizeof(IocCtrlEventUnregisterBatchParams) == 8, 186 static_assert(sizeof(IocCtrlEventUnregisterBatchParams) == 8,
187 "IocCtrlEventKill is incorrect size"); 187 "IocCtrlEventKill is incorrect size");
188 188
189 NvResult NvOsGetConfigU32(const std::vector<u8>& input, std::vector<u8>& output); 189 NvResult NvOsGetConfigU32(std::span<const u8> input, std::vector<u8>& output);
190 NvResult IocCtrlEventWait(const std::vector<u8>& input, std::vector<u8>& output, 190 NvResult IocCtrlEventWait(std::span<const u8> input, std::vector<u8>& output,
191 bool is_allocation); 191 bool is_allocation);
192 NvResult IocCtrlEventRegister(const std::vector<u8>& input, std::vector<u8>& output); 192 NvResult IocCtrlEventRegister(std::span<const u8> input, std::vector<u8>& output);
193 NvResult IocCtrlEventUnregister(const std::vector<u8>& input, std::vector<u8>& output); 193 NvResult IocCtrlEventUnregister(std::span<const u8> input, std::vector<u8>& output);
194 NvResult IocCtrlEventUnregisterBatch(const std::vector<u8>& input, std::vector<u8>& output); 194 NvResult IocCtrlEventUnregisterBatch(std::span<const u8> input, std::vector<u8>& output);
195 NvResult IocCtrlClearEventWait(const std::vector<u8>& input, std::vector<u8>& output); 195 NvResult IocCtrlClearEventWait(std::span<const u8> input, std::vector<u8>& output);
196 196
197 NvResult FreeEvent(u32 slot); 197 NvResult FreeEvent(u32 slot);
198 198
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp b/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp
index b97813fbc..be3c083db 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp
@@ -21,7 +21,7 @@ nvhost_ctrl_gpu::~nvhost_ctrl_gpu() {
21 events_interface.FreeEvent(unknown_event); 21 events_interface.FreeEvent(unknown_event);
22} 22}
23 23
24NvResult nvhost_ctrl_gpu::Ioctl1(DeviceFD fd, Ioctl command, const std::vector<u8>& input, 24NvResult nvhost_ctrl_gpu::Ioctl1(DeviceFD fd, Ioctl command, std::span<const u8> input,
25 std::vector<u8>& output) { 25 std::vector<u8>& output) {
26 switch (command.group) { 26 switch (command.group) {
27 case 'G': 27 case 'G':
@@ -53,13 +53,13 @@ NvResult nvhost_ctrl_gpu::Ioctl1(DeviceFD fd, Ioctl command, const std::vector<u
53 return NvResult::NotImplemented; 53 return NvResult::NotImplemented;
54} 54}
55 55
56NvResult nvhost_ctrl_gpu::Ioctl2(DeviceFD fd, Ioctl command, const std::vector<u8>& input, 56NvResult nvhost_ctrl_gpu::Ioctl2(DeviceFD fd, Ioctl command, std::span<const u8> input,
57 const std::vector<u8>& inline_input, std::vector<u8>& output) { 57 std::span<const u8> inline_input, std::vector<u8>& output) {
58 UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw); 58 UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);
59 return NvResult::NotImplemented; 59 return NvResult::NotImplemented;
60} 60}
61 61
62NvResult nvhost_ctrl_gpu::Ioctl3(DeviceFD fd, Ioctl command, const std::vector<u8>& input, 62NvResult nvhost_ctrl_gpu::Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> input,
63 std::vector<u8>& output, std::vector<u8>& inline_output) { 63 std::vector<u8>& output, std::vector<u8>& inline_output) {
64 switch (command.group) { 64 switch (command.group) {
65 case 'G': 65 case 'G':
@@ -82,8 +82,7 @@ NvResult nvhost_ctrl_gpu::Ioctl3(DeviceFD fd, Ioctl command, const std::vector<u
82void nvhost_ctrl_gpu::OnOpen(DeviceFD fd) {} 82void nvhost_ctrl_gpu::OnOpen(DeviceFD fd) {}
83void nvhost_ctrl_gpu::OnClose(DeviceFD fd) {} 83void nvhost_ctrl_gpu::OnClose(DeviceFD fd) {}
84 84
85NvResult nvhost_ctrl_gpu::GetCharacteristics(const std::vector<u8>& input, 85NvResult nvhost_ctrl_gpu::GetCharacteristics(std::span<const u8> input, std::vector<u8>& output) {
86 std::vector<u8>& output) {
87 LOG_DEBUG(Service_NVDRV, "called"); 86 LOG_DEBUG(Service_NVDRV, "called");
88 IoctlCharacteristics params{}; 87 IoctlCharacteristics params{};
89 std::memcpy(&params, input.data(), input.size()); 88 std::memcpy(&params, input.data(), input.size());
@@ -128,7 +127,7 @@ NvResult nvhost_ctrl_gpu::GetCharacteristics(const std::vector<u8>& input,
128 return NvResult::Success; 127 return NvResult::Success;
129} 128}
130 129
131NvResult nvhost_ctrl_gpu::GetCharacteristics(const std::vector<u8>& input, std::vector<u8>& output, 130NvResult nvhost_ctrl_gpu::GetCharacteristics(std::span<const u8> input, std::vector<u8>& output,
132 std::vector<u8>& inline_output) { 131 std::vector<u8>& inline_output) {
133 LOG_DEBUG(Service_NVDRV, "called"); 132 LOG_DEBUG(Service_NVDRV, "called");
134 IoctlCharacteristics params{}; 133 IoctlCharacteristics params{};
@@ -176,7 +175,7 @@ NvResult nvhost_ctrl_gpu::GetCharacteristics(const std::vector<u8>& input, std::
176 return NvResult::Success; 175 return NvResult::Success;
177} 176}
178 177
179NvResult nvhost_ctrl_gpu::GetTPCMasks(const std::vector<u8>& input, std::vector<u8>& output) { 178NvResult nvhost_ctrl_gpu::GetTPCMasks(std::span<const u8> input, std::vector<u8>& output) {
180 IoctlGpuGetTpcMasksArgs params{}; 179 IoctlGpuGetTpcMasksArgs params{};
181 std::memcpy(&params, input.data(), input.size()); 180 std::memcpy(&params, input.data(), input.size());
182 LOG_DEBUG(Service_NVDRV, "called, mask_buffer_size=0x{:X}", params.mask_buffer_size); 181 LOG_DEBUG(Service_NVDRV, "called, mask_buffer_size=0x{:X}", params.mask_buffer_size);
@@ -187,7 +186,7 @@ NvResult nvhost_ctrl_gpu::GetTPCMasks(const std::vector<u8>& input, std::vector<
187 return NvResult::Success; 186 return NvResult::Success;
188} 187}
189 188
190NvResult nvhost_ctrl_gpu::GetTPCMasks(const std::vector<u8>& input, std::vector<u8>& output, 189NvResult nvhost_ctrl_gpu::GetTPCMasks(std::span<const u8> input, std::vector<u8>& output,
191 std::vector<u8>& inline_output) { 190 std::vector<u8>& inline_output) {
192 IoctlGpuGetTpcMasksArgs params{}; 191 IoctlGpuGetTpcMasksArgs params{};
193 std::memcpy(&params, input.data(), input.size()); 192 std::memcpy(&params, input.data(), input.size());
@@ -200,7 +199,7 @@ NvResult nvhost_ctrl_gpu::GetTPCMasks(const std::vector<u8>& input, std::vector<
200 return NvResult::Success; 199 return NvResult::Success;
201} 200}
202 201
203NvResult nvhost_ctrl_gpu::GetActiveSlotMask(const std::vector<u8>& input, std::vector<u8>& output) { 202NvResult nvhost_ctrl_gpu::GetActiveSlotMask(std::span<const u8> input, std::vector<u8>& output) {
204 LOG_DEBUG(Service_NVDRV, "called"); 203 LOG_DEBUG(Service_NVDRV, "called");
205 204
206 IoctlActiveSlotMask params{}; 205 IoctlActiveSlotMask params{};
@@ -213,7 +212,7 @@ NvResult nvhost_ctrl_gpu::GetActiveSlotMask(const std::vector<u8>& input, std::v
213 return NvResult::Success; 212 return NvResult::Success;
214} 213}
215 214
216NvResult nvhost_ctrl_gpu::ZCullGetCtxSize(const std::vector<u8>& input, std::vector<u8>& output) { 215NvResult nvhost_ctrl_gpu::ZCullGetCtxSize(std::span<const u8> input, std::vector<u8>& output) {
217 LOG_DEBUG(Service_NVDRV, "called"); 216 LOG_DEBUG(Service_NVDRV, "called");
218 217
219 IoctlZcullGetCtxSize params{}; 218 IoctlZcullGetCtxSize params{};
@@ -225,7 +224,7 @@ NvResult nvhost_ctrl_gpu::ZCullGetCtxSize(const std::vector<u8>& input, std::vec
225 return NvResult::Success; 224 return NvResult::Success;
226} 225}
227 226
228NvResult nvhost_ctrl_gpu::ZCullGetInfo(const std::vector<u8>& input, std::vector<u8>& output) { 227NvResult nvhost_ctrl_gpu::ZCullGetInfo(std::span<const u8> input, std::vector<u8>& output) {
229 LOG_DEBUG(Service_NVDRV, "called"); 228 LOG_DEBUG(Service_NVDRV, "called");
230 229
231 IoctlNvgpuGpuZcullGetInfoArgs params{}; 230 IoctlNvgpuGpuZcullGetInfoArgs params{};
@@ -248,7 +247,7 @@ NvResult nvhost_ctrl_gpu::ZCullGetInfo(const std::vector<u8>& input, std::vector
248 return NvResult::Success; 247 return NvResult::Success;
249} 248}
250 249
251NvResult nvhost_ctrl_gpu::ZBCSetTable(const std::vector<u8>& input, std::vector<u8>& output) { 250NvResult nvhost_ctrl_gpu::ZBCSetTable(std::span<const u8> input, std::vector<u8>& output) {
252 LOG_WARNING(Service_NVDRV, "(STUBBED) called"); 251 LOG_WARNING(Service_NVDRV, "(STUBBED) called");
253 252
254 IoctlZbcSetTable params{}; 253 IoctlZbcSetTable params{};
@@ -264,7 +263,7 @@ NvResult nvhost_ctrl_gpu::ZBCSetTable(const std::vector<u8>& input, std::vector<
264 return NvResult::Success; 263 return NvResult::Success;
265} 264}
266 265
267NvResult nvhost_ctrl_gpu::ZBCQueryTable(const std::vector<u8>& input, std::vector<u8>& output) { 266NvResult nvhost_ctrl_gpu::ZBCQueryTable(std::span<const u8> input, std::vector<u8>& output) {
268 LOG_WARNING(Service_NVDRV, "(STUBBED) called"); 267 LOG_WARNING(Service_NVDRV, "(STUBBED) called");
269 268
270 IoctlZbcQueryTable params{}; 269 IoctlZbcQueryTable params{};
@@ -274,7 +273,7 @@ NvResult nvhost_ctrl_gpu::ZBCQueryTable(const std::vector<u8>& input, std::vecto
274 return NvResult::Success; 273 return NvResult::Success;
275} 274}
276 275
277NvResult nvhost_ctrl_gpu::FlushL2(const std::vector<u8>& input, std::vector<u8>& output) { 276NvResult nvhost_ctrl_gpu::FlushL2(std::span<const u8> input, std::vector<u8>& output) {
278 LOG_WARNING(Service_NVDRV, "(STUBBED) called"); 277 LOG_WARNING(Service_NVDRV, "(STUBBED) called");
279 278
280 IoctlFlushL2 params{}; 279 IoctlFlushL2 params{};
@@ -284,7 +283,7 @@ NvResult nvhost_ctrl_gpu::FlushL2(const std::vector<u8>& input, std::vector<u8>&
284 return NvResult::Success; 283 return NvResult::Success;
285} 284}
286 285
287NvResult nvhost_ctrl_gpu::GetGpuTime(const std::vector<u8>& input, std::vector<u8>& output) { 286NvResult nvhost_ctrl_gpu::GetGpuTime(std::span<const u8> input, std::vector<u8>& output) {
288 LOG_DEBUG(Service_NVDRV, "called"); 287 LOG_DEBUG(Service_NVDRV, "called");
289 288
290 IoctlGetGpuTime params{}; 289 IoctlGetGpuTime params{};
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.h b/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.h
index 1e8f254e2..b9333d9d3 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.h
+++ b/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.h
@@ -21,12 +21,12 @@ public:
21 explicit nvhost_ctrl_gpu(Core::System& system_, EventInterface& events_interface_); 21 explicit nvhost_ctrl_gpu(Core::System& system_, EventInterface& events_interface_);
22 ~nvhost_ctrl_gpu() override; 22 ~nvhost_ctrl_gpu() override;
23 23
24 NvResult Ioctl1(DeviceFD fd, Ioctl command, const std::vector<u8>& input, 24 NvResult Ioctl1(DeviceFD fd, Ioctl command, std::span<const u8> input,
25 std::vector<u8>& output) override; 25 std::vector<u8>& output) override;
26 NvResult Ioctl2(DeviceFD fd, Ioctl command, const std::vector<u8>& input, 26 NvResult Ioctl2(DeviceFD fd, Ioctl command, std::span<const u8> input,
27 const std::vector<u8>& inline_input, std::vector<u8>& output) override; 27 std::span<const u8> inline_input, std::vector<u8>& output) override;
28 NvResult Ioctl3(DeviceFD fd, Ioctl command, const std::vector<u8>& input, 28 NvResult Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> input, std::vector<u8>& output,
29 std::vector<u8>& output, std::vector<u8>& inline_output) override; 29 std::vector<u8>& inline_output) override;
30 30
31 void OnOpen(DeviceFD fd) override; 31 void OnOpen(DeviceFD fd) override;
32 void OnClose(DeviceFD fd) override; 32 void OnClose(DeviceFD fd) override;
@@ -151,21 +151,21 @@ private:
151 }; 151 };
152 static_assert(sizeof(IoctlGetGpuTime) == 0x10, "IoctlGetGpuTime is incorrect size"); 152 static_assert(sizeof(IoctlGetGpuTime) == 0x10, "IoctlGetGpuTime is incorrect size");
153 153
154 NvResult GetCharacteristics(const std::vector<u8>& input, std::vector<u8>& output); 154 NvResult GetCharacteristics(std::span<const u8> input, std::vector<u8>& output);
155 NvResult GetCharacteristics(const std::vector<u8>& input, std::vector<u8>& output, 155 NvResult GetCharacteristics(std::span<const u8> input, std::vector<u8>& output,
156 std::vector<u8>& inline_output); 156 std::vector<u8>& inline_output);
157 157
158 NvResult GetTPCMasks(const std::vector<u8>& input, std::vector<u8>& output); 158 NvResult GetTPCMasks(std::span<const u8> input, std::vector<u8>& output);
159 NvResult GetTPCMasks(const std::vector<u8>& input, std::vector<u8>& output, 159 NvResult GetTPCMasks(std::span<const u8> input, std::vector<u8>& output,
160 std::vector<u8>& inline_output); 160 std::vector<u8>& inline_output);
161 161
162 NvResult GetActiveSlotMask(const std::vector<u8>& input, std::vector<u8>& output); 162 NvResult GetActiveSlotMask(std::span<const u8> input, std::vector<u8>& output);
163 NvResult ZCullGetCtxSize(const std::vector<u8>& input, std::vector<u8>& output); 163 NvResult ZCullGetCtxSize(std::span<const u8> input, std::vector<u8>& output);
164 NvResult ZCullGetInfo(const std::vector<u8>& input, std::vector<u8>& output); 164 NvResult ZCullGetInfo(std::span<const u8> input, std::vector<u8>& output);
165 NvResult ZBCSetTable(const std::vector<u8>& input, std::vector<u8>& output); 165 NvResult ZBCSetTable(std::span<const u8> input, std::vector<u8>& output);
166 NvResult ZBCQueryTable(const std::vector<u8>& input, std::vector<u8>& output); 166 NvResult ZBCQueryTable(std::span<const u8> input, std::vector<u8>& output);
167 NvResult FlushL2(const std::vector<u8>& input, std::vector<u8>& output); 167 NvResult FlushL2(std::span<const u8> input, std::vector<u8>& output);
168 NvResult GetGpuTime(const std::vector<u8>& input, std::vector<u8>& output); 168 NvResult GetGpuTime(std::span<const u8> input, std::vector<u8>& output);
169 169
170 EventInterface& events_interface; 170 EventInterface& events_interface;
171 171
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp b/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp
index e123564c6..d2308fffc 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp
@@ -46,7 +46,7 @@ nvhost_gpu::~nvhost_gpu() {
46 syncpoint_manager.FreeSyncpoint(channel_syncpoint); 46 syncpoint_manager.FreeSyncpoint(channel_syncpoint);
47} 47}
48 48
49NvResult nvhost_gpu::Ioctl1(DeviceFD fd, Ioctl command, const std::vector<u8>& input, 49NvResult nvhost_gpu::Ioctl1(DeviceFD fd, Ioctl command, std::span<const u8> input,
50 std::vector<u8>& output) { 50 std::vector<u8>& output) {
51 switch (command.group) { 51 switch (command.group) {
52 case 0x0: 52 case 0x0:
@@ -98,8 +98,8 @@ NvResult nvhost_gpu::Ioctl1(DeviceFD fd, Ioctl command, const std::vector<u8>& i
98 return NvResult::NotImplemented; 98 return NvResult::NotImplemented;
99}; 99};
100 100
101NvResult nvhost_gpu::Ioctl2(DeviceFD fd, Ioctl command, const std::vector<u8>& input, 101NvResult nvhost_gpu::Ioctl2(DeviceFD fd, Ioctl command, std::span<const u8> input,
102 const std::vector<u8>& inline_input, std::vector<u8>& output) { 102 std::span<const u8> inline_input, std::vector<u8>& output) {
103 switch (command.group) { 103 switch (command.group) {
104 case 'H': 104 case 'H':
105 switch (command.cmd) { 105 switch (command.cmd) {
@@ -112,7 +112,7 @@ NvResult nvhost_gpu::Ioctl2(DeviceFD fd, Ioctl command, const std::vector<u8>& i
112 return NvResult::NotImplemented; 112 return NvResult::NotImplemented;
113} 113}
114 114
115NvResult nvhost_gpu::Ioctl3(DeviceFD fd, Ioctl command, const std::vector<u8>& input, 115NvResult nvhost_gpu::Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> input,
116 std::vector<u8>& output, std::vector<u8>& inline_output) { 116 std::vector<u8>& output, std::vector<u8>& inline_output) {
117 UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw); 117 UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);
118 return NvResult::NotImplemented; 118 return NvResult::NotImplemented;
@@ -121,7 +121,7 @@ NvResult nvhost_gpu::Ioctl3(DeviceFD fd, Ioctl command, const std::vector<u8>& i
121void nvhost_gpu::OnOpen(DeviceFD fd) {} 121void nvhost_gpu::OnOpen(DeviceFD fd) {}
122void nvhost_gpu::OnClose(DeviceFD fd) {} 122void nvhost_gpu::OnClose(DeviceFD fd) {}
123 123
124NvResult nvhost_gpu::SetNVMAPfd(const std::vector<u8>& input, std::vector<u8>& output) { 124NvResult nvhost_gpu::SetNVMAPfd(std::span<const u8> input, std::vector<u8>& output) {
125 IoctlSetNvmapFD params{}; 125 IoctlSetNvmapFD params{};
126 std::memcpy(&params, input.data(), input.size()); 126 std::memcpy(&params, input.data(), input.size());
127 LOG_DEBUG(Service_NVDRV, "called, fd={}", params.nvmap_fd); 127 LOG_DEBUG(Service_NVDRV, "called, fd={}", params.nvmap_fd);
@@ -130,7 +130,7 @@ NvResult nvhost_gpu::SetNVMAPfd(const std::vector<u8>& input, std::vector<u8>& o
130 return NvResult::Success; 130 return NvResult::Success;
131} 131}
132 132
133NvResult nvhost_gpu::SetClientData(const std::vector<u8>& input, std::vector<u8>& output) { 133NvResult nvhost_gpu::SetClientData(std::span<const u8> input, std::vector<u8>& output) {
134 LOG_DEBUG(Service_NVDRV, "called"); 134 LOG_DEBUG(Service_NVDRV, "called");
135 135
136 IoctlClientData params{}; 136 IoctlClientData params{};
@@ -139,7 +139,7 @@ NvResult nvhost_gpu::SetClientData(const std::vector<u8>& input, std::vector<u8>
139 return NvResult::Success; 139 return NvResult::Success;
140} 140}
141 141
142NvResult nvhost_gpu::GetClientData(const std::vector<u8>& input, std::vector<u8>& output) { 142NvResult nvhost_gpu::GetClientData(std::span<const u8> input, std::vector<u8>& output) {
143 LOG_DEBUG(Service_NVDRV, "called"); 143 LOG_DEBUG(Service_NVDRV, "called");
144 144
145 IoctlClientData params{}; 145 IoctlClientData params{};
@@ -149,7 +149,7 @@ NvResult nvhost_gpu::GetClientData(const std::vector<u8>& input, std::vector<u8>
149 return NvResult::Success; 149 return NvResult::Success;
150} 150}
151 151
152NvResult nvhost_gpu::ZCullBind(const std::vector<u8>& input, std::vector<u8>& output) { 152NvResult nvhost_gpu::ZCullBind(std::span<const u8> input, std::vector<u8>& output) {
153 std::memcpy(&zcull_params, input.data(), input.size()); 153 std::memcpy(&zcull_params, input.data(), input.size());
154 LOG_DEBUG(Service_NVDRV, "called, gpu_va={:X}, mode={:X}", zcull_params.gpu_va, 154 LOG_DEBUG(Service_NVDRV, "called, gpu_va={:X}, mode={:X}", zcull_params.gpu_va,
155 zcull_params.mode); 155 zcull_params.mode);
@@ -158,7 +158,7 @@ NvResult nvhost_gpu::ZCullBind(const std::vector<u8>& input, std::vector<u8>& ou
158 return NvResult::Success; 158 return NvResult::Success;
159} 159}
160 160
161NvResult nvhost_gpu::SetErrorNotifier(const std::vector<u8>& input, std::vector<u8>& output) { 161NvResult nvhost_gpu::SetErrorNotifier(std::span<const u8> input, std::vector<u8>& output) {
162 IoctlSetErrorNotifier params{}; 162 IoctlSetErrorNotifier params{};
163 std::memcpy(&params, input.data(), input.size()); 163 std::memcpy(&params, input.data(), input.size());
164 LOG_WARNING(Service_NVDRV, "(STUBBED) called, offset={:X}, size={:X}, mem={:X}", params.offset, 164 LOG_WARNING(Service_NVDRV, "(STUBBED) called, offset={:X}, size={:X}, mem={:X}", params.offset,
@@ -168,14 +168,14 @@ NvResult nvhost_gpu::SetErrorNotifier(const std::vector<u8>& input, std::vector<
168 return NvResult::Success; 168 return NvResult::Success;
169} 169}
170 170
171NvResult nvhost_gpu::SetChannelPriority(const std::vector<u8>& input, std::vector<u8>& output) { 171NvResult nvhost_gpu::SetChannelPriority(std::span<const u8> input, std::vector<u8>& output) {
172 std::memcpy(&channel_priority, input.data(), input.size()); 172 std::memcpy(&channel_priority, input.data(), input.size());
173 LOG_DEBUG(Service_NVDRV, "(STUBBED) called, priority={:X}", channel_priority); 173 LOG_DEBUG(Service_NVDRV, "(STUBBED) called, priority={:X}", channel_priority);
174 174
175 return NvResult::Success; 175 return NvResult::Success;
176} 176}
177 177
178NvResult nvhost_gpu::AllocGPFIFOEx2(const std::vector<u8>& input, std::vector<u8>& output) { 178NvResult nvhost_gpu::AllocGPFIFOEx2(std::span<const u8> input, std::vector<u8>& output) {
179 IoctlAllocGpfifoEx2 params{}; 179 IoctlAllocGpfifoEx2 params{};
180 std::memcpy(&params, input.data(), input.size()); 180 std::memcpy(&params, input.data(), input.size());
181 LOG_WARNING(Service_NVDRV, 181 LOG_WARNING(Service_NVDRV,
@@ -197,7 +197,7 @@ NvResult nvhost_gpu::AllocGPFIFOEx2(const std::vector<u8>& input, std::vector<u8
197 return NvResult::Success; 197 return NvResult::Success;
198} 198}
199 199
200NvResult nvhost_gpu::AllocateObjectContext(const std::vector<u8>& input, std::vector<u8>& output) { 200NvResult nvhost_gpu::AllocateObjectContext(std::span<const u8> input, std::vector<u8>& output) {
201 IoctlAllocObjCtx params{}; 201 IoctlAllocObjCtx params{};
202 std::memcpy(&params, input.data(), input.size()); 202 std::memcpy(&params, input.data(), input.size());
203 LOG_WARNING(Service_NVDRV, "(STUBBED) called, class_num={:X}, flags={:X}", params.class_num, 203 LOG_WARNING(Service_NVDRV, "(STUBBED) called, class_num={:X}, flags={:X}", params.class_num,
@@ -293,7 +293,7 @@ NvResult nvhost_gpu::SubmitGPFIFOImpl(IoctlSubmitGpfifo& params, std::vector<u8>
293 return NvResult::Success; 293 return NvResult::Success;
294} 294}
295 295
296NvResult nvhost_gpu::SubmitGPFIFOBase(const std::vector<u8>& input, std::vector<u8>& output, 296NvResult nvhost_gpu::SubmitGPFIFOBase(std::span<const u8> input, std::vector<u8>& output,
297 bool kickoff) { 297 bool kickoff) {
298 if (input.size() < sizeof(IoctlSubmitGpfifo)) { 298 if (input.size() < sizeof(IoctlSubmitGpfifo)) {
299 UNIMPLEMENTED(); 299 UNIMPLEMENTED();
@@ -314,8 +314,7 @@ NvResult nvhost_gpu::SubmitGPFIFOBase(const std::vector<u8>& input, std::vector<
314 return SubmitGPFIFOImpl(params, output, std::move(entries)); 314 return SubmitGPFIFOImpl(params, output, std::move(entries));
315} 315}
316 316
317NvResult nvhost_gpu::SubmitGPFIFOBase(const std::vector<u8>& input, 317NvResult nvhost_gpu::SubmitGPFIFOBase(std::span<const u8> input, std::span<const u8> input_inline,
318 const std::vector<u8>& input_inline,
319 std::vector<u8>& output) { 318 std::vector<u8>& output) {
320 if (input.size() < sizeof(IoctlSubmitGpfifo)) { 319 if (input.size() < sizeof(IoctlSubmitGpfifo)) {
321 UNIMPLEMENTED(); 320 UNIMPLEMENTED();
@@ -328,7 +327,7 @@ NvResult nvhost_gpu::SubmitGPFIFOBase(const std::vector<u8>& input,
328 return SubmitGPFIFOImpl(params, output, std::move(entries)); 327 return SubmitGPFIFOImpl(params, output, std::move(entries));
329} 328}
330 329
331NvResult nvhost_gpu::GetWaitbase(const std::vector<u8>& input, std::vector<u8>& output) { 330NvResult nvhost_gpu::GetWaitbase(std::span<const u8> input, std::vector<u8>& output) {
332 IoctlGetWaitbase params{}; 331 IoctlGetWaitbase params{};
333 std::memcpy(&params, input.data(), sizeof(IoctlGetWaitbase)); 332 std::memcpy(&params, input.data(), sizeof(IoctlGetWaitbase));
334 LOG_INFO(Service_NVDRV, "called, unknown=0x{:X}", params.unknown); 333 LOG_INFO(Service_NVDRV, "called, unknown=0x{:X}", params.unknown);
@@ -338,7 +337,7 @@ NvResult nvhost_gpu::GetWaitbase(const std::vector<u8>& input, std::vector<u8>&
338 return NvResult::Success; 337 return NvResult::Success;
339} 338}
340 339
341NvResult nvhost_gpu::ChannelSetTimeout(const std::vector<u8>& input, std::vector<u8>& output) { 340NvResult nvhost_gpu::ChannelSetTimeout(std::span<const u8> input, std::vector<u8>& output) {
342 IoctlChannelSetTimeout params{}; 341 IoctlChannelSetTimeout params{};
343 std::memcpy(&params, input.data(), sizeof(IoctlChannelSetTimeout)); 342 std::memcpy(&params, input.data(), sizeof(IoctlChannelSetTimeout));
344 LOG_INFO(Service_NVDRV, "called, timeout=0x{:X}", params.timeout); 343 LOG_INFO(Service_NVDRV, "called, timeout=0x{:X}", params.timeout);
@@ -346,7 +345,7 @@ NvResult nvhost_gpu::ChannelSetTimeout(const std::vector<u8>& input, std::vector
346 return NvResult::Success; 345 return NvResult::Success;
347} 346}
348 347
349NvResult nvhost_gpu::ChannelSetTimeslice(const std::vector<u8>& input, std::vector<u8>& output) { 348NvResult nvhost_gpu::ChannelSetTimeslice(std::span<const u8> input, std::vector<u8>& output) {
350 IoctlSetTimeslice params{}; 349 IoctlSetTimeslice params{};
351 std::memcpy(&params, input.data(), sizeof(IoctlSetTimeslice)); 350 std::memcpy(&params, input.data(), sizeof(IoctlSetTimeslice));
352 LOG_INFO(Service_NVDRV, "called, timeslice=0x{:X}", params.timeslice); 351 LOG_INFO(Service_NVDRV, "called, timeslice=0x{:X}", params.timeslice);
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_gpu.h b/src/core/hle/service/nvdrv/devices/nvhost_gpu.h
index 1e4ecd55b..3ca58202d 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_gpu.h
+++ b/src/core/hle/service/nvdrv/devices/nvhost_gpu.h
@@ -40,12 +40,12 @@ public:
40 NvCore::Container& core); 40 NvCore::Container& core);
41 ~nvhost_gpu() override; 41 ~nvhost_gpu() override;
42 42
43 NvResult Ioctl1(DeviceFD fd, Ioctl command, const std::vector<u8>& input, 43 NvResult Ioctl1(DeviceFD fd, Ioctl command, std::span<const u8> input,
44 std::vector<u8>& output) override; 44 std::vector<u8>& output) override;
45 NvResult Ioctl2(DeviceFD fd, Ioctl command, const std::vector<u8>& input, 45 NvResult Ioctl2(DeviceFD fd, Ioctl command, std::span<const u8> input,
46 const std::vector<u8>& inline_input, std::vector<u8>& output) override; 46 std::span<const u8> inline_input, std::vector<u8>& output) override;
47 NvResult Ioctl3(DeviceFD fd, Ioctl command, const std::vector<u8>& input, 47 NvResult Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> input, std::vector<u8>& output,
48 std::vector<u8>& output, std::vector<u8>& inline_output) override; 48 std::vector<u8>& inline_output) override;
49 49
50 void OnOpen(DeviceFD fd) override; 50 void OnOpen(DeviceFD fd) override;
51 void OnClose(DeviceFD fd) override; 51 void OnClose(DeviceFD fd) override;
@@ -186,23 +186,23 @@ private:
186 u32_le channel_priority{}; 186 u32_le channel_priority{};
187 u32_le channel_timeslice{}; 187 u32_le channel_timeslice{};
188 188
189 NvResult SetNVMAPfd(const std::vector<u8>& input, std::vector<u8>& output); 189 NvResult SetNVMAPfd(std::span<const u8> input, std::vector<u8>& output);
190 NvResult SetClientData(const std::vector<u8>& input, std::vector<u8>& output); 190 NvResult SetClientData(std::span<const u8> input, std::vector<u8>& output);
191 NvResult GetClientData(const std::vector<u8>& input, std::vector<u8>& output); 191 NvResult GetClientData(std::span<const u8> input, std::vector<u8>& output);
192 NvResult ZCullBind(const std::vector<u8>& input, std::vector<u8>& output); 192 NvResult ZCullBind(std::span<const u8> input, std::vector<u8>& output);
193 NvResult SetErrorNotifier(const std::vector<u8>& input, std::vector<u8>& output); 193 NvResult SetErrorNotifier(std::span<const u8> input, std::vector<u8>& output);
194 NvResult SetChannelPriority(const std::vector<u8>& input, std::vector<u8>& output); 194 NvResult SetChannelPriority(std::span<const u8> input, std::vector<u8>& output);
195 NvResult AllocGPFIFOEx2(const std::vector<u8>& input, std::vector<u8>& output); 195 NvResult AllocGPFIFOEx2(std::span<const u8> input, std::vector<u8>& output);
196 NvResult AllocateObjectContext(const std::vector<u8>& input, std::vector<u8>& output); 196 NvResult AllocateObjectContext(std::span<const u8> input, std::vector<u8>& output);
197 NvResult SubmitGPFIFOImpl(IoctlSubmitGpfifo& params, std::vector<u8>& output, 197 NvResult SubmitGPFIFOImpl(IoctlSubmitGpfifo& params, std::vector<u8>& output,
198 Tegra::CommandList&& entries); 198 Tegra::CommandList&& entries);
199 NvResult SubmitGPFIFOBase(const std::vector<u8>& input, std::vector<u8>& output, 199 NvResult SubmitGPFIFOBase(std::span<const u8> input, std::vector<u8>& output,
200 bool kickoff = false); 200 bool kickoff = false);
201 NvResult SubmitGPFIFOBase(const std::vector<u8>& input, const std::vector<u8>& input_inline, 201 NvResult SubmitGPFIFOBase(std::span<const u8> input, std::span<const u8> input_inline,
202 std::vector<u8>& output); 202 std::vector<u8>& output);
203 NvResult GetWaitbase(const std::vector<u8>& input, std::vector<u8>& output); 203 NvResult GetWaitbase(std::span<const u8> input, std::vector<u8>& output);
204 NvResult ChannelSetTimeout(const std::vector<u8>& input, std::vector<u8>& output); 204 NvResult ChannelSetTimeout(std::span<const u8> input, std::vector<u8>& output);
205 NvResult ChannelSetTimeslice(const std::vector<u8>& input, std::vector<u8>& output); 205 NvResult ChannelSetTimeslice(std::span<const u8> input, std::vector<u8>& output);
206 206
207 EventInterface& events_interface; 207 EventInterface& events_interface;
208 NvCore::Container& core; 208 NvCore::Container& core;
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp b/src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp
index 1703f9cc3..0c7aee1b8 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp
@@ -15,7 +15,7 @@ nvhost_nvdec::nvhost_nvdec(Core::System& system_, NvCore::Container& core_)
15 : nvhost_nvdec_common{system_, core_, NvCore::ChannelType::NvDec} {} 15 : nvhost_nvdec_common{system_, core_, NvCore::ChannelType::NvDec} {}
16nvhost_nvdec::~nvhost_nvdec() = default; 16nvhost_nvdec::~nvhost_nvdec() = default;
17 17
18NvResult nvhost_nvdec::Ioctl1(DeviceFD fd, Ioctl command, const std::vector<u8>& input, 18NvResult nvhost_nvdec::Ioctl1(DeviceFD fd, Ioctl command, std::span<const u8> input,
19 std::vector<u8>& output) { 19 std::vector<u8>& output) {
20 switch (command.group) { 20 switch (command.group) {
21 case 0x0: 21 case 0x0:
@@ -55,13 +55,13 @@ NvResult nvhost_nvdec::Ioctl1(DeviceFD fd, Ioctl command, const std::vector<u8>&
55 return NvResult::NotImplemented; 55 return NvResult::NotImplemented;
56} 56}
57 57
58NvResult nvhost_nvdec::Ioctl2(DeviceFD fd, Ioctl command, const std::vector<u8>& input, 58NvResult nvhost_nvdec::Ioctl2(DeviceFD fd, Ioctl command, std::span<const u8> input,
59 const std::vector<u8>& inline_input, std::vector<u8>& output) { 59 std::span<const u8> inline_input, std::vector<u8>& output) {
60 UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw); 60 UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);
61 return NvResult::NotImplemented; 61 return NvResult::NotImplemented;
62} 62}
63 63
64NvResult nvhost_nvdec::Ioctl3(DeviceFD fd, Ioctl command, const std::vector<u8>& input, 64NvResult nvhost_nvdec::Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> input,
65 std::vector<u8>& output, std::vector<u8>& inline_output) { 65 std::vector<u8>& output, std::vector<u8>& inline_output) {
66 UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw); 66 UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);
67 return NvResult::NotImplemented; 67 return NvResult::NotImplemented;
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_nvdec.h b/src/core/hle/service/nvdrv/devices/nvhost_nvdec.h
index c1b4e53e8..0d615bbcb 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_nvdec.h
+++ b/src/core/hle/service/nvdrv/devices/nvhost_nvdec.h
@@ -13,12 +13,12 @@ public:
13 explicit nvhost_nvdec(Core::System& system_, NvCore::Container& core); 13 explicit nvhost_nvdec(Core::System& system_, NvCore::Container& core);
14 ~nvhost_nvdec() override; 14 ~nvhost_nvdec() override;
15 15
16 NvResult Ioctl1(DeviceFD fd, Ioctl command, const std::vector<u8>& input, 16 NvResult Ioctl1(DeviceFD fd, Ioctl command, std::span<const u8> input,
17 std::vector<u8>& output) override; 17 std::vector<u8>& output) override;
18 NvResult Ioctl2(DeviceFD fd, Ioctl command, const std::vector<u8>& input, 18 NvResult Ioctl2(DeviceFD fd, Ioctl command, std::span<const u8> input,
19 const std::vector<u8>& inline_input, std::vector<u8>& output) override; 19 std::span<const u8> inline_input, std::vector<u8>& output) override;
20 NvResult Ioctl3(DeviceFD fd, Ioctl command, const std::vector<u8>& input, 20 NvResult Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> input, std::vector<u8>& output,
21 std::vector<u8>& output, std::vector<u8>& inline_output) override; 21 std::vector<u8>& inline_output) override;
22 22
23 void OnOpen(DeviceFD fd) override; 23 void OnOpen(DeviceFD fd) override;
24 void OnClose(DeviceFD fd) override; 24 void OnClose(DeviceFD fd) override;
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.cpp b/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.cpp
index 99eede702..7bcef105b 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.cpp
@@ -23,7 +23,7 @@ namespace {
23// Copies count amount of type T from the input vector into the dst vector. 23// Copies count amount of type T from the input vector into the dst vector.
24// Returns the number of bytes written into dst. 24// Returns the number of bytes written into dst.
25template <typename T> 25template <typename T>
26std::size_t SliceVectors(const std::vector<u8>& input, std::vector<T>& dst, std::size_t count, 26std::size_t SliceVectors(std::span<const u8> input, std::vector<T>& dst, std::size_t count,
27 std::size_t offset) { 27 std::size_t offset) {
28 if (dst.empty()) { 28 if (dst.empty()) {
29 return 0; 29 return 0;
@@ -63,7 +63,7 @@ nvhost_nvdec_common::~nvhost_nvdec_common() {
63 core.Host1xDeviceFile().syncpts_accumulated.push_back(channel_syncpoint); 63 core.Host1xDeviceFile().syncpts_accumulated.push_back(channel_syncpoint);
64} 64}
65 65
66NvResult nvhost_nvdec_common::SetNVMAPfd(const std::vector<u8>& input) { 66NvResult nvhost_nvdec_common::SetNVMAPfd(std::span<const u8> input) {
67 IoctlSetNvmapFD params{}; 67 IoctlSetNvmapFD params{};
68 std::memcpy(&params, input.data(), sizeof(IoctlSetNvmapFD)); 68 std::memcpy(&params, input.data(), sizeof(IoctlSetNvmapFD));
69 LOG_DEBUG(Service_NVDRV, "called, fd={}", params.nvmap_fd); 69 LOG_DEBUG(Service_NVDRV, "called, fd={}", params.nvmap_fd);
@@ -72,7 +72,7 @@ NvResult nvhost_nvdec_common::SetNVMAPfd(const std::vector<u8>& input) {
72 return NvResult::Success; 72 return NvResult::Success;
73} 73}
74 74
75NvResult nvhost_nvdec_common::Submit(DeviceFD fd, const std::vector<u8>& input, 75NvResult nvhost_nvdec_common::Submit(DeviceFD fd, std::span<const u8> input,
76 std::vector<u8>& output) { 76 std::vector<u8>& output) {
77 IoctlSubmit params{}; 77 IoctlSubmit params{};
78 std::memcpy(&params, input.data(), sizeof(IoctlSubmit)); 78 std::memcpy(&params, input.data(), sizeof(IoctlSubmit));
@@ -121,7 +121,7 @@ NvResult nvhost_nvdec_common::Submit(DeviceFD fd, const std::vector<u8>& input,
121 return NvResult::Success; 121 return NvResult::Success;
122} 122}
123 123
124NvResult nvhost_nvdec_common::GetSyncpoint(const std::vector<u8>& input, std::vector<u8>& output) { 124NvResult nvhost_nvdec_common::GetSyncpoint(std::span<const u8> input, std::vector<u8>& output) {
125 IoctlGetSyncpoint params{}; 125 IoctlGetSyncpoint params{};
126 std::memcpy(&params, input.data(), sizeof(IoctlGetSyncpoint)); 126 std::memcpy(&params, input.data(), sizeof(IoctlGetSyncpoint));
127 LOG_DEBUG(Service_NVDRV, "called GetSyncpoint, id={}", params.param); 127 LOG_DEBUG(Service_NVDRV, "called GetSyncpoint, id={}", params.param);
@@ -133,7 +133,7 @@ NvResult nvhost_nvdec_common::GetSyncpoint(const std::vector<u8>& input, std::ve
133 return NvResult::Success; 133 return NvResult::Success;
134} 134}
135 135
136NvResult nvhost_nvdec_common::GetWaitbase(const std::vector<u8>& input, std::vector<u8>& output) { 136NvResult nvhost_nvdec_common::GetWaitbase(std::span<const u8> input, std::vector<u8>& output) {
137 IoctlGetWaitbase params{}; 137 IoctlGetWaitbase params{};
138 LOG_CRITICAL(Service_NVDRV, "called WAITBASE"); 138 LOG_CRITICAL(Service_NVDRV, "called WAITBASE");
139 std::memcpy(&params, input.data(), sizeof(IoctlGetWaitbase)); 139 std::memcpy(&params, input.data(), sizeof(IoctlGetWaitbase));
@@ -142,7 +142,7 @@ NvResult nvhost_nvdec_common::GetWaitbase(const std::vector<u8>& input, std::vec
142 return NvResult::Success; 142 return NvResult::Success;
143} 143}
144 144
145NvResult nvhost_nvdec_common::MapBuffer(const std::vector<u8>& input, std::vector<u8>& output) { 145NvResult nvhost_nvdec_common::MapBuffer(std::span<const u8> input, std::vector<u8>& output) {
146 IoctlMapBuffer params{}; 146 IoctlMapBuffer params{};
147 std::memcpy(&params, input.data(), sizeof(IoctlMapBuffer)); 147 std::memcpy(&params, input.data(), sizeof(IoctlMapBuffer));
148 std::vector<MapBufferEntry> cmd_buffer_handles(params.num_entries); 148 std::vector<MapBufferEntry> cmd_buffer_handles(params.num_entries);
@@ -159,7 +159,7 @@ NvResult nvhost_nvdec_common::MapBuffer(const std::vector<u8>& input, std::vecto
159 return NvResult::Success; 159 return NvResult::Success;
160} 160}
161 161
162NvResult nvhost_nvdec_common::UnmapBuffer(const std::vector<u8>& input, std::vector<u8>& output) { 162NvResult nvhost_nvdec_common::UnmapBuffer(std::span<const u8> input, std::vector<u8>& output) {
163 IoctlMapBuffer params{}; 163 IoctlMapBuffer params{};
164 std::memcpy(&params, input.data(), sizeof(IoctlMapBuffer)); 164 std::memcpy(&params, input.data(), sizeof(IoctlMapBuffer));
165 std::vector<MapBufferEntry> cmd_buffer_handles(params.num_entries); 165 std::vector<MapBufferEntry> cmd_buffer_handles(params.num_entries);
@@ -173,8 +173,7 @@ NvResult nvhost_nvdec_common::UnmapBuffer(const std::vector<u8>& input, std::vec
173 return NvResult::Success; 173 return NvResult::Success;
174} 174}
175 175
176NvResult nvhost_nvdec_common::SetSubmitTimeout(const std::vector<u8>& input, 176NvResult nvhost_nvdec_common::SetSubmitTimeout(std::span<const u8> input, std::vector<u8>& output) {
177 std::vector<u8>& output) {
178 std::memcpy(&submit_timeout, input.data(), input.size()); 177 std::memcpy(&submit_timeout, input.data(), input.size());
179 LOG_WARNING(Service_NVDRV, "(STUBBED) called"); 178 LOG_WARNING(Service_NVDRV, "(STUBBED) called");
180 return NvResult::Success; 179 return NvResult::Success;
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.h b/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.h
index fe76100c8..5af26a26f 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.h
+++ b/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.h
@@ -107,13 +107,13 @@ protected:
107 static_assert(sizeof(IoctlMapBuffer) == 0x0C, "IoctlMapBuffer is incorrect size"); 107 static_assert(sizeof(IoctlMapBuffer) == 0x0C, "IoctlMapBuffer is incorrect size");
108 108
109 /// Ioctl command implementations 109 /// Ioctl command implementations
110 NvResult SetNVMAPfd(const std::vector<u8>& input); 110 NvResult SetNVMAPfd(std::span<const u8> input);
111 NvResult Submit(DeviceFD fd, const std::vector<u8>& input, std::vector<u8>& output); 111 NvResult Submit(DeviceFD fd, std::span<const u8> input, std::vector<u8>& output);
112 NvResult GetSyncpoint(const std::vector<u8>& input, std::vector<u8>& output); 112 NvResult GetSyncpoint(std::span<const u8> input, std::vector<u8>& output);
113 NvResult GetWaitbase(const std::vector<u8>& input, std::vector<u8>& output); 113 NvResult GetWaitbase(std::span<const u8> input, std::vector<u8>& output);
114 NvResult MapBuffer(const std::vector<u8>& input, std::vector<u8>& output); 114 NvResult MapBuffer(std::span<const u8> input, std::vector<u8>& output);
115 NvResult UnmapBuffer(const std::vector<u8>& input, std::vector<u8>& output); 115 NvResult UnmapBuffer(std::span<const u8> input, std::vector<u8>& output);
116 NvResult SetSubmitTimeout(const std::vector<u8>& input, std::vector<u8>& output); 116 NvResult SetSubmitTimeout(std::span<const u8> input, std::vector<u8>& output);
117 117
118 Kernel::KEvent* QueryEvent(u32 event_id) override; 118 Kernel::KEvent* QueryEvent(u32 event_id) override;
119 119
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_nvjpg.cpp b/src/core/hle/service/nvdrv/devices/nvhost_nvjpg.cpp
index bdbc2f9e1..39f30e7c8 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_nvjpg.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvhost_nvjpg.cpp
@@ -12,7 +12,7 @@ namespace Service::Nvidia::Devices {
12nvhost_nvjpg::nvhost_nvjpg(Core::System& system_) : nvdevice{system_} {} 12nvhost_nvjpg::nvhost_nvjpg(Core::System& system_) : nvdevice{system_} {}
13nvhost_nvjpg::~nvhost_nvjpg() = default; 13nvhost_nvjpg::~nvhost_nvjpg() = default;
14 14
15NvResult nvhost_nvjpg::Ioctl1(DeviceFD fd, Ioctl command, const std::vector<u8>& input, 15NvResult nvhost_nvjpg::Ioctl1(DeviceFD fd, Ioctl command, std::span<const u8> input,
16 std::vector<u8>& output) { 16 std::vector<u8>& output) {
17 switch (command.group) { 17 switch (command.group) {
18 case 'H': 18 case 'H':
@@ -31,13 +31,13 @@ NvResult nvhost_nvjpg::Ioctl1(DeviceFD fd, Ioctl command, const std::vector<u8>&
31 return NvResult::NotImplemented; 31 return NvResult::NotImplemented;
32} 32}
33 33
34NvResult nvhost_nvjpg::Ioctl2(DeviceFD fd, Ioctl command, const std::vector<u8>& input, 34NvResult nvhost_nvjpg::Ioctl2(DeviceFD fd, Ioctl command, std::span<const u8> input,
35 const std::vector<u8>& inline_input, std::vector<u8>& output) { 35 std::span<const u8> inline_input, std::vector<u8>& output) {
36 UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw); 36 UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);
37 return NvResult::NotImplemented; 37 return NvResult::NotImplemented;
38} 38}
39 39
40NvResult nvhost_nvjpg::Ioctl3(DeviceFD fd, Ioctl command, const std::vector<u8>& input, 40NvResult nvhost_nvjpg::Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> input,
41 std::vector<u8>& output, std::vector<u8>& inline_output) { 41 std::vector<u8>& output, std::vector<u8>& inline_output) {
42 UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw); 42 UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);
43 return NvResult::NotImplemented; 43 return NvResult::NotImplemented;
@@ -46,7 +46,7 @@ NvResult nvhost_nvjpg::Ioctl3(DeviceFD fd, Ioctl command, const std::vector<u8>&
46void nvhost_nvjpg::OnOpen(DeviceFD fd) {} 46void nvhost_nvjpg::OnOpen(DeviceFD fd) {}
47void nvhost_nvjpg::OnClose(DeviceFD fd) {} 47void nvhost_nvjpg::OnClose(DeviceFD fd) {}
48 48
49NvResult nvhost_nvjpg::SetNVMAPfd(const std::vector<u8>& input, std::vector<u8>& output) { 49NvResult nvhost_nvjpg::SetNVMAPfd(std::span<const u8> input, std::vector<u8>& output) {
50 IoctlSetNvmapFD params{}; 50 IoctlSetNvmapFD params{};
51 std::memcpy(&params, input.data(), input.size()); 51 std::memcpy(&params, input.data(), input.size());
52 LOG_DEBUG(Service_NVDRV, "called, fd={}", params.nvmap_fd); 52 LOG_DEBUG(Service_NVDRV, "called, fd={}", params.nvmap_fd);
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_nvjpg.h b/src/core/hle/service/nvdrv/devices/nvhost_nvjpg.h
index 440e7d371..41b57e872 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_nvjpg.h
+++ b/src/core/hle/service/nvdrv/devices/nvhost_nvjpg.h
@@ -15,12 +15,12 @@ public:
15 explicit nvhost_nvjpg(Core::System& system_); 15 explicit nvhost_nvjpg(Core::System& system_);
16 ~nvhost_nvjpg() override; 16 ~nvhost_nvjpg() override;
17 17
18 NvResult Ioctl1(DeviceFD fd, Ioctl command, const std::vector<u8>& input, 18 NvResult Ioctl1(DeviceFD fd, Ioctl command, std::span<const u8> input,
19 std::vector<u8>& output) override; 19 std::vector<u8>& output) override;
20 NvResult Ioctl2(DeviceFD fd, Ioctl command, const std::vector<u8>& input, 20 NvResult Ioctl2(DeviceFD fd, Ioctl command, std::span<const u8> input,
21 const std::vector<u8>& inline_input, std::vector<u8>& output) override; 21 std::span<const u8> inline_input, std::vector<u8>& output) override;
22 NvResult Ioctl3(DeviceFD fd, Ioctl command, const std::vector<u8>& input, 22 NvResult Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> input, std::vector<u8>& output,
23 std::vector<u8>& output, std::vector<u8>& inline_output) override; 23 std::vector<u8>& inline_output) override;
24 24
25 void OnOpen(DeviceFD fd) override; 25 void OnOpen(DeviceFD fd) override;
26 void OnClose(DeviceFD fd) override; 26 void OnClose(DeviceFD fd) override;
@@ -33,7 +33,7 @@ private:
33 33
34 s32_le nvmap_fd{}; 34 s32_le nvmap_fd{};
35 35
36 NvResult SetNVMAPfd(const std::vector<u8>& input, std::vector<u8>& output); 36 NvResult SetNVMAPfd(std::span<const u8> input, std::vector<u8>& output);
37}; 37};
38 38
39} // namespace Service::Nvidia::Devices 39} // namespace Service::Nvidia::Devices
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_vic.cpp b/src/core/hle/service/nvdrv/devices/nvhost_vic.cpp
index 73f97136e..b0ea402a7 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_vic.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvhost_vic.cpp
@@ -15,7 +15,7 @@ nvhost_vic::nvhost_vic(Core::System& system_, NvCore::Container& core_)
15 15
16nvhost_vic::~nvhost_vic() = default; 16nvhost_vic::~nvhost_vic() = default;
17 17
18NvResult nvhost_vic::Ioctl1(DeviceFD fd, Ioctl command, const std::vector<u8>& input, 18NvResult nvhost_vic::Ioctl1(DeviceFD fd, Ioctl command, std::span<const u8> input,
19 std::vector<u8>& output) { 19 std::vector<u8>& output) {
20 switch (command.group) { 20 switch (command.group) {
21 case 0x0: 21 case 0x0:
@@ -55,13 +55,13 @@ NvResult nvhost_vic::Ioctl1(DeviceFD fd, Ioctl command, const std::vector<u8>& i
55 return NvResult::NotImplemented; 55 return NvResult::NotImplemented;
56} 56}
57 57
58NvResult nvhost_vic::Ioctl2(DeviceFD fd, Ioctl command, const std::vector<u8>& input, 58NvResult nvhost_vic::Ioctl2(DeviceFD fd, Ioctl command, std::span<const u8> input,
59 const std::vector<u8>& inline_input, std::vector<u8>& output) { 59 std::span<const u8> inline_input, std::vector<u8>& output) {
60 UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw); 60 UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);
61 return NvResult::NotImplemented; 61 return NvResult::NotImplemented;
62} 62}
63 63
64NvResult nvhost_vic::Ioctl3(DeviceFD fd, Ioctl command, const std::vector<u8>& input, 64NvResult nvhost_vic::Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> input,
65 std::vector<u8>& output, std::vector<u8>& inline_output) { 65 std::vector<u8>& output, std::vector<u8>& inline_output) {
66 UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw); 66 UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);
67 return NvResult::NotImplemented; 67 return NvResult::NotImplemented;
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_vic.h b/src/core/hle/service/nvdrv/devices/nvhost_vic.h
index f164caafb..b5e350a83 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_vic.h
+++ b/src/core/hle/service/nvdrv/devices/nvhost_vic.h
@@ -12,12 +12,12 @@ public:
12 explicit nvhost_vic(Core::System& system_, NvCore::Container& core); 12 explicit nvhost_vic(Core::System& system_, NvCore::Container& core);
13 ~nvhost_vic(); 13 ~nvhost_vic();
14 14
15 NvResult Ioctl1(DeviceFD fd, Ioctl command, const std::vector<u8>& input, 15 NvResult Ioctl1(DeviceFD fd, Ioctl command, std::span<const u8> input,
16 std::vector<u8>& output) override; 16 std::vector<u8>& output) override;
17 NvResult Ioctl2(DeviceFD fd, Ioctl command, const std::vector<u8>& input, 17 NvResult Ioctl2(DeviceFD fd, Ioctl command, std::span<const u8> input,
18 const std::vector<u8>& inline_input, std::vector<u8>& output) override; 18 std::span<const u8> inline_input, std::vector<u8>& output) override;
19 NvResult Ioctl3(DeviceFD fd, Ioctl command, const std::vector<u8>& input, 19 NvResult Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> input, std::vector<u8>& output,
20 std::vector<u8>& output, std::vector<u8>& inline_output) override; 20 std::vector<u8>& inline_output) override;
21 21
22 void OnOpen(DeviceFD fd) override; 22 void OnOpen(DeviceFD fd) override;
23 void OnClose(DeviceFD fd) override; 23 void OnClose(DeviceFD fd) override;
diff --git a/src/core/hle/service/nvdrv/devices/nvmap.cpp b/src/core/hle/service/nvdrv/devices/nvmap.cpp
index fa29db758..29c1e0f01 100644
--- a/src/core/hle/service/nvdrv/devices/nvmap.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvmap.cpp
@@ -25,7 +25,7 @@ nvmap::nvmap(Core::System& system_, NvCore::Container& container_)
25 25
26nvmap::~nvmap() = default; 26nvmap::~nvmap() = default;
27 27
28NvResult nvmap::Ioctl1(DeviceFD fd, Ioctl command, const std::vector<u8>& input, 28NvResult nvmap::Ioctl1(DeviceFD fd, Ioctl command, std::span<const u8> input,
29 std::vector<u8>& output) { 29 std::vector<u8>& output) {
30 switch (command.group) { 30 switch (command.group) {
31 case 0x1: 31 case 0x1:
@@ -54,13 +54,13 @@ NvResult nvmap::Ioctl1(DeviceFD fd, Ioctl command, const std::vector<u8>& input,
54 return NvResult::NotImplemented; 54 return NvResult::NotImplemented;
55} 55}
56 56
57NvResult nvmap::Ioctl2(DeviceFD fd, Ioctl command, const std::vector<u8>& input, 57NvResult nvmap::Ioctl2(DeviceFD fd, Ioctl command, std::span<const u8> input,
58 const std::vector<u8>& inline_input, std::vector<u8>& output) { 58 std::span<const u8> inline_input, std::vector<u8>& output) {
59 UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw); 59 UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);
60 return NvResult::NotImplemented; 60 return NvResult::NotImplemented;
61} 61}
62 62
63NvResult nvmap::Ioctl3(DeviceFD fd, Ioctl command, const std::vector<u8>& input, 63NvResult nvmap::Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> input,
64 std::vector<u8>& output, std::vector<u8>& inline_output) { 64 std::vector<u8>& output, std::vector<u8>& inline_output) {
65 UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw); 65 UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);
66 return NvResult::NotImplemented; 66 return NvResult::NotImplemented;
@@ -69,7 +69,7 @@ NvResult nvmap::Ioctl3(DeviceFD fd, Ioctl command, const std::vector<u8>& input,
69void nvmap::OnOpen(DeviceFD fd) {} 69void nvmap::OnOpen(DeviceFD fd) {}
70void nvmap::OnClose(DeviceFD fd) {} 70void nvmap::OnClose(DeviceFD fd) {}
71 71
72NvResult nvmap::IocCreate(const std::vector<u8>& input, std::vector<u8>& output) { 72NvResult nvmap::IocCreate(std::span<const u8> input, std::vector<u8>& output) {
73 IocCreateParams params; 73 IocCreateParams params;
74 std::memcpy(&params, input.data(), sizeof(params)); 74 std::memcpy(&params, input.data(), sizeof(params));
75 LOG_DEBUG(Service_NVDRV, "called, size=0x{:08X}", params.size); 75 LOG_DEBUG(Service_NVDRV, "called, size=0x{:08X}", params.size);
@@ -89,7 +89,7 @@ NvResult nvmap::IocCreate(const std::vector<u8>& input, std::vector<u8>& output)
89 return NvResult::Success; 89 return NvResult::Success;
90} 90}
91 91
92NvResult nvmap::IocAlloc(const std::vector<u8>& input, std::vector<u8>& output) { 92NvResult nvmap::IocAlloc(std::span<const u8> input, std::vector<u8>& output) {
93 IocAllocParams params; 93 IocAllocParams params;
94 std::memcpy(&params, input.data(), sizeof(params)); 94 std::memcpy(&params, input.data(), sizeof(params));
95 LOG_DEBUG(Service_NVDRV, "called, addr={:X}", params.address); 95 LOG_DEBUG(Service_NVDRV, "called, addr={:X}", params.address);
@@ -137,7 +137,7 @@ NvResult nvmap::IocAlloc(const std::vector<u8>& input, std::vector<u8>& output)
137 return result; 137 return result;
138} 138}
139 139
140NvResult nvmap::IocGetId(const std::vector<u8>& input, std::vector<u8>& output) { 140NvResult nvmap::IocGetId(std::span<const u8> input, std::vector<u8>& output) {
141 IocGetIdParams params; 141 IocGetIdParams params;
142 std::memcpy(&params, input.data(), sizeof(params)); 142 std::memcpy(&params, input.data(), sizeof(params));
143 143
@@ -161,7 +161,7 @@ NvResult nvmap::IocGetId(const std::vector<u8>& input, std::vector<u8>& output)
161 return NvResult::Success; 161 return NvResult::Success;
162} 162}
163 163
164NvResult nvmap::IocFromId(const std::vector<u8>& input, std::vector<u8>& output) { 164NvResult nvmap::IocFromId(std::span<const u8> input, std::vector<u8>& output) {
165 IocFromIdParams params; 165 IocFromIdParams params;
166 std::memcpy(&params, input.data(), sizeof(params)); 166 std::memcpy(&params, input.data(), sizeof(params));
167 167
@@ -192,7 +192,7 @@ NvResult nvmap::IocFromId(const std::vector<u8>& input, std::vector<u8>& output)
192 return NvResult::Success; 192 return NvResult::Success;
193} 193}
194 194
195NvResult nvmap::IocParam(const std::vector<u8>& input, std::vector<u8>& output) { 195NvResult nvmap::IocParam(std::span<const u8> input, std::vector<u8>& output) {
196 enum class ParamTypes { Size = 1, Alignment = 2, Base = 3, Heap = 4, Kind = 5, Compr = 6 }; 196 enum class ParamTypes { Size = 1, Alignment = 2, Base = 3, Heap = 4, Kind = 5, Compr = 6 };
197 197
198 IocParamParams params; 198 IocParamParams params;
@@ -241,7 +241,7 @@ NvResult nvmap::IocParam(const std::vector<u8>& input, std::vector<u8>& output)
241 return NvResult::Success; 241 return NvResult::Success;
242} 242}
243 243
244NvResult nvmap::IocFree(const std::vector<u8>& input, std::vector<u8>& output) { 244NvResult nvmap::IocFree(std::span<const u8> input, std::vector<u8>& output) {
245 IocFreeParams params; 245 IocFreeParams params;
246 std::memcpy(&params, input.data(), sizeof(params)); 246 std::memcpy(&params, input.data(), sizeof(params));
247 247
diff --git a/src/core/hle/service/nvdrv/devices/nvmap.h b/src/core/hle/service/nvdrv/devices/nvmap.h
index e9bfd0358..82bd3b118 100644
--- a/src/core/hle/service/nvdrv/devices/nvmap.h
+++ b/src/core/hle/service/nvdrv/devices/nvmap.h
@@ -26,12 +26,12 @@ public:
26 nvmap(const nvmap&) = delete; 26 nvmap(const nvmap&) = delete;
27 nvmap& operator=(const nvmap&) = delete; 27 nvmap& operator=(const nvmap&) = delete;
28 28
29 NvResult Ioctl1(DeviceFD fd, Ioctl command, const std::vector<u8>& input, 29 NvResult Ioctl1(DeviceFD fd, Ioctl command, std::span<const u8> input,
30 std::vector<u8>& output) override; 30 std::vector<u8>& output) override;
31 NvResult Ioctl2(DeviceFD fd, Ioctl command, const std::vector<u8>& input, 31 NvResult Ioctl2(DeviceFD fd, Ioctl command, std::span<const u8> input,
32 const std::vector<u8>& inline_input, std::vector<u8>& output) override; 32 std::span<const u8> inline_input, std::vector<u8>& output) override;
33 NvResult Ioctl3(DeviceFD fd, Ioctl command, const std::vector<u8>& input, 33 NvResult Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> input, std::vector<u8>& output,
34 std::vector<u8>& output, std::vector<u8>& inline_output) override; 34 std::vector<u8>& inline_output) override;
35 35
36 void OnOpen(DeviceFD fd) override; 36 void OnOpen(DeviceFD fd) override;
37 void OnClose(DeviceFD fd) override; 37 void OnClose(DeviceFD fd) override;
@@ -106,12 +106,12 @@ private:
106 }; 106 };
107 static_assert(sizeof(IocGetIdParams) == 8, "IocGetIdParams has wrong size"); 107 static_assert(sizeof(IocGetIdParams) == 8, "IocGetIdParams has wrong size");
108 108
109 NvResult IocCreate(const std::vector<u8>& input, std::vector<u8>& output); 109 NvResult IocCreate(std::span<const u8> input, std::vector<u8>& output);
110 NvResult IocAlloc(const std::vector<u8>& input, std::vector<u8>& output); 110 NvResult IocAlloc(std::span<const u8> input, std::vector<u8>& output);
111 NvResult IocGetId(const std::vector<u8>& input, std::vector<u8>& output); 111 NvResult IocGetId(std::span<const u8> input, std::vector<u8>& output);
112 NvResult IocFromId(const std::vector<u8>& input, std::vector<u8>& output); 112 NvResult IocFromId(std::span<const u8> input, std::vector<u8>& output);
113 NvResult IocParam(const std::vector<u8>& input, std::vector<u8>& output); 113 NvResult IocParam(std::span<const u8> input, std::vector<u8>& output);
114 NvResult IocFree(const std::vector<u8>& input, std::vector<u8>& output); 114 NvResult IocFree(std::span<const u8> input, std::vector<u8>& output);
115 115
116 NvCore::Container& container; 116 NvCore::Container& container;
117 NvCore::NvMap& file; 117 NvCore::NvMap& file;
diff --git a/src/core/hle/service/nvdrv/nvdrv.cpp b/src/core/hle/service/nvdrv/nvdrv.cpp
index 6fc8565c0..52d27e755 100644
--- a/src/core/hle/service/nvdrv/nvdrv.cpp
+++ b/src/core/hle/service/nvdrv/nvdrv.cpp
@@ -124,7 +124,7 @@ DeviceFD Module::Open(const std::string& device_name) {
124 return fd; 124 return fd;
125} 125}
126 126
127NvResult Module::Ioctl1(DeviceFD fd, Ioctl command, const std::vector<u8>& input, 127NvResult Module::Ioctl1(DeviceFD fd, Ioctl command, std::span<const u8> input,
128 std::vector<u8>& output) { 128 std::vector<u8>& output) {
129 if (fd < 0) { 129 if (fd < 0) {
130 LOG_ERROR(Service_NVDRV, "Invalid DeviceFD={}!", fd); 130 LOG_ERROR(Service_NVDRV, "Invalid DeviceFD={}!", fd);
@@ -141,8 +141,8 @@ NvResult Module::Ioctl1(DeviceFD fd, Ioctl command, const std::vector<u8>& input
141 return itr->second->Ioctl1(fd, command, input, output); 141 return itr->second->Ioctl1(fd, command, input, output);
142} 142}
143 143
144NvResult Module::Ioctl2(DeviceFD fd, Ioctl command, const std::vector<u8>& input, 144NvResult Module::Ioctl2(DeviceFD fd, Ioctl command, std::span<const u8> input,
145 const std::vector<u8>& inline_input, std::vector<u8>& output) { 145 std::span<const u8> inline_input, std::vector<u8>& output) {
146 if (fd < 0) { 146 if (fd < 0) {
147 LOG_ERROR(Service_NVDRV, "Invalid DeviceFD={}!", fd); 147 LOG_ERROR(Service_NVDRV, "Invalid DeviceFD={}!", fd);
148 return NvResult::InvalidState; 148 return NvResult::InvalidState;
@@ -158,7 +158,7 @@ NvResult Module::Ioctl2(DeviceFD fd, Ioctl command, const std::vector<u8>& input
158 return itr->second->Ioctl2(fd, command, input, inline_input, output); 158 return itr->second->Ioctl2(fd, command, input, inline_input, output);
159} 159}
160 160
161NvResult Module::Ioctl3(DeviceFD fd, Ioctl command, const std::vector<u8>& input, 161NvResult Module::Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> input,
162 std::vector<u8>& output, std::vector<u8>& inline_output) { 162 std::vector<u8>& output, std::vector<u8>& inline_output) {
163 if (fd < 0) { 163 if (fd < 0) {
164 LOG_ERROR(Service_NVDRV, "Invalid DeviceFD={}!", fd); 164 LOG_ERROR(Service_NVDRV, "Invalid DeviceFD={}!", fd);
diff --git a/src/core/hle/service/nvdrv/nvdrv.h b/src/core/hle/service/nvdrv/nvdrv.h
index f3c81bd88..b09b6e585 100644
--- a/src/core/hle/service/nvdrv/nvdrv.h
+++ b/src/core/hle/service/nvdrv/nvdrv.h
@@ -7,6 +7,7 @@
7#include <functional> 7#include <functional>
8#include <list> 8#include <list>
9#include <memory> 9#include <memory>
10#include <span>
10#include <string> 11#include <string>
11#include <unordered_map> 12#include <unordered_map>
12#include <vector> 13#include <vector>
@@ -79,14 +80,13 @@ public:
79 DeviceFD Open(const std::string& device_name); 80 DeviceFD Open(const std::string& device_name);
80 81
81 /// Sends an ioctl command to the specified file descriptor. 82 /// Sends an ioctl command to the specified file descriptor.
82 NvResult Ioctl1(DeviceFD fd, Ioctl command, const std::vector<u8>& input, 83 NvResult Ioctl1(DeviceFD fd, Ioctl command, std::span<const u8> input, std::vector<u8>& output);
83 std::vector<u8>& output);
84 84
85 NvResult Ioctl2(DeviceFD fd, Ioctl command, const std::vector<u8>& input, 85 NvResult Ioctl2(DeviceFD fd, Ioctl command, std::span<const u8> input,
86 const std::vector<u8>& inline_input, std::vector<u8>& output); 86 std::span<const u8> inline_input, std::vector<u8>& output);
87 87
88 NvResult Ioctl3(DeviceFD fd, Ioctl command, const std::vector<u8>& input, 88 NvResult Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> input, std::vector<u8>& output,
89 std::vector<u8>& output, std::vector<u8>& inline_output); 89 std::vector<u8>& inline_output);
90 90
91 /// Closes a device file descriptor and returns operation success. 91 /// Closes a device file descriptor and returns operation success.
92 NvResult Close(DeviceFD fd); 92 NvResult Close(DeviceFD fd);
diff --git a/src/core/hle/service/nvflinger/buffer_queue_producer.cpp b/src/core/hle/service/nvflinger/buffer_queue_producer.cpp
index e601b5da1..bcbe05b0d 100644
--- a/src/core/hle/service/nvflinger/buffer_queue_producer.cpp
+++ b/src/core/hle/service/nvflinger/buffer_queue_producer.cpp
@@ -815,8 +815,8 @@ Status BufferQueueProducer::SetPreallocatedBuffer(s32 slot,
815 815
816void BufferQueueProducer::Transact(Kernel::HLERequestContext& ctx, TransactionId code, u32 flags) { 816void BufferQueueProducer::Transact(Kernel::HLERequestContext& ctx, TransactionId code, u32 flags) {
817 Status status{Status::NoError}; 817 Status status{Status::NoError};
818 Parcel parcel_in{ctx.ReadBuffer()}; 818 InputParcel parcel_in{ctx.ReadBuffer()};
819 Parcel parcel_out{}; 819 OutputParcel parcel_out{};
820 820
821 switch (code) { 821 switch (code) {
822 case TransactionId::Connect: { 822 case TransactionId::Connect: {
diff --git a/src/core/hle/service/nvflinger/graphic_buffer_producer.cpp b/src/core/hle/service/nvflinger/graphic_buffer_producer.cpp
index 4043c91f1..769e8c0a3 100644
--- a/src/core/hle/service/nvflinger/graphic_buffer_producer.cpp
+++ b/src/core/hle/service/nvflinger/graphic_buffer_producer.cpp
@@ -9,7 +9,7 @@
9 9
10namespace Service::android { 10namespace Service::android {
11 11
12QueueBufferInput::QueueBufferInput(Parcel& parcel) { 12QueueBufferInput::QueueBufferInput(InputParcel& parcel) {
13 parcel.ReadFlattened(*this); 13 parcel.ReadFlattened(*this);
14} 14}
15 15
diff --git a/src/core/hle/service/nvflinger/graphic_buffer_producer.h b/src/core/hle/service/nvflinger/graphic_buffer_producer.h
index 6ea327bbe..2969f0fd5 100644
--- a/src/core/hle/service/nvflinger/graphic_buffer_producer.h
+++ b/src/core/hle/service/nvflinger/graphic_buffer_producer.h
@@ -14,11 +14,11 @@
14 14
15namespace Service::android { 15namespace Service::android {
16 16
17class Parcel; 17class InputParcel;
18 18
19#pragma pack(push, 1) 19#pragma pack(push, 1)
20struct QueueBufferInput final { 20struct QueueBufferInput final {
21 explicit QueueBufferInput(Parcel& parcel); 21 explicit QueueBufferInput(InputParcel& parcel);
22 22
23 void Deflate(s64* timestamp_, bool* is_auto_timestamp_, Common::Rectangle<s32>* crop_, 23 void Deflate(s64* timestamp_, bool* is_auto_timestamp_, Common::Rectangle<s32>* crop_,
24 NativeWindowScalingMode* scaling_mode_, NativeWindowTransform* transform_, 24 NativeWindowScalingMode* scaling_mode_, NativeWindowTransform* transform_,
diff --git a/src/core/hle/service/nvflinger/parcel.h b/src/core/hle/service/nvflinger/parcel.h
index f3fa2587d..d1b6201e0 100644
--- a/src/core/hle/service/nvflinger/parcel.h
+++ b/src/core/hle/service/nvflinger/parcel.h
@@ -4,6 +4,7 @@
4#pragma once 4#pragma once
5 5
6#include <memory> 6#include <memory>
7#include <span>
7#include <vector> 8#include <vector>
8 9
9#include "common/alignment.h" 10#include "common/alignment.h"
@@ -12,18 +13,17 @@
12 13
13namespace Service::android { 14namespace Service::android {
14 15
15class Parcel final { 16struct ParcelHeader {
16public: 17 u32 data_size;
17 static constexpr std::size_t DefaultBufferSize = 0x40; 18 u32 data_offset;
18 19 u32 objects_size;
19 Parcel() : buffer(DefaultBufferSize) {} 20 u32 objects_offset;
20 21};
21 template <typename T> 22static_assert(sizeof(ParcelHeader) == 16, "ParcelHeader has wrong size");
22 explicit Parcel(const T& out_data) : buffer(DefaultBufferSize) {
23 Write(out_data);
24 }
25 23
26 explicit Parcel(std::vector<u8> in_data) : buffer(std::move(in_data)) { 24class InputParcel final {
25public:
26 explicit InputParcel(std::span<const u8> in_data) : read_buffer(std::move(in_data)) {
27 DeserializeHeader(); 27 DeserializeHeader();
28 [[maybe_unused]] const std::u16string token = ReadInterfaceToken(); 28 [[maybe_unused]] const std::u16string token = ReadInterfaceToken();
29 } 29 }
@@ -31,9 +31,9 @@ public:
31 template <typename T> 31 template <typename T>
32 void Read(T& val) { 32 void Read(T& val) {
33 static_assert(std::is_trivially_copyable_v<T>, "T must be trivially copyable."); 33 static_assert(std::is_trivially_copyable_v<T>, "T must be trivially copyable.");
34 ASSERT(read_index + sizeof(T) <= buffer.size()); 34 ASSERT(read_index + sizeof(T) <= read_buffer.size());
35 35
36 std::memcpy(&val, buffer.data() + read_index, sizeof(T)); 36 std::memcpy(&val, read_buffer.data() + read_index, sizeof(T));
37 read_index += sizeof(T); 37 read_index += sizeof(T);
38 read_index = Common::AlignUp(read_index, 4); 38 read_index = Common::AlignUp(read_index, 4);
39 } 39 }
@@ -62,10 +62,10 @@ public:
62 template <typename T> 62 template <typename T>
63 T ReadUnaligned() { 63 T ReadUnaligned() {
64 static_assert(std::is_trivially_copyable_v<T>, "T must be trivially copyable."); 64 static_assert(std::is_trivially_copyable_v<T>, "T must be trivially copyable.");
65 ASSERT(read_index + sizeof(T) <= buffer.size()); 65 ASSERT(read_index + sizeof(T) <= read_buffer.size());
66 66
67 T val; 67 T val;
68 std::memcpy(&val, buffer.data() + read_index, sizeof(T)); 68 std::memcpy(&val, read_buffer.data() + read_index, sizeof(T));
69 read_index += sizeof(T); 69 read_index += sizeof(T);
70 return val; 70 return val;
71 } 71 }
@@ -101,6 +101,31 @@ public:
101 return token; 101 return token;
102 } 102 }
103 103
104 void DeserializeHeader() {
105 ASSERT(read_buffer.size() > sizeof(ParcelHeader));
106
107 ParcelHeader header{};
108 std::memcpy(&header, read_buffer.data(), sizeof(ParcelHeader));
109
110 read_index = header.data_offset;
111 }
112
113private:
114 std::span<const u8> read_buffer;
115 std::size_t read_index = 0;
116};
117
118class OutputParcel final {
119public:
120 static constexpr std::size_t DefaultBufferSize = 0x40;
121
122 OutputParcel() : buffer(DefaultBufferSize) {}
123
124 template <typename T>
125 explicit OutputParcel(const T& out_data) : buffer(DefaultBufferSize) {
126 Write(out_data);
127 }
128
104 template <typename T> 129 template <typename T>
105 void Write(const T& val) { 130 void Write(const T& val) {
106 static_assert(std::is_trivially_copyable_v<T>, "T must be trivially copyable."); 131 static_assert(std::is_trivially_copyable_v<T>, "T must be trivially copyable.");
@@ -133,40 +158,20 @@ public:
133 WriteObject(ptr.get()); 158 WriteObject(ptr.get());
134 } 159 }
135 160
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 { 161 std::vector<u8> Serialize() const {
146 ASSERT(read_index == 0); 162 ParcelHeader header{};
147 163 header.data_size = static_cast<u32>(write_index - sizeof(ParcelHeader));
148 Header header{}; 164 header.data_offset = sizeof(ParcelHeader);
149 header.data_size = static_cast<u32>(write_index - sizeof(Header));
150 header.data_offset = sizeof(Header);
151 header.objects_size = 4; 165 header.objects_size = 4;
152 header.objects_offset = static_cast<u32>(sizeof(Header) + header.data_size); 166 header.objects_offset = static_cast<u32>(sizeof(ParcelHeader) + header.data_size);
153 std::memcpy(buffer.data(), &header, sizeof(Header)); 167 std::memcpy(buffer.data(), &header, sizeof(ParcelHeader));
154 168
155 return buffer; 169 return buffer;
156 } 170 }
157 171
158private: 172private:
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; 173 mutable std::vector<u8> buffer;
168 std::size_t read_index = 0; 174 std::size_t write_index = sizeof(ParcelHeader);
169 std::size_t write_index = sizeof(Header);
170}; 175};
171 176
172} // namespace Service::android 177} // namespace Service::android
diff --git a/src/core/hle/service/prepo/prepo.cpp b/src/core/hle/service/prepo/prepo.cpp
index 78f897d3e..01040b32a 100644
--- a/src/core/hle/service/prepo/prepo.cpp
+++ b/src/core/hle/service/prepo/prepo.cpp
@@ -63,7 +63,7 @@ private:
63 return ctx.ReadBuffer(1); 63 return ctx.ReadBuffer(1);
64 } 64 }
65 65
66 return std::vector<u8>{}; 66 return std::span<const u8>{};
67 }(); 67 }();
68 68
69 LOG_DEBUG(Service_PREPO, 69 LOG_DEBUG(Service_PREPO,
@@ -90,7 +90,7 @@ private:
90 return ctx.ReadBuffer(1); 90 return ctx.ReadBuffer(1);
91 } 91 }
92 92
93 return std::vector<u8>{}; 93 return std::span<const u8>{};
94 }(); 94 }();
95 95
96 LOG_DEBUG(Service_PREPO, 96 LOG_DEBUG(Service_PREPO,
@@ -142,7 +142,7 @@ private:
142 return ctx.ReadBuffer(1); 142 return ctx.ReadBuffer(1);
143 } 143 }
144 144
145 return std::vector<u8>{}; 145 return std::span<const u8>{};
146 }(); 146 }();
147 147
148 LOG_DEBUG(Service_PREPO, "called, title_id={:016X}, data1_size={:016X}, data2_size={:016X}", 148 LOG_DEBUG(Service_PREPO, "called, title_id={:016X}, data1_size={:016X}, data2_size={:016X}",
@@ -166,7 +166,7 @@ private:
166 return ctx.ReadBuffer(1); 166 return ctx.ReadBuffer(1);
167 } 167 }
168 168
169 return std::vector<u8>{}; 169 return std::span<const u8>{};
170 }(); 170 }();
171 171
172 LOG_DEBUG(Service_PREPO, 172 LOG_DEBUG(Service_PREPO,
diff --git a/src/core/hle/service/sockets/bsd.cpp b/src/core/hle/service/sockets/bsd.cpp
index 9e94a462f..bdb499268 100644
--- a/src/core/hle/service/sockets/bsd.cpp
+++ b/src/core/hle/service/sockets/bsd.cpp
@@ -208,7 +208,6 @@ void BSD::Bind(Kernel::HLERequestContext& ctx) {
208 const s32 fd = rp.Pop<s32>(); 208 const s32 fd = rp.Pop<s32>();
209 209
210 LOG_DEBUG(Service, "called. fd={} addrlen={}", fd, ctx.GetReadBufferSize()); 210 LOG_DEBUG(Service, "called. fd={} addrlen={}", fd, ctx.GetReadBufferSize());
211
212 BuildErrnoResponse(ctx, BindImpl(fd, ctx.ReadBuffer())); 211 BuildErrnoResponse(ctx, BindImpl(fd, ctx.ReadBuffer()));
213} 212}
214 213
@@ -312,7 +311,7 @@ void BSD::SetSockOpt(Kernel::HLERequestContext& ctx) {
312 const u32 level = rp.Pop<u32>(); 311 const u32 level = rp.Pop<u32>();
313 const OptName optname = static_cast<OptName>(rp.Pop<u32>()); 312 const OptName optname = static_cast<OptName>(rp.Pop<u32>());
314 313
315 const std::vector<u8> buffer = ctx.ReadBuffer(); 314 const auto buffer = ctx.ReadBuffer();
316 const u8* optval = buffer.empty() ? nullptr : buffer.data(); 315 const u8* optval = buffer.empty() ? nullptr : buffer.data();
317 size_t optlen = buffer.size(); 316 size_t optlen = buffer.size();
318 317
@@ -489,7 +488,7 @@ std::pair<s32, Errno> BSD::SocketImpl(Domain domain, Type type, Protocol protoco
489 return {fd, Errno::SUCCESS}; 488 return {fd, Errno::SUCCESS};
490} 489}
491 490
492std::pair<s32, Errno> BSD::PollImpl(std::vector<u8>& write_buffer, std::vector<u8> read_buffer, 491std::pair<s32, Errno> BSD::PollImpl(std::vector<u8>& write_buffer, std::span<const u8> read_buffer,
493 s32 nfds, s32 timeout) { 492 s32 nfds, s32 timeout) {
494 if (write_buffer.size() < nfds * sizeof(PollFD)) { 493 if (write_buffer.size() < nfds * sizeof(PollFD)) {
495 return {-1, Errno::INVAL}; 494 return {-1, Errno::INVAL};
@@ -584,7 +583,7 @@ std::pair<s32, Errno> BSD::AcceptImpl(s32 fd, std::vector<u8>& write_buffer) {
584 return {new_fd, Errno::SUCCESS}; 583 return {new_fd, Errno::SUCCESS};
585} 584}
586 585
587Errno BSD::BindImpl(s32 fd, const std::vector<u8>& addr) { 586Errno BSD::BindImpl(s32 fd, std::span<const u8> addr) {
588 if (!IsFileDescriptorValid(fd)) { 587 if (!IsFileDescriptorValid(fd)) {
589 return Errno::BADF; 588 return Errno::BADF;
590 } 589 }
@@ -595,7 +594,7 @@ Errno BSD::BindImpl(s32 fd, const std::vector<u8>& addr) {
595 return Translate(file_descriptors[fd]->socket->Bind(Translate(addr_in))); 594 return Translate(file_descriptors[fd]->socket->Bind(Translate(addr_in)));
596} 595}
597 596
598Errno BSD::ConnectImpl(s32 fd, const std::vector<u8>& addr) { 597Errno BSD::ConnectImpl(s32 fd, std::span<const u8> addr) {
599 if (!IsFileDescriptorValid(fd)) { 598 if (!IsFileDescriptorValid(fd)) {
600 return Errno::BADF; 599 return Errno::BADF;
601 } 600 }
@@ -800,15 +799,15 @@ std::pair<s32, Errno> BSD::RecvFromImpl(s32 fd, u32 flags, std::vector<u8>& mess
800 return {ret, bsd_errno}; 799 return {ret, bsd_errno};
801} 800}
802 801
803std::pair<s32, Errno> BSD::SendImpl(s32 fd, u32 flags, const std::vector<u8>& message) { 802std::pair<s32, Errno> BSD::SendImpl(s32 fd, u32 flags, std::span<const u8> message) {
804 if (!IsFileDescriptorValid(fd)) { 803 if (!IsFileDescriptorValid(fd)) {
805 return {-1, Errno::BADF}; 804 return {-1, Errno::BADF};
806 } 805 }
807 return Translate(file_descriptors[fd]->socket->Send(message, flags)); 806 return Translate(file_descriptors[fd]->socket->Send(message, flags));
808} 807}
809 808
810std::pair<s32, Errno> BSD::SendToImpl(s32 fd, u32 flags, const std::vector<u8>& message, 809std::pair<s32, Errno> BSD::SendToImpl(s32 fd, u32 flags, std::span<const u8> message,
811 const std::vector<u8>& addr) { 810 std::span<const u8> addr) {
812 if (!IsFileDescriptorValid(fd)) { 811 if (!IsFileDescriptorValid(fd)) {
813 return {-1, Errno::BADF}; 812 return {-1, Errno::BADF};
814 } 813 }
diff --git a/src/core/hle/service/sockets/bsd.h b/src/core/hle/service/sockets/bsd.h
index 81e855e0f..56bb3f8b1 100644
--- a/src/core/hle/service/sockets/bsd.h
+++ b/src/core/hle/service/sockets/bsd.h
@@ -4,6 +4,7 @@
4#pragma once 4#pragma once
5 5
6#include <memory> 6#include <memory>
7#include <span>
7#include <vector> 8#include <vector>
8 9
9#include "common/common_types.h" 10#include "common/common_types.h"
@@ -44,7 +45,7 @@ private:
44 45
45 s32 nfds; 46 s32 nfds;
46 s32 timeout; 47 s32 timeout;
47 std::vector<u8> read_buffer; 48 std::span<const u8> read_buffer;
48 std::vector<u8> write_buffer; 49 std::vector<u8> write_buffer;
49 s32 ret{}; 50 s32 ret{};
50 Errno bsd_errno{}; 51 Errno bsd_errno{};
@@ -65,7 +66,7 @@ private:
65 void Response(Kernel::HLERequestContext& ctx); 66 void Response(Kernel::HLERequestContext& ctx);
66 67
67 s32 fd; 68 s32 fd;
68 std::vector<u8> addr; 69 std::span<const u8> addr;
69 Errno bsd_errno{}; 70 Errno bsd_errno{};
70 }; 71 };
71 72
@@ -98,7 +99,7 @@ private:
98 99
99 s32 fd; 100 s32 fd;
100 u32 flags; 101 u32 flags;
101 std::vector<u8> message; 102 std::span<const u8> message;
102 s32 ret{}; 103 s32 ret{};
103 Errno bsd_errno{}; 104 Errno bsd_errno{};
104 }; 105 };
@@ -109,8 +110,8 @@ private:
109 110
110 s32 fd; 111 s32 fd;
111 u32 flags; 112 u32 flags;
112 std::vector<u8> message; 113 std::span<const u8> message;
113 std::vector<u8> addr; 114 std::span<const u8> addr;
114 s32 ret{}; 115 s32 ret{};
115 Errno bsd_errno{}; 116 Errno bsd_errno{};
116 }; 117 };
@@ -143,11 +144,11 @@ private:
143 void ExecuteWork(Kernel::HLERequestContext& ctx, Work work); 144 void ExecuteWork(Kernel::HLERequestContext& ctx, Work work);
144 145
145 std::pair<s32, Errno> SocketImpl(Domain domain, Type type, Protocol protocol); 146 std::pair<s32, Errno> SocketImpl(Domain domain, Type type, Protocol protocol);
146 std::pair<s32, Errno> PollImpl(std::vector<u8>& write_buffer, std::vector<u8> read_buffer, 147 std::pair<s32, Errno> PollImpl(std::vector<u8>& write_buffer, std::span<const u8> read_buffer,
147 s32 nfds, s32 timeout); 148 s32 nfds, s32 timeout);
148 std::pair<s32, Errno> AcceptImpl(s32 fd, std::vector<u8>& write_buffer); 149 std::pair<s32, Errno> AcceptImpl(s32 fd, std::vector<u8>& write_buffer);
149 Errno BindImpl(s32 fd, const std::vector<u8>& addr); 150 Errno BindImpl(s32 fd, std::span<const u8> addr);
150 Errno ConnectImpl(s32 fd, const std::vector<u8>& addr); 151 Errno ConnectImpl(s32 fd, std::span<const u8> addr);
151 Errno GetPeerNameImpl(s32 fd, std::vector<u8>& write_buffer); 152 Errno GetPeerNameImpl(s32 fd, std::vector<u8>& write_buffer);
152 Errno GetSockNameImpl(s32 fd, std::vector<u8>& write_buffer); 153 Errno GetSockNameImpl(s32 fd, std::vector<u8>& write_buffer);
153 Errno ListenImpl(s32 fd, s32 backlog); 154 Errno ListenImpl(s32 fd, s32 backlog);
@@ -157,9 +158,9 @@ private:
157 std::pair<s32, Errno> RecvImpl(s32 fd, u32 flags, std::vector<u8>& message); 158 std::pair<s32, Errno> RecvImpl(s32 fd, u32 flags, std::vector<u8>& message);
158 std::pair<s32, Errno> RecvFromImpl(s32 fd, u32 flags, std::vector<u8>& message, 159 std::pair<s32, Errno> RecvFromImpl(s32 fd, u32 flags, std::vector<u8>& message,
159 std::vector<u8>& addr); 160 std::vector<u8>& addr);
160 std::pair<s32, Errno> SendImpl(s32 fd, u32 flags, const std::vector<u8>& message); 161 std::pair<s32, Errno> SendImpl(s32 fd, u32 flags, std::span<const u8> message);
161 std::pair<s32, Errno> SendToImpl(s32 fd, u32 flags, const std::vector<u8>& message, 162 std::pair<s32, Errno> SendToImpl(s32 fd, u32 flags, std::span<const u8> message,
162 const std::vector<u8>& addr); 163 std::span<const u8> addr);
163 Errno CloseImpl(s32 fd); 164 Errno CloseImpl(s32 fd);
164 165
165 s32 FindFreeFileDescriptorHandle() noexcept; 166 s32 FindFreeFileDescriptorHandle() noexcept;
diff --git a/src/core/hle/service/sockets/sfdnsres.cpp b/src/core/hle/service/sockets/sfdnsres.cpp
index 097c37d7a..e96eda7f3 100644
--- a/src/core/hle/service/sockets/sfdnsres.cpp
+++ b/src/core/hle/service/sockets/sfdnsres.cpp
@@ -243,4 +243,4 @@ void SFDNSRES::GetAddrInfoRequestWithOptions(Kernel::HLERequestContext& ctx) {
243 rb.Push(0); 243 rb.Push(0);
244} 244}
245 245
246} // namespace Service::Sockets \ No newline at end of file 246} // namespace Service::Sockets
diff --git a/src/core/hle/service/ssl/ssl.cpp b/src/core/hle/service/ssl/ssl.cpp
index 3735e0452..dcf47083f 100644
--- a/src/core/hle/service/ssl/ssl.cpp
+++ b/src/core/hle/service/ssl/ssl.cpp
@@ -101,7 +101,7 @@ private:
101 void ImportServerPki(Kernel::HLERequestContext& ctx) { 101 void ImportServerPki(Kernel::HLERequestContext& ctx) {
102 IPC::RequestParser rp{ctx}; 102 IPC::RequestParser rp{ctx};
103 const auto certificate_format = rp.PopEnum<CertificateFormat>(); 103 const auto certificate_format = rp.PopEnum<CertificateFormat>();
104 const auto pkcs_12_certificates = ctx.ReadBuffer(0); 104 [[maybe_unused]] const auto pkcs_12_certificates = ctx.ReadBuffer(0);
105 105
106 constexpr u64 server_id = 0; 106 constexpr u64 server_id = 0;
107 107
@@ -113,13 +113,13 @@ private:
113 } 113 }
114 114
115 void ImportClientPki(Kernel::HLERequestContext& ctx) { 115 void ImportClientPki(Kernel::HLERequestContext& ctx) {
116 const auto pkcs_12_certificate = ctx.ReadBuffer(0); 116 [[maybe_unused]] const auto pkcs_12_certificate = ctx.ReadBuffer(0);
117 const auto ascii_password = [&ctx] { 117 [[maybe_unused]] const auto ascii_password = [&ctx] {
118 if (ctx.CanReadBuffer(1)) { 118 if (ctx.CanReadBuffer(1)) {
119 return ctx.ReadBuffer(1); 119 return ctx.ReadBuffer(1);
120 } 120 }
121 121
122 return std::vector<u8>{}; 122 return std::span<const u8>{};
123 }(); 123 }();
124 124
125 constexpr u64 client_id = 0; 125 constexpr u64 client_id = 0;
diff --git a/src/core/hle/service/vi/vi.cpp b/src/core/hle/service/vi/vi.cpp
index bb283e74e..2fb631183 100644
--- a/src/core/hle/service/vi/vi.cpp
+++ b/src/core/hle/service/vi/vi.cpp
@@ -603,7 +603,7 @@ private:
603 return; 603 return;
604 } 604 }
605 605
606 const auto parcel = android::Parcel{NativeWindow{*buffer_queue_id}}; 606 const auto parcel = android::OutputParcel{NativeWindow{*buffer_queue_id}};
607 const auto buffer_size = ctx.WriteBuffer(parcel.Serialize()); 607 const auto buffer_size = ctx.WriteBuffer(parcel.Serialize());
608 608
609 IPC::ResponseBuilder rb{ctx, 4}; 609 IPC::ResponseBuilder rb{ctx, 4};
@@ -649,7 +649,7 @@ private:
649 return; 649 return;
650 } 650 }
651 651
652 const auto parcel = android::Parcel{NativeWindow{*buffer_queue_id}}; 652 const auto parcel = android::OutputParcel{NativeWindow{*buffer_queue_id}};
653 const auto buffer_size = ctx.WriteBuffer(parcel.Serialize()); 653 const auto buffer_size = ctx.WriteBuffer(parcel.Serialize());
654 654
655 IPC::ResponseBuilder rb{ctx, 6}; 655 IPC::ResponseBuilder rb{ctx, 6};
diff --git a/src/core/internal_network/network.cpp b/src/core/internal_network/network.cpp
index 282ea1ff9..7494fb62d 100644
--- a/src/core/internal_network/network.cpp
+++ b/src/core/internal_network/network.cpp
@@ -550,7 +550,7 @@ std::pair<s32, Errno> Socket::RecvFrom(int flags, std::vector<u8>& message, Sock
550 return {-1, GetAndLogLastError()}; 550 return {-1, GetAndLogLastError()};
551} 551}
552 552
553std::pair<s32, Errno> Socket::Send(const std::vector<u8>& message, int flags) { 553std::pair<s32, Errno> Socket::Send(std::span<const u8> message, int flags) {
554 ASSERT(message.size() < static_cast<size_t>(std::numeric_limits<int>::max())); 554 ASSERT(message.size() < static_cast<size_t>(std::numeric_limits<int>::max()));
555 ASSERT(flags == 0); 555 ASSERT(flags == 0);
556 556
@@ -563,7 +563,7 @@ std::pair<s32, Errno> Socket::Send(const std::vector<u8>& message, int flags) {
563 return {-1, GetAndLogLastError()}; 563 return {-1, GetAndLogLastError()};
564} 564}
565 565
566std::pair<s32, Errno> Socket::SendTo(u32 flags, const std::vector<u8>& message, 566std::pair<s32, Errno> Socket::SendTo(u32 flags, std::span<const u8> message,
567 const SockAddrIn* addr) { 567 const SockAddrIn* addr) {
568 ASSERT(flags == 0); 568 ASSERT(flags == 0);
569 569
diff --git a/src/core/internal_network/socket_proxy.cpp b/src/core/internal_network/socket_proxy.cpp
index 1e1c42cea..7a77171c2 100644
--- a/src/core/internal_network/socket_proxy.cpp
+++ b/src/core/internal_network/socket_proxy.cpp
@@ -182,7 +182,7 @@ std::pair<s32, Errno> ProxySocket::ReceivePacket(int flags, std::vector<u8>& mes
182 return {static_cast<u32>(read_bytes), Errno::SUCCESS}; 182 return {static_cast<u32>(read_bytes), Errno::SUCCESS};
183} 183}
184 184
185std::pair<s32, Errno> ProxySocket::Send(const std::vector<u8>& message, int flags) { 185std::pair<s32, Errno> ProxySocket::Send(std::span<const u8> message, int flags) {
186 LOG_WARNING(Network, "(STUBBED) called"); 186 LOG_WARNING(Network, "(STUBBED) called");
187 ASSERT(message.size() < static_cast<size_t>(std::numeric_limits<int>::max())); 187 ASSERT(message.size() < static_cast<size_t>(std::numeric_limits<int>::max()));
188 ASSERT(flags == 0); 188 ASSERT(flags == 0);
@@ -200,7 +200,7 @@ void ProxySocket::SendPacket(ProxyPacket& packet) {
200 } 200 }
201} 201}
202 202
203std::pair<s32, Errno> ProxySocket::SendTo(u32 flags, const std::vector<u8>& message, 203std::pair<s32, Errno> ProxySocket::SendTo(u32 flags, std::span<const u8> message,
204 const SockAddrIn* addr) { 204 const SockAddrIn* addr) {
205 ASSERT(flags == 0); 205 ASSERT(flags == 0);
206 206
diff --git a/src/core/internal_network/socket_proxy.h b/src/core/internal_network/socket_proxy.h
index f12b5f567..9421492bc 100644
--- a/src/core/internal_network/socket_proxy.h
+++ b/src/core/internal_network/socket_proxy.h
@@ -4,6 +4,7 @@
4#pragma once 4#pragma once
5 5
6#include <mutex> 6#include <mutex>
7#include <span>
7#include <vector> 8#include <vector>
8#include <queue> 9#include <queue>
9 10
@@ -48,11 +49,11 @@ public:
48 std::pair<s32, Errno> ReceivePacket(int flags, std::vector<u8>& message, SockAddrIn* addr, 49 std::pair<s32, Errno> ReceivePacket(int flags, std::vector<u8>& message, SockAddrIn* addr,
49 std::size_t max_length); 50 std::size_t max_length);
50 51
51 std::pair<s32, Errno> Send(const std::vector<u8>& message, int flags) override; 52 std::pair<s32, Errno> Send(std::span<const u8> message, int flags) override;
52 53
53 void SendPacket(ProxyPacket& packet); 54 void SendPacket(ProxyPacket& packet);
54 55
55 std::pair<s32, Errno> SendTo(u32 flags, const std::vector<u8>& message, 56 std::pair<s32, Errno> SendTo(u32 flags, std::span<const u8> message,
56 const SockAddrIn* addr) override; 57 const SockAddrIn* addr) override;
57 58
58 Errno SetLinger(bool enable, u32 linger) override; 59 Errno SetLinger(bool enable, u32 linger) override;
diff --git a/src/core/internal_network/sockets.h b/src/core/internal_network/sockets.h
index 2e328c645..4c7489258 100644
--- a/src/core/internal_network/sockets.h
+++ b/src/core/internal_network/sockets.h
@@ -5,6 +5,7 @@
5 5
6#include <map> 6#include <map>
7#include <memory> 7#include <memory>
8#include <span>
8#include <utility> 9#include <utility>
9 10
10#if defined(_WIN32) 11#if defined(_WIN32)
@@ -66,9 +67,9 @@ public:
66 virtual std::pair<s32, Errno> RecvFrom(int flags, std::vector<u8>& message, 67 virtual std::pair<s32, Errno> RecvFrom(int flags, std::vector<u8>& message,
67 SockAddrIn* addr) = 0; 68 SockAddrIn* addr) = 0;
68 69
69 virtual std::pair<s32, Errno> Send(const std::vector<u8>& message, int flags) = 0; 70 virtual std::pair<s32, Errno> Send(std::span<const u8> message, int flags) = 0;
70 71
71 virtual std::pair<s32, Errno> SendTo(u32 flags, const std::vector<u8>& message, 72 virtual std::pair<s32, Errno> SendTo(u32 flags, std::span<const u8> message,
72 const SockAddrIn* addr) = 0; 73 const SockAddrIn* addr) = 0;
73 74
74 virtual Errno SetLinger(bool enable, u32 linger) = 0; 75 virtual Errno SetLinger(bool enable, u32 linger) = 0;
@@ -138,9 +139,9 @@ public:
138 139
139 std::pair<s32, Errno> RecvFrom(int flags, std::vector<u8>& message, SockAddrIn* addr) override; 140 std::pair<s32, Errno> RecvFrom(int flags, std::vector<u8>& message, SockAddrIn* addr) override;
140 141
141 std::pair<s32, Errno> Send(const std::vector<u8>& message, int flags) override; 142 std::pair<s32, Errno> Send(std::span<const u8> message, int flags) override;
142 143
143 std::pair<s32, Errno> SendTo(u32 flags, const std::vector<u8>& message, 144 std::pair<s32, Errno> SendTo(u32 flags, std::span<const u8> message,
144 const SockAddrIn* addr) override; 145 const SockAddrIn* addr) override;
145 146
146 Errno SetLinger(bool enable, u32 linger) override; 147 Errno SetLinger(bool enable, u32 linger) override;
diff --git a/src/core/memory.cpp b/src/core/memory.cpp
index a1e41faff..af9660b55 100644
--- a/src/core/memory.cpp
+++ b/src/core/memory.cpp
@@ -383,6 +383,10 @@ struct Memory::Impl {
383 return; 383 return;
384 } 384 }
385 385
386 if (Settings::IsFastmemEnabled()) {
387 system.DeviceMemory().buffer.Protect(vaddr, size, !debug, !debug);
388 }
389
386 // Iterate over a contiguous CPU address space, marking/unmarking the region. 390 // Iterate over a contiguous CPU address space, marking/unmarking the region.
387 // The region is at a granularity of CPU pages. 391 // The region is at a granularity of CPU pages.
388 392
@@ -436,7 +440,7 @@ struct Memory::Impl {
436 } 440 }
437 441
438 if (Settings::IsFastmemEnabled()) { 442 if (Settings::IsFastmemEnabled()) {
439 const bool is_read_enable = !Settings::IsGPULevelExtreme() || !cached; 443 const bool is_read_enable = Settings::IsGPULevelHigh() || !cached;
440 system.DeviceMemory().buffer.Protect(vaddr, size, is_read_enable, !cached); 444 system.DeviceMemory().buffer.Protect(vaddr, size, is_read_enable, !cached);
441 } 445 }
442 446
diff --git a/src/core/reporter.cpp b/src/core/reporter.cpp
index 77821e047..59dfb8767 100644
--- a/src/core/reporter.cpp
+++ b/src/core/reporter.cpp
@@ -312,7 +312,7 @@ void Reporter::SaveUnimplementedAppletReport(
312} 312}
313 313
314void Reporter::SavePlayReport(PlayReportType type, u64 title_id, 314void Reporter::SavePlayReport(PlayReportType type, u64 title_id,
315 const std::vector<std::vector<u8>>& data, 315 const std::vector<std::span<const u8>>& data,
316 std::optional<u64> process_id, std::optional<u128> user_id) const { 316 std::optional<u64> process_id, std::optional<u128> user_id) const {
317 if (!IsReportingEnabled()) { 317 if (!IsReportingEnabled()) {
318 return; 318 return;
diff --git a/src/core/reporter.h b/src/core/reporter.h
index 9fdb9d6c1..bb11f8e7c 100644
--- a/src/core/reporter.h
+++ b/src/core/reporter.h
@@ -5,6 +5,7 @@
5 5
6#include <array> 6#include <array>
7#include <optional> 7#include <optional>
8#include <span>
8#include <string> 9#include <string>
9#include <vector> 10#include <vector>
10#include "common/common_types.h" 11#include "common/common_types.h"
@@ -56,7 +57,8 @@ public:
56 System, 57 System,
57 }; 58 };
58 59
59 void SavePlayReport(PlayReportType type, u64 title_id, const std::vector<std::vector<u8>>& data, 60 void SavePlayReport(PlayReportType type, u64 title_id,
61 const std::vector<std::span<const u8>>& data,
60 std::optional<u64> process_id = {}, std::optional<u128> user_id = {}) const; 62 std::optional<u64> process_id = {}, std::optional<u128> user_id = {}) const;
61 63
62 // Used by error applet 64 // Used by error applet
diff --git a/src/input_common/CMakeLists.txt b/src/input_common/CMakeLists.txt
index cef2c4d52..e3b627e4f 100644
--- a/src/input_common/CMakeLists.txt
+++ b/src/input_common/CMakeLists.txt
@@ -51,8 +51,29 @@ endif()
51 51
52if (ENABLE_SDL2) 52if (ENABLE_SDL2)
53 target_sources(input_common PRIVATE 53 target_sources(input_common PRIVATE
54 drivers/joycon.cpp
55 drivers/joycon.h
54 drivers/sdl_driver.cpp 56 drivers/sdl_driver.cpp
55 drivers/sdl_driver.h 57 drivers/sdl_driver.h
58 helpers/joycon_driver.cpp
59 helpers/joycon_driver.h
60 helpers/joycon_protocol/calibration.cpp
61 helpers/joycon_protocol/calibration.h
62 helpers/joycon_protocol/common_protocol.cpp
63 helpers/joycon_protocol/common_protocol.h
64 helpers/joycon_protocol/generic_functions.cpp
65 helpers/joycon_protocol/generic_functions.h
66 helpers/joycon_protocol/joycon_types.h
67 helpers/joycon_protocol/irs.cpp
68 helpers/joycon_protocol/irs.h
69 helpers/joycon_protocol/nfc.cpp
70 helpers/joycon_protocol/nfc.h
71 helpers/joycon_protocol/poller.cpp
72 helpers/joycon_protocol/poller.h
73 helpers/joycon_protocol/ringcon.cpp
74 helpers/joycon_protocol/ringcon.h
75 helpers/joycon_protocol/rumble.cpp
76 helpers/joycon_protocol/rumble.h
56 ) 77 )
57 target_link_libraries(input_common PRIVATE SDL2::SDL2) 78 target_link_libraries(input_common PRIVATE SDL2::SDL2)
58 target_compile_definitions(input_common PRIVATE HAVE_SDL2) 79 target_compile_definitions(input_common PRIVATE HAVE_SDL2)
diff --git a/src/input_common/drivers/camera.cpp b/src/input_common/drivers/camera.cpp
index fad9177dc..04970f635 100644
--- a/src/input_common/drivers/camera.cpp
+++ b/src/input_common/drivers/camera.cpp
@@ -72,11 +72,11 @@ std::size_t Camera::getImageHeight() const {
72 } 72 }
73} 73}
74 74
75Common::Input::CameraError Camera::SetCameraFormat( 75Common::Input::DriverResult Camera::SetCameraFormat(
76 [[maybe_unused]] const PadIdentifier& identifier_, 76 [[maybe_unused]] const PadIdentifier& identifier_,
77 const Common::Input::CameraFormat camera_format) { 77 const Common::Input::CameraFormat camera_format) {
78 status.format = camera_format; 78 status.format = camera_format;
79 return Common::Input::CameraError::None; 79 return Common::Input::DriverResult::Success;
80} 80}
81 81
82} // namespace InputCommon 82} // namespace InputCommon
diff --git a/src/input_common/drivers/camera.h b/src/input_common/drivers/camera.h
index ead3e0fde..24b27e325 100644
--- a/src/input_common/drivers/camera.h
+++ b/src/input_common/drivers/camera.h
@@ -22,8 +22,8 @@ public:
22 std::size_t getImageWidth() const; 22 std::size_t getImageWidth() const;
23 std::size_t getImageHeight() const; 23 std::size_t getImageHeight() const;
24 24
25 Common::Input::CameraError SetCameraFormat(const PadIdentifier& identifier_, 25 Common::Input::DriverResult SetCameraFormat(const PadIdentifier& identifier_,
26 Common::Input::CameraFormat camera_format) override; 26 Common::Input::CameraFormat camera_format) override;
27 27
28private: 28private:
29 Common::Input::CameraStatus status{}; 29 Common::Input::CameraStatus status{};
diff --git a/src/input_common/drivers/gc_adapter.cpp b/src/input_common/drivers/gc_adapter.cpp
index 826fa2109..d09ff178b 100644
--- a/src/input_common/drivers/gc_adapter.cpp
+++ b/src/input_common/drivers/gc_adapter.cpp
@@ -6,6 +6,7 @@
6 6
7#include "common/logging/log.h" 7#include "common/logging/log.h"
8#include "common/param_package.h" 8#include "common/param_package.h"
9#include "common/polyfill_thread.h"
9#include "common/settings_input.h" 10#include "common/settings_input.h"
10#include "common/thread.h" 11#include "common/thread.h"
11#include "input_common/drivers/gc_adapter.h" 12#include "input_common/drivers/gc_adapter.h"
@@ -217,8 +218,7 @@ void GCAdapter::AdapterScanThread(std::stop_token stop_token) {
217 Common::SetCurrentThreadName("ScanGCAdapter"); 218 Common::SetCurrentThreadName("ScanGCAdapter");
218 usb_adapter_handle = nullptr; 219 usb_adapter_handle = nullptr;
219 pads = {}; 220 pads = {};
220 while (!stop_token.stop_requested() && !Setup()) { 221 while (!Setup() && Common::StoppableTimedWait(stop_token, std::chrono::seconds{2})) {
221 std::this_thread::sleep_for(std::chrono::seconds(2));
222 } 222 }
223} 223}
224 224
@@ -324,7 +324,7 @@ bool GCAdapter::GetGCEndpoint(libusb_device* device) {
324 return true; 324 return true;
325} 325}
326 326
327Common::Input::VibrationError GCAdapter::SetVibration( 327Common::Input::DriverResult GCAdapter::SetVibration(
328 const PadIdentifier& identifier, const Common::Input::VibrationStatus& vibration) { 328 const PadIdentifier& identifier, const Common::Input::VibrationStatus& vibration) {
329 const auto mean_amplitude = (vibration.low_amplitude + vibration.high_amplitude) * 0.5f; 329 const auto mean_amplitude = (vibration.low_amplitude + vibration.high_amplitude) * 0.5f;
330 const auto processed_amplitude = 330 const auto processed_amplitude =
@@ -333,9 +333,9 @@ Common::Input::VibrationError GCAdapter::SetVibration(
333 pads[identifier.port].rumble_amplitude = processed_amplitude; 333 pads[identifier.port].rumble_amplitude = processed_amplitude;
334 334
335 if (!rumble_enabled) { 335 if (!rumble_enabled) {
336 return Common::Input::VibrationError::Disabled; 336 return Common::Input::DriverResult::Disabled;
337 } 337 }
338 return Common::Input::VibrationError::None; 338 return Common::Input::DriverResult::Success;
339} 339}
340 340
341bool GCAdapter::IsVibrationEnabled([[maybe_unused]] const PadIdentifier& identifier) { 341bool GCAdapter::IsVibrationEnabled([[maybe_unused]] const PadIdentifier& identifier) {
diff --git a/src/input_common/drivers/gc_adapter.h b/src/input_common/drivers/gc_adapter.h
index b5270fd0b..3c2eb376d 100644
--- a/src/input_common/drivers/gc_adapter.h
+++ b/src/input_common/drivers/gc_adapter.h
@@ -25,7 +25,7 @@ public:
25 explicit GCAdapter(std::string input_engine_); 25 explicit GCAdapter(std::string input_engine_);
26 ~GCAdapter() override; 26 ~GCAdapter() override;
27 27
28 Common::Input::VibrationError SetVibration( 28 Common::Input::DriverResult SetVibration(
29 const PadIdentifier& identifier, const Common::Input::VibrationStatus& vibration) override; 29 const PadIdentifier& identifier, const Common::Input::VibrationStatus& vibration) override;
30 30
31 bool IsVibrationEnabled(const PadIdentifier& identifier) override; 31 bool IsVibrationEnabled(const PadIdentifier& identifier) override;
diff --git a/src/input_common/drivers/joycon.cpp b/src/input_common/drivers/joycon.cpp
new file mode 100644
index 000000000..4fcfb4510
--- /dev/null
+++ b/src/input_common/drivers/joycon.cpp
@@ -0,0 +1,677 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include <fmt/format.h>
5
6#include "common/param_package.h"
7#include "common/polyfill_ranges.h"
8#include "common/polyfill_thread.h"
9#include "common/settings.h"
10#include "common/thread.h"
11#include "input_common/drivers/joycon.h"
12#include "input_common/helpers/joycon_driver.h"
13#include "input_common/helpers/joycon_protocol/joycon_types.h"
14
15namespace InputCommon {
16
17Joycons::Joycons(const std::string& input_engine_) : InputEngine(input_engine_) {
18 // Avoid conflicting with SDL driver
19 if (!Settings::values.enable_joycon_driver) {
20 return;
21 }
22 LOG_INFO(Input, "Joycon driver Initialization started");
23 const int init_res = SDL_hid_init();
24 if (init_res == 0) {
25 Setup();
26 } else {
27 LOG_ERROR(Input, "Hidapi could not be initialized. failed with error = {}", init_res);
28 }
29}
30
31Joycons::~Joycons() {
32 Reset();
33}
34
35void Joycons::Reset() {
36 scan_thread = {};
37 for (const auto& device : left_joycons) {
38 if (!device) {
39 continue;
40 }
41 device->Stop();
42 }
43 for (const auto& device : right_joycons) {
44 if (!device) {
45 continue;
46 }
47 device->Stop();
48 }
49 SDL_hid_exit();
50}
51
52void Joycons::Setup() {
53 u32 port = 0;
54 PreSetController(GetIdentifier(0, Joycon::ControllerType::None));
55 for (auto& device : left_joycons) {
56 PreSetController(GetIdentifier(port, Joycon::ControllerType::Left));
57 device = std::make_shared<Joycon::JoyconDriver>(port++);
58 }
59 port = 0;
60 for (auto& device : right_joycons) {
61 PreSetController(GetIdentifier(port, Joycon::ControllerType::Right));
62 device = std::make_shared<Joycon::JoyconDriver>(port++);
63 }
64
65 scan_thread = std::jthread([this](std::stop_token stop_token) { ScanThread(stop_token); });
66}
67
68void Joycons::ScanThread(std::stop_token stop_token) {
69 constexpr u16 nintendo_vendor_id = 0x057e;
70 Common::SetCurrentThreadName("JoyconScanThread");
71
72 do {
73 SDL_hid_device_info* devs = SDL_hid_enumerate(nintendo_vendor_id, 0x0);
74 SDL_hid_device_info* cur_dev = devs;
75
76 while (cur_dev) {
77 if (IsDeviceNew(cur_dev)) {
78 LOG_DEBUG(Input, "Device Found,type : {:04X} {:04X}", cur_dev->vendor_id,
79 cur_dev->product_id);
80 RegisterNewDevice(cur_dev);
81 }
82 cur_dev = cur_dev->next;
83 }
84
85 SDL_hid_free_enumeration(devs);
86 } while (Common::StoppableTimedWait(stop_token, std::chrono::seconds{5}));
87}
88
89bool Joycons::IsDeviceNew(SDL_hid_device_info* device_info) const {
90 Joycon::ControllerType type{};
91 Joycon::SerialNumber serial_number{};
92
93 const auto result = Joycon::JoyconDriver::GetDeviceType(device_info, type);
94 if (result != Joycon::DriverResult::Success) {
95 return false;
96 }
97
98 const auto result2 = Joycon::JoyconDriver::GetSerialNumber(device_info, serial_number);
99 if (result2 != Joycon::DriverResult::Success) {
100 return false;
101 }
102
103 auto is_handle_identical = [serial_number](std::shared_ptr<Joycon::JoyconDriver> device) {
104 if (!device) {
105 return false;
106 }
107 if (!device->IsConnected()) {
108 return false;
109 }
110 if (device->GetHandleSerialNumber() != serial_number) {
111 return false;
112 }
113 return true;
114 };
115
116 // Check if device already exist
117 switch (type) {
118 case Joycon::ControllerType::Left:
119 for (const auto& device : left_joycons) {
120 if (is_handle_identical(device)) {
121 return false;
122 }
123 }
124 break;
125 case Joycon::ControllerType::Right:
126 for (const auto& device : right_joycons) {
127 if (is_handle_identical(device)) {
128 return false;
129 }
130 }
131 break;
132 default:
133 return false;
134 }
135
136 return true;
137}
138
139void Joycons::RegisterNewDevice(SDL_hid_device_info* device_info) {
140 Joycon::ControllerType type{};
141 auto result = Joycon::JoyconDriver::GetDeviceType(device_info, type);
142 auto handle = GetNextFreeHandle(type);
143 if (handle == nullptr) {
144 LOG_WARNING(Input, "No free handles available");
145 return;
146 }
147 if (result == Joycon::DriverResult::Success) {
148 result = handle->RequestDeviceAccess(device_info);
149 }
150 if (result == Joycon::DriverResult::Success) {
151 LOG_WARNING(Input, "Initialize device");
152
153 const std::size_t port = handle->GetDevicePort();
154 const Joycon::JoyconCallbacks callbacks{
155 .on_battery_data = {[this, port, type](Joycon::Battery value) {
156 OnBatteryUpdate(port, type, value);
157 }},
158 .on_color_data = {[this, port, type](Joycon::Color value) {
159 OnColorUpdate(port, type, value);
160 }},
161 .on_button_data = {[this, port, type](int id, bool value) {
162 OnButtonUpdate(port, type, id, value);
163 }},
164 .on_stick_data = {[this, port, type](int id, f32 value) {
165 OnStickUpdate(port, type, id, value);
166 }},
167 .on_motion_data = {[this, port, type](int id, const Joycon::MotionData& value) {
168 OnMotionUpdate(port, type, id, value);
169 }},
170 .on_ring_data = {[this](f32 ring_data) { OnRingConUpdate(ring_data); }},
171 .on_amiibo_data = {[this, port](const std::vector<u8>& amiibo_data) {
172 OnAmiiboUpdate(port, amiibo_data);
173 }},
174 .on_camera_data = {[this, port](const std::vector<u8>& camera_data,
175 Joycon::IrsResolution format) {
176 OnCameraUpdate(port, camera_data, format);
177 }},
178 };
179
180 handle->InitializeDevice();
181 handle->SetCallbacks(callbacks);
182 }
183}
184
185std::shared_ptr<Joycon::JoyconDriver> Joycons::GetNextFreeHandle(
186 Joycon::ControllerType type) const {
187 if (type == Joycon::ControllerType::Left) {
188 const auto unconnected_device =
189 std::ranges::find_if(left_joycons, [](auto& device) { return !device->IsConnected(); });
190 if (unconnected_device != left_joycons.end()) {
191 return *unconnected_device;
192 }
193 }
194 if (type == Joycon::ControllerType::Right) {
195 const auto unconnected_device = std::ranges::find_if(
196 right_joycons, [](auto& device) { return !device->IsConnected(); });
197
198 if (unconnected_device != right_joycons.end()) {
199 return *unconnected_device;
200 }
201 }
202 return nullptr;
203}
204
205bool Joycons::IsVibrationEnabled(const PadIdentifier& identifier) {
206 const auto handle = GetHandle(identifier);
207 if (handle == nullptr) {
208 return false;
209 }
210 return handle->IsVibrationEnabled();
211}
212
213Common::Input::DriverResult Joycons::SetVibration(const PadIdentifier& identifier,
214 const Common::Input::VibrationStatus& vibration) {
215 const Joycon::VibrationValue native_vibration{
216 .low_amplitude = vibration.low_amplitude,
217 .low_frequency = vibration.low_frequency,
218 .high_amplitude = vibration.high_amplitude,
219 .high_frequency = vibration.high_frequency,
220 };
221 auto handle = GetHandle(identifier);
222 if (handle == nullptr) {
223 return Common::Input::DriverResult::InvalidHandle;
224 }
225
226 handle->SetVibration(native_vibration);
227 return Common::Input::DriverResult::Success;
228}
229
230Common::Input::DriverResult Joycons::SetLeds(const PadIdentifier& identifier,
231 const Common::Input::LedStatus& led_status) {
232 auto handle = GetHandle(identifier);
233 if (handle == nullptr) {
234 return Common::Input::DriverResult::InvalidHandle;
235 }
236 int led_config = led_status.led_1 ? 1 : 0;
237 led_config += led_status.led_2 ? 2 : 0;
238 led_config += led_status.led_3 ? 4 : 0;
239 led_config += led_status.led_4 ? 8 : 0;
240
241 return static_cast<Common::Input::DriverResult>(
242 handle->SetLedConfig(static_cast<u8>(led_config)));
243}
244
245Common::Input::DriverResult Joycons::SetCameraFormat(const PadIdentifier& identifier,
246 Common::Input::CameraFormat camera_format) {
247 auto handle = GetHandle(identifier);
248 if (handle == nullptr) {
249 return Common::Input::DriverResult::InvalidHandle;
250 }
251 return static_cast<Common::Input::DriverResult>(handle->SetIrsConfig(
252 Joycon::IrsMode::ImageTransfer, static_cast<Joycon::IrsResolution>(camera_format)));
253};
254
255Common::Input::NfcState Joycons::SupportsNfc(const PadIdentifier& identifier_) const {
256 return Common::Input::NfcState::Success;
257};
258
259Common::Input::NfcState Joycons::WriteNfcData(const PadIdentifier& identifier_,
260 const std::vector<u8>& data) {
261 return Common::Input::NfcState::NotSupported;
262};
263
264Common::Input::DriverResult Joycons::SetPollingMode(const PadIdentifier& identifier,
265 const Common::Input::PollingMode polling_mode) {
266 auto handle = GetHandle(identifier);
267 if (handle == nullptr) {
268 LOG_ERROR(Input, "Invalid handle {}", identifier.port);
269 return Common::Input::DriverResult::InvalidHandle;
270 }
271
272 switch (polling_mode) {
273 case Common::Input::PollingMode::Active:
274 return static_cast<Common::Input::DriverResult>(handle->SetActiveMode());
275 case Common::Input::PollingMode::Pasive:
276 return static_cast<Common::Input::DriverResult>(handle->SetPasiveMode());
277 case Common::Input::PollingMode::IR:
278 return static_cast<Common::Input::DriverResult>(handle->SetIrMode());
279 case Common::Input::PollingMode::NFC:
280 return static_cast<Common::Input::DriverResult>(handle->SetNfcMode());
281 case Common::Input::PollingMode::Ring:
282 return static_cast<Common::Input::DriverResult>(handle->SetRingConMode());
283 default:
284 return Common::Input::DriverResult::NotSupported;
285 }
286}
287
288void Joycons::OnBatteryUpdate(std::size_t port, Joycon::ControllerType type,
289 Joycon::Battery value) {
290 const auto identifier = GetIdentifier(port, type);
291 if (value.charging != 0) {
292 SetBattery(identifier, Common::Input::BatteryLevel::Charging);
293 return;
294 }
295
296 Common::Input::BatteryLevel battery{};
297 switch (value.status) {
298 case 0:
299 battery = Common::Input::BatteryLevel::Empty;
300 break;
301 case 1:
302 battery = Common::Input::BatteryLevel::Critical;
303 break;
304 case 2:
305 battery = Common::Input::BatteryLevel::Low;
306 break;
307 case 3:
308 battery = Common::Input::BatteryLevel::Medium;
309 break;
310 case 4:
311 default:
312 battery = Common::Input::BatteryLevel::Full;
313 break;
314 }
315 SetBattery(identifier, battery);
316}
317
318void Joycons::OnColorUpdate(std::size_t port, Joycon::ControllerType type,
319 const Joycon::Color& value) {
320 const auto identifier = GetIdentifier(port, type);
321 Common::Input::BodyColorStatus color{
322 .body = value.body,
323 .buttons = value.buttons,
324 .left_grip = value.left_grip,
325 .right_grip = value.right_grip,
326 };
327 SetColor(identifier, color);
328}
329
330void Joycons::OnButtonUpdate(std::size_t port, Joycon::ControllerType type, int id, bool value) {
331 const auto identifier = GetIdentifier(port, type);
332 SetButton(identifier, id, value);
333}
334
335void Joycons::OnStickUpdate(std::size_t port, Joycon::ControllerType type, int id, f32 value) {
336 const auto identifier = GetIdentifier(port, type);
337 SetAxis(identifier, id, value);
338}
339
340void Joycons::OnMotionUpdate(std::size_t port, Joycon::ControllerType type, int id,
341 const Joycon::MotionData& value) {
342 const auto identifier = GetIdentifier(port, type);
343 BasicMotion motion_data{
344 .gyro_x = value.gyro_x,
345 .gyro_y = value.gyro_y,
346 .gyro_z = value.gyro_z,
347 .accel_x = value.accel_x,
348 .accel_y = value.accel_y,
349 .accel_z = value.accel_z,
350 .delta_timestamp = 15000,
351 };
352 SetMotion(identifier, id, motion_data);
353}
354
355void Joycons::OnRingConUpdate(f32 ring_data) {
356 // To simplify ring detection it will always be mapped to an empty identifier for all
357 // controllers
358 constexpr PadIdentifier identifier = {
359 .guid = Common::UUID{},
360 .port = 0,
361 .pad = 0,
362 };
363 SetAxis(identifier, 100, ring_data);
364}
365
366void Joycons::OnAmiiboUpdate(std::size_t port, const std::vector<u8>& amiibo_data) {
367 const auto identifier = GetIdentifier(port, Joycon::ControllerType::Right);
368 const auto nfc_state = amiibo_data.empty() ? Common::Input::NfcState::AmiiboRemoved
369 : Common::Input::NfcState::NewAmiibo;
370 SetNfc(identifier, {nfc_state, amiibo_data});
371}
372
373void Joycons::OnCameraUpdate(std::size_t port, const std::vector<u8>& camera_data,
374 Joycon::IrsResolution format) {
375 const auto identifier = GetIdentifier(port, Joycon::ControllerType::Right);
376 SetCamera(identifier, {static_cast<Common::Input::CameraFormat>(format), camera_data});
377}
378
379std::shared_ptr<Joycon::JoyconDriver> Joycons::GetHandle(PadIdentifier identifier) const {
380 auto is_handle_active = [&](std::shared_ptr<Joycon::JoyconDriver> device) {
381 if (!device) {
382 return false;
383 }
384 if (!device->IsConnected()) {
385 return false;
386 }
387 if (device->GetDevicePort() == identifier.port) {
388 return true;
389 }
390 return false;
391 };
392 const auto type = static_cast<Joycon::ControllerType>(identifier.pad);
393
394 if (type == Joycon::ControllerType::Left) {
395 const auto matching_device = std::ranges::find_if(
396 left_joycons, [is_handle_active](auto& device) { return is_handle_active(device); });
397
398 if (matching_device != left_joycons.end()) {
399 return *matching_device;
400 }
401 }
402
403 if (type == Joycon::ControllerType::Right) {
404 const auto matching_device = std::ranges::find_if(
405 right_joycons, [is_handle_active](auto& device) { return is_handle_active(device); });
406
407 if (matching_device != right_joycons.end()) {
408 return *matching_device;
409 }
410 }
411
412 return nullptr;
413}
414
415PadIdentifier Joycons::GetIdentifier(std::size_t port, Joycon::ControllerType type) const {
416 const std::array<u8, 16> guid{0, 0, 0, 0, 0, 0, 0, 0,
417 0, 0, 0, 0, 0, 0, 0, static_cast<u8>(type)};
418 return {
419 .guid = Common::UUID{guid},
420 .port = port,
421 .pad = static_cast<std::size_t>(type),
422 };
423}
424
425Common::ParamPackage Joycons::GetParamPackage(std::size_t port, Joycon::ControllerType type) const {
426 const auto identifier = GetIdentifier(port, type);
427 return {
428 {"engine", GetEngineName()},
429 {"guid", identifier.guid.RawString()},
430 {"port", std::to_string(identifier.port)},
431 {"pad", std::to_string(identifier.pad)},
432 };
433}
434
435std::vector<Common::ParamPackage> Joycons::GetInputDevices() const {
436 std::vector<Common::ParamPackage> devices{};
437
438 auto add_entry = [&](std::shared_ptr<Joycon::JoyconDriver> device) {
439 if (!device) {
440 return;
441 }
442 if (!device->IsConnected()) {
443 return;
444 }
445 auto param = GetParamPackage(device->GetDevicePort(), device->GetHandleDeviceType());
446 std::string name = fmt::format("{} {}", JoyconName(device->GetHandleDeviceType()),
447 device->GetDevicePort() + 1);
448 param.Set("display", std::move(name));
449 devices.emplace_back(param);
450 };
451
452 for (const auto& controller : left_joycons) {
453 add_entry(controller);
454 }
455 for (const auto& controller : right_joycons) {
456 add_entry(controller);
457 }
458
459 // List dual joycon pairs
460 for (std::size_t i = 0; i < MaxSupportedControllers; i++) {
461 if (!left_joycons[i] || !right_joycons[i]) {
462 continue;
463 }
464 if (!left_joycons[i]->IsConnected() || !right_joycons[i]->IsConnected()) {
465 continue;
466 }
467 auto main_param = GetParamPackage(i, left_joycons[i]->GetHandleDeviceType());
468 const auto second_param = GetParamPackage(i, right_joycons[i]->GetHandleDeviceType());
469 const auto type = Joycon::ControllerType::Dual;
470 std::string name = fmt::format("{} {}", JoyconName(type), i + 1);
471
472 main_param.Set("display", std::move(name));
473 main_param.Set("guid2", second_param.Get("guid", ""));
474 main_param.Set("pad", std::to_string(static_cast<size_t>(type)));
475 devices.emplace_back(main_param);
476 }
477
478 return devices;
479}
480
481ButtonMapping Joycons::GetButtonMappingForDevice(const Common::ParamPackage& params) {
482 static constexpr std::array<std::tuple<Settings::NativeButton::Values, Joycon::PadButton, bool>,
483 18>
484 switch_to_joycon_button = {
485 std::tuple{Settings::NativeButton::A, Joycon::PadButton::A, true},
486 {Settings::NativeButton::B, Joycon::PadButton::B, true},
487 {Settings::NativeButton::X, Joycon::PadButton::X, true},
488 {Settings::NativeButton::Y, Joycon::PadButton::Y, true},
489 {Settings::NativeButton::DLeft, Joycon::PadButton::Left, false},
490 {Settings::NativeButton::DUp, Joycon::PadButton::Up, false},
491 {Settings::NativeButton::DRight, Joycon::PadButton::Right, false},
492 {Settings::NativeButton::DDown, Joycon::PadButton::Down, false},
493 {Settings::NativeButton::L, Joycon::PadButton::L, false},
494 {Settings::NativeButton::R, Joycon::PadButton::R, true},
495 {Settings::NativeButton::ZL, Joycon::PadButton::ZL, false},
496 {Settings::NativeButton::ZR, Joycon::PadButton::ZR, true},
497 {Settings::NativeButton::Plus, Joycon::PadButton::Plus, true},
498 {Settings::NativeButton::Minus, Joycon::PadButton::Minus, false},
499 {Settings::NativeButton::Home, Joycon::PadButton::Home, true},
500 {Settings::NativeButton::Screenshot, Joycon::PadButton::Capture, false},
501 {Settings::NativeButton::LStick, Joycon::PadButton::StickL, false},
502 {Settings::NativeButton::RStick, Joycon::PadButton::StickR, true},
503 };
504
505 if (!params.Has("port")) {
506 return {};
507 }
508
509 ButtonMapping mapping{};
510 for (const auto& [switch_button, joycon_button, side] : switch_to_joycon_button) {
511 const std::size_t port = static_cast<std::size_t>(params.Get("port", 0));
512 auto pad = static_cast<Joycon::ControllerType>(params.Get("pad", 0));
513 if (pad == Joycon::ControllerType::Dual) {
514 pad = side ? Joycon::ControllerType::Right : Joycon::ControllerType::Left;
515 }
516
517 Common::ParamPackage button_params = GetParamPackage(port, pad);
518 button_params.Set("button", static_cast<int>(joycon_button));
519 mapping.insert_or_assign(switch_button, std::move(button_params));
520 }
521
522 // Map SL and SR buttons for left joycons
523 if (params.Get("pad", 0) == static_cast<int>(Joycon::ControllerType::Left)) {
524 const std::size_t port = static_cast<std::size_t>(params.Get("port", 0));
525 Common::ParamPackage button_params = GetParamPackage(port, Joycon::ControllerType::Left);
526
527 Common::ParamPackage sl_button_params = button_params;
528 Common::ParamPackage sr_button_params = button_params;
529 sl_button_params.Set("button", static_cast<int>(Joycon::PadButton::LeftSL));
530 sr_button_params.Set("button", static_cast<int>(Joycon::PadButton::LeftSR));
531 mapping.insert_or_assign(Settings::NativeButton::SL, std::move(sl_button_params));
532 mapping.insert_or_assign(Settings::NativeButton::SR, std::move(sr_button_params));
533 }
534
535 // Map SL and SR buttons for right joycons
536 if (params.Get("pad", 0) == static_cast<int>(Joycon::ControllerType::Right)) {
537 const std::size_t port = static_cast<std::size_t>(params.Get("port", 0));
538 Common::ParamPackage button_params = GetParamPackage(port, Joycon::ControllerType::Right);
539
540 Common::ParamPackage sl_button_params = button_params;
541 Common::ParamPackage sr_button_params = button_params;
542 sl_button_params.Set("button", static_cast<int>(Joycon::PadButton::RightSL));
543 sr_button_params.Set("button", static_cast<int>(Joycon::PadButton::RightSR));
544 mapping.insert_or_assign(Settings::NativeButton::SL, std::move(sl_button_params));
545 mapping.insert_or_assign(Settings::NativeButton::SR, std::move(sr_button_params));
546 }
547
548 return mapping;
549}
550
551AnalogMapping Joycons::GetAnalogMappingForDevice(const Common::ParamPackage& params) {
552 if (!params.Has("port")) {
553 return {};
554 }
555
556 const std::size_t port = static_cast<std::size_t>(params.Get("port", 0));
557 auto pad_left = static_cast<Joycon::ControllerType>(params.Get("pad", 0));
558 auto pad_right = pad_left;
559 if (pad_left == Joycon::ControllerType::Dual) {
560 pad_left = Joycon::ControllerType::Left;
561 pad_right = Joycon::ControllerType::Right;
562 }
563
564 AnalogMapping mapping = {};
565 Common::ParamPackage left_analog_params = GetParamPackage(port, pad_left);
566 left_analog_params.Set("axis_x", static_cast<int>(Joycon::PadAxes::LeftStickX));
567 left_analog_params.Set("axis_y", static_cast<int>(Joycon::PadAxes::LeftStickY));
568 mapping.insert_or_assign(Settings::NativeAnalog::LStick, std::move(left_analog_params));
569 Common::ParamPackage right_analog_params = GetParamPackage(port, pad_right);
570 right_analog_params.Set("axis_x", static_cast<int>(Joycon::PadAxes::RightStickX));
571 right_analog_params.Set("axis_y", static_cast<int>(Joycon::PadAxes::RightStickY));
572 mapping.insert_or_assign(Settings::NativeAnalog::RStick, std::move(right_analog_params));
573 return mapping;
574}
575
576MotionMapping Joycons::GetMotionMappingForDevice(const Common::ParamPackage& params) {
577 if (!params.Has("port")) {
578 return {};
579 }
580
581 const std::size_t port = static_cast<std::size_t>(params.Get("port", 0));
582 auto pad_left = static_cast<Joycon::ControllerType>(params.Get("pad", 0));
583 auto pad_right = pad_left;
584 if (pad_left == Joycon::ControllerType::Dual) {
585 pad_left = Joycon::ControllerType::Left;
586 pad_right = Joycon::ControllerType::Right;
587 }
588
589 MotionMapping mapping = {};
590 Common::ParamPackage left_motion_params = GetParamPackage(port, pad_left);
591 left_motion_params.Set("motion", 0);
592 mapping.insert_or_assign(Settings::NativeMotion::MotionLeft, std::move(left_motion_params));
593 Common::ParamPackage right_Motion_params = GetParamPackage(port, pad_right);
594 right_Motion_params.Set("motion", 1);
595 mapping.insert_or_assign(Settings::NativeMotion::MotionRight, std::move(right_Motion_params));
596 return mapping;
597}
598
599Common::Input::ButtonNames Joycons::GetUIButtonName(const Common::ParamPackage& params) const {
600 const auto button = static_cast<Joycon::PadButton>(params.Get("button", 0));
601 switch (button) {
602 case Joycon::PadButton::Left:
603 return Common::Input::ButtonNames::ButtonLeft;
604 case Joycon::PadButton::Right:
605 return Common::Input::ButtonNames::ButtonRight;
606 case Joycon::PadButton::Down:
607 return Common::Input::ButtonNames::ButtonDown;
608 case Joycon::PadButton::Up:
609 return Common::Input::ButtonNames::ButtonUp;
610 case Joycon::PadButton::LeftSL:
611 case Joycon::PadButton::RightSL:
612 return Common::Input::ButtonNames::TriggerSL;
613 case Joycon::PadButton::LeftSR:
614 case Joycon::PadButton::RightSR:
615 return Common::Input::ButtonNames::TriggerSR;
616 case Joycon::PadButton::L:
617 return Common::Input::ButtonNames::TriggerL;
618 case Joycon::PadButton::R:
619 return Common::Input::ButtonNames::TriggerR;
620 case Joycon::PadButton::ZL:
621 return Common::Input::ButtonNames::TriggerZL;
622 case Joycon::PadButton::ZR:
623 return Common::Input::ButtonNames::TriggerZR;
624 case Joycon::PadButton::A:
625 return Common::Input::ButtonNames::ButtonA;
626 case Joycon::PadButton::B:
627 return Common::Input::ButtonNames::ButtonB;
628 case Joycon::PadButton::X:
629 return Common::Input::ButtonNames::ButtonX;
630 case Joycon::PadButton::Y:
631 return Common::Input::ButtonNames::ButtonY;
632 case Joycon::PadButton::Plus:
633 return Common::Input::ButtonNames::ButtonPlus;
634 case Joycon::PadButton::Minus:
635 return Common::Input::ButtonNames::ButtonMinus;
636 case Joycon::PadButton::Home:
637 return Common::Input::ButtonNames::ButtonHome;
638 case Joycon::PadButton::Capture:
639 return Common::Input::ButtonNames::ButtonCapture;
640 case Joycon::PadButton::StickL:
641 return Common::Input::ButtonNames::ButtonStickL;
642 case Joycon::PadButton::StickR:
643 return Common::Input::ButtonNames::ButtonStickR;
644 default:
645 return Common::Input::ButtonNames::Undefined;
646 }
647}
648
649Common::Input::ButtonNames Joycons::GetUIName(const Common::ParamPackage& params) const {
650 if (params.Has("button")) {
651 return GetUIButtonName(params);
652 }
653 if (params.Has("axis")) {
654 return Common::Input::ButtonNames::Value;
655 }
656 if (params.Has("motion")) {
657 return Common::Input::ButtonNames::Engine;
658 }
659
660 return Common::Input::ButtonNames::Invalid;
661}
662
663std::string Joycons::JoyconName(Joycon::ControllerType type) const {
664 switch (type) {
665 case Joycon::ControllerType::Left:
666 return "Left Joycon";
667 case Joycon::ControllerType::Right:
668 return "Right Joycon";
669 case Joycon::ControllerType::Pro:
670 return "Pro Controller";
671 case Joycon::ControllerType::Dual:
672 return "Dual Joycon";
673 default:
674 return "Unknown Switch Controller";
675 }
676}
677} // namespace InputCommon
diff --git a/src/input_common/drivers/joycon.h b/src/input_common/drivers/joycon.h
new file mode 100644
index 000000000..2149ab7fd
--- /dev/null
+++ b/src/input_common/drivers/joycon.h
@@ -0,0 +1,111 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include <array>
7#include <span>
8#include <thread>
9#include <SDL_hidapi.h>
10
11#include "input_common/input_engine.h"
12
13namespace InputCommon::Joycon {
14using SerialNumber = std::array<u8, 15>;
15struct Battery;
16struct Color;
17struct MotionData;
18enum class ControllerType : u8;
19enum class DriverResult;
20enum class IrsResolution;
21class JoyconDriver;
22} // namespace InputCommon::Joycon
23
24namespace InputCommon {
25
26class Joycons final : public InputCommon::InputEngine {
27public:
28 explicit Joycons(const std::string& input_engine_);
29
30 ~Joycons();
31
32 bool IsVibrationEnabled(const PadIdentifier& identifier) override;
33 Common::Input::DriverResult SetVibration(
34 const PadIdentifier& identifier, const Common::Input::VibrationStatus& vibration) override;
35
36 Common::Input::DriverResult SetLeds(const PadIdentifier& identifier,
37 const Common::Input::LedStatus& led_status) override;
38
39 Common::Input::DriverResult SetCameraFormat(const PadIdentifier& identifier,
40 Common::Input::CameraFormat camera_format) override;
41
42 Common::Input::NfcState SupportsNfc(const PadIdentifier& identifier_) const override;
43 Common::Input::NfcState WriteNfcData(const PadIdentifier& identifier_,
44 const std::vector<u8>& data) override;
45
46 Common::Input::DriverResult SetPollingMode(
47 const PadIdentifier& identifier, const Common::Input::PollingMode polling_mode) override;
48
49 /// Used for automapping features
50 std::vector<Common::ParamPackage> GetInputDevices() const override;
51 ButtonMapping GetButtonMappingForDevice(const Common::ParamPackage& params) override;
52 AnalogMapping GetAnalogMappingForDevice(const Common::ParamPackage& params) override;
53 MotionMapping GetMotionMappingForDevice(const Common::ParamPackage& params) override;
54 Common::Input::ButtonNames GetUIName(const Common::ParamPackage& params) const override;
55
56private:
57 static constexpr std::size_t MaxSupportedControllers = 8;
58
59 /// For shutting down, clear all data, join all threads, release usb devices
60 void Reset();
61
62 /// Registers controllers, clears all data and starts the scan thread
63 void Setup();
64
65 /// Actively searchs for new devices
66 void ScanThread(std::stop_token stop_token);
67
68 /// Returns true if device is valid and not registered
69 bool IsDeviceNew(SDL_hid_device_info* device_info) const;
70
71 /// Tries to connect to the new device
72 void RegisterNewDevice(SDL_hid_device_info* device_info);
73
74 /// Returns the next free handle
75 std::shared_ptr<Joycon::JoyconDriver> GetNextFreeHandle(Joycon::ControllerType type) const;
76
77 void OnBatteryUpdate(std::size_t port, Joycon::ControllerType type, Joycon::Battery value);
78 void OnColorUpdate(std::size_t port, Joycon::ControllerType type, const Joycon::Color& value);
79 void OnButtonUpdate(std::size_t port, Joycon::ControllerType type, int id, bool value);
80 void OnStickUpdate(std::size_t port, Joycon::ControllerType type, int id, f32 value);
81 void OnMotionUpdate(std::size_t port, Joycon::ControllerType type, int id,
82 const Joycon::MotionData& value);
83 void OnRingConUpdate(f32 ring_data);
84 void OnAmiiboUpdate(std::size_t port, const std::vector<u8>& amiibo_data);
85 void OnCameraUpdate(std::size_t port, const std::vector<u8>& camera_data,
86 Joycon::IrsResolution format);
87
88 /// Returns a JoyconHandle corresponding to a PadIdentifier
89 std::shared_ptr<Joycon::JoyconDriver> GetHandle(PadIdentifier identifier) const;
90
91 /// Returns a PadIdentifier corresponding to the port number and joycon type
92 PadIdentifier GetIdentifier(std::size_t port, Joycon::ControllerType type) const;
93
94 /// Returns a ParamPackage corresponding to the port number and joycon type
95 Common::ParamPackage GetParamPackage(std::size_t port, Joycon::ControllerType type) const;
96
97 std::string JoyconName(std::size_t port) const;
98
99 Common::Input::ButtonNames GetUIButtonName(const Common::ParamPackage& params) const;
100
101 /// Returns the name of the device in text format
102 std::string JoyconName(Joycon::ControllerType type) const;
103
104 std::jthread scan_thread;
105
106 // Joycon types are split by type to ease supporting dualjoycon configurations
107 std::array<std::shared_ptr<Joycon::JoyconDriver>, MaxSupportedControllers> left_joycons{};
108 std::array<std::shared_ptr<Joycon::JoyconDriver>, MaxSupportedControllers> right_joycons{};
109};
110
111} // namespace InputCommon
diff --git a/src/input_common/drivers/sdl_driver.cpp b/src/input_common/drivers/sdl_driver.cpp
index 4818bb744..d975eb815 100644
--- a/src/input_common/drivers/sdl_driver.cpp
+++ b/src/input_common/drivers/sdl_driver.cpp
@@ -40,25 +40,26 @@ public:
40 } 40 }
41 41
42 void EnableMotion() { 42 void EnableMotion() {
43 if (sdl_controller) { 43 if (!sdl_controller) {
44 SDL_GameController* controller = sdl_controller.get(); 44 return;
45 has_accel = SDL_GameControllerHasSensor(controller, SDL_SENSOR_ACCEL) == SDL_TRUE; 45 }
46 has_gyro = SDL_GameControllerHasSensor(controller, SDL_SENSOR_GYRO) == SDL_TRUE; 46 SDL_GameController* controller = sdl_controller.get();
47 if (has_accel) { 47 if (HasMotion()) {
48 SDL_GameControllerSetSensorEnabled(controller, SDL_SENSOR_ACCEL, SDL_TRUE); 48 SDL_GameControllerSetSensorEnabled(controller, SDL_SENSOR_ACCEL, SDL_FALSE);
49 } 49 SDL_GameControllerSetSensorEnabled(controller, SDL_SENSOR_GYRO, SDL_FALSE);
50 if (has_gyro) { 50 }
51 SDL_GameControllerSetSensorEnabled(controller, SDL_SENSOR_GYRO, SDL_TRUE); 51 has_accel = SDL_GameControllerHasSensor(controller, SDL_SENSOR_ACCEL) == SDL_TRUE;
52 } 52 has_gyro = SDL_GameControllerHasSensor(controller, SDL_SENSOR_GYRO) == SDL_TRUE;
53 if (has_accel) {
54 SDL_GameControllerSetSensorEnabled(controller, SDL_SENSOR_ACCEL, SDL_TRUE);
55 }
56 if (has_gyro) {
57 SDL_GameControllerSetSensorEnabled(controller, SDL_SENSOR_GYRO, SDL_TRUE);
53 } 58 }
54 } 59 }
55 60
56 bool HasGyro() const { 61 bool HasMotion() const {
57 return has_gyro; 62 return has_gyro || has_accel;
58 }
59
60 bool HasAccel() const {
61 return has_accel;
62 } 63 }
63 64
64 bool UpdateMotion(SDL_ControllerSensorEvent event) { 65 bool UpdateMotion(SDL_ControllerSensorEvent event) {
@@ -85,6 +86,20 @@ public:
85 if (time_difference == 0) { 86 if (time_difference == 0) {
86 return false; 87 return false;
87 } 88 }
89
90 // Motion data is invalid
91 if (motion.accel_x == 0 && motion.gyro_x == 0 && motion.accel_y == 0 &&
92 motion.gyro_y == 0 && motion.accel_z == 0 && motion.gyro_z == 0) {
93 if (motion_error_count++ < 200) {
94 return false;
95 }
96 // Try restarting the sensor
97 motion_error_count = 0;
98 EnableMotion();
99 return false;
100 }
101
102 motion_error_count = 0;
88 motion.delta_timestamp = time_difference * 1000; 103 motion.delta_timestamp = time_difference * 1000;
89 return true; 104 return true;
90 } 105 }
@@ -250,6 +265,7 @@ private:
250 mutable std::mutex mutex; 265 mutable std::mutex mutex;
251 266
252 u64 last_motion_update{}; 267 u64 last_motion_update{};
268 std::size_t motion_error_count{};
253 bool has_gyro{false}; 269 bool has_gyro{false};
254 bool has_accel{false}; 270 bool has_accel{false};
255 bool has_vibration{false}; 271 bool has_vibration{false};
@@ -318,6 +334,15 @@ void SDLDriver::InitJoystick(int joystick_index) {
318 334
319 const auto guid = GetGUID(sdl_joystick); 335 const auto guid = GetGUID(sdl_joystick);
320 336
337 if (Settings::values.enable_joycon_driver) {
338 if (guid.uuid[5] == 0x05 && guid.uuid[4] == 0x7e &&
339 (guid.uuid[8] == 0x06 || guid.uuid[8] == 0x07)) {
340 LOG_WARNING(Input, "Preferring joycon driver for device index {}", joystick_index);
341 SDL_JoystickClose(sdl_joystick);
342 return;
343 }
344 }
345
321 std::scoped_lock lock{joystick_map_mutex}; 346 std::scoped_lock lock{joystick_map_mutex};
322 if (joystick_map.find(guid) == joystick_map.end()) { 347 if (joystick_map.find(guid) == joystick_map.end()) {
323 auto joystick = std::make_shared<SDLJoystick>(guid, 0, sdl_joystick, sdl_gamecontroller); 348 auto joystick = std::make_shared<SDLJoystick>(guid, 0, sdl_joystick, sdl_gamecontroller);
@@ -440,9 +465,13 @@ SDLDriver::SDLDriver(std::string input_engine_) : InputEngine(std::move(input_en
440 SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_PS5_RUMBLE, "1"); 465 SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_PS5_RUMBLE, "1");
441 SDL_SetHint(SDL_HINT_JOYSTICK_ALLOW_BACKGROUND_EVENTS, "1"); 466 SDL_SetHint(SDL_HINT_JOYSTICK_ALLOW_BACKGROUND_EVENTS, "1");
442 467
443 // Use hidapi driver for joycons. This will allow joycons to be detected as a GameController and 468 // Disable hidapi drivers for switch controllers when the custom joycon driver is enabled
444 // not a generic one 469 if (Settings::values.enable_joycon_driver) {
445 SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_JOY_CONS, "1"); 470 SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_JOY_CONS, "0");
471 } else {
472 SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_JOY_CONS, "1");
473 }
474 SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_SWITCH, "1");
446 475
447 // Disable hidapi driver for xbox. Already default on Windows, this causes conflict with native 476 // Disable hidapi driver for xbox. Already default on Windows, this causes conflict with native
448 // driver on Linux. 477 // driver on Linux.
@@ -532,7 +561,7 @@ std::vector<Common::ParamPackage> SDLDriver::GetInputDevices() const {
532 return devices; 561 return devices;
533} 562}
534 563
535Common::Input::VibrationError SDLDriver::SetVibration( 564Common::Input::DriverResult SDLDriver::SetVibration(
536 const PadIdentifier& identifier, const Common::Input::VibrationStatus& vibration) { 565 const PadIdentifier& identifier, const Common::Input::VibrationStatus& vibration) {
537 const auto joystick = 566 const auto joystick =
538 GetSDLJoystickByGUID(identifier.guid.RawString(), static_cast<int>(identifier.port)); 567 GetSDLJoystickByGUID(identifier.guid.RawString(), static_cast<int>(identifier.port));
@@ -566,7 +595,7 @@ Common::Input::VibrationError SDLDriver::SetVibration(
566 .vibration = new_vibration, 595 .vibration = new_vibration,
567 }); 596 });
568 597
569 return Common::Input::VibrationError::None; 598 return Common::Input::DriverResult::Success;
570} 599}
571 600
572bool SDLDriver::IsVibrationEnabled(const PadIdentifier& identifier) { 601bool SDLDriver::IsVibrationEnabled(const PadIdentifier& identifier) {
@@ -942,18 +971,18 @@ MotionMapping SDLDriver::GetMotionMappingForDevice(const Common::ParamPackage& p
942 MotionMapping mapping = {}; 971 MotionMapping mapping = {};
943 joystick->EnableMotion(); 972 joystick->EnableMotion();
944 973
945 if (joystick->HasGyro() || joystick->HasAccel()) { 974 if (joystick->HasMotion()) {
946 mapping.insert_or_assign(Settings::NativeMotion::MotionRight, 975 mapping.insert_or_assign(Settings::NativeMotion::MotionRight,
947 BuildMotionParam(joystick->GetPort(), joystick->GetGUID())); 976 BuildMotionParam(joystick->GetPort(), joystick->GetGUID()));
948 } 977 }
949 if (params.Has("guid2")) { 978 if (params.Has("guid2")) {
950 joystick2->EnableMotion(); 979 joystick2->EnableMotion();
951 if (joystick2->HasGyro() || joystick2->HasAccel()) { 980 if (joystick2->HasMotion()) {
952 mapping.insert_or_assign(Settings::NativeMotion::MotionLeft, 981 mapping.insert_or_assign(Settings::NativeMotion::MotionLeft,
953 BuildMotionParam(joystick2->GetPort(), joystick2->GetGUID())); 982 BuildMotionParam(joystick2->GetPort(), joystick2->GetGUID()));
954 } 983 }
955 } else { 984 } else {
956 if (joystick->HasGyro() || joystick->HasAccel()) { 985 if (joystick->HasMotion()) {
957 mapping.insert_or_assign(Settings::NativeMotion::MotionLeft, 986 mapping.insert_or_assign(Settings::NativeMotion::MotionLeft,
958 BuildMotionParam(joystick->GetPort(), joystick->GetGUID())); 987 BuildMotionParam(joystick->GetPort(), joystick->GetGUID()));
959 } 988 }
diff --git a/src/input_common/drivers/sdl_driver.h b/src/input_common/drivers/sdl_driver.h
index 366bcc496..ffde169b3 100644
--- a/src/input_common/drivers/sdl_driver.h
+++ b/src/input_common/drivers/sdl_driver.h
@@ -63,7 +63,7 @@ public:
63 63
64 bool IsStickInverted(const Common::ParamPackage& params) override; 64 bool IsStickInverted(const Common::ParamPackage& params) override;
65 65
66 Common::Input::VibrationError SetVibration( 66 Common::Input::DriverResult SetVibration(
67 const PadIdentifier& identifier, const Common::Input::VibrationStatus& vibration) override; 67 const PadIdentifier& identifier, const Common::Input::VibrationStatus& vibration) override;
68 68
69 bool IsVibrationEnabled(const PadIdentifier& identifier) override; 69 bool IsVibrationEnabled(const PadIdentifier& identifier) override;
diff --git a/src/input_common/drivers/virtual_amiibo.cpp b/src/input_common/drivers/virtual_amiibo.cpp
index 63ffaca67..4a0268a4d 100644
--- a/src/input_common/drivers/virtual_amiibo.cpp
+++ b/src/input_common/drivers/virtual_amiibo.cpp
@@ -22,22 +22,23 @@ VirtualAmiibo::VirtualAmiibo(std::string input_engine_) : InputEngine(std::move(
22 22
23VirtualAmiibo::~VirtualAmiibo() = default; 23VirtualAmiibo::~VirtualAmiibo() = default;
24 24
25Common::Input::PollingError VirtualAmiibo::SetPollingMode( 25Common::Input::DriverResult VirtualAmiibo::SetPollingMode(
26 [[maybe_unused]] const PadIdentifier& identifier_, 26 [[maybe_unused]] const PadIdentifier& identifier_,
27 const Common::Input::PollingMode polling_mode_) { 27 const Common::Input::PollingMode polling_mode_) {
28 polling_mode = polling_mode_; 28 polling_mode = polling_mode_;
29 29
30 if (polling_mode == Common::Input::PollingMode::NFC) { 30 switch (polling_mode) {
31 case Common::Input::PollingMode::NFC:
31 if (state == State::Initialized) { 32 if (state == State::Initialized) {
32 state = State::WaitingForAmiibo; 33 state = State::WaitingForAmiibo;
33 } 34 }
34 } else { 35 return Common::Input::DriverResult::Success;
36 default:
35 if (state == State::AmiiboIsOpen) { 37 if (state == State::AmiiboIsOpen) {
36 CloseAmiibo(); 38 CloseAmiibo();
37 } 39 }
40 return Common::Input::DriverResult::NotSupported;
38 } 41 }
39
40 return Common::Input::PollingError::None;
41} 42}
42 43
43Common::Input::NfcState VirtualAmiibo::SupportsNfc( 44Common::Input::NfcState VirtualAmiibo::SupportsNfc(
diff --git a/src/input_common/drivers/virtual_amiibo.h b/src/input_common/drivers/virtual_amiibo.h
index 0f9dad333..13cacfc0a 100644
--- a/src/input_common/drivers/virtual_amiibo.h
+++ b/src/input_common/drivers/virtual_amiibo.h
@@ -36,7 +36,7 @@ public:
36 ~VirtualAmiibo() override; 36 ~VirtualAmiibo() override;
37 37
38 // Sets polling mode to a controller 38 // Sets polling mode to a controller
39 Common::Input::PollingError SetPollingMode( 39 Common::Input::DriverResult SetPollingMode(
40 const PadIdentifier& identifier_, const Common::Input::PollingMode polling_mode_) override; 40 const PadIdentifier& identifier_, const Common::Input::PollingMode polling_mode_) override;
41 41
42 Common::Input::NfcState SupportsNfc(const PadIdentifier& identifier_) const override; 42 Common::Input::NfcState SupportsNfc(const PadIdentifier& identifier_) const override;
diff --git a/src/input_common/helpers/joycon_driver.cpp b/src/input_common/helpers/joycon_driver.cpp
new file mode 100644
index 000000000..8f94c9f45
--- /dev/null
+++ b/src/input_common/helpers/joycon_driver.cpp
@@ -0,0 +1,575 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "common/logging/log.h"
5#include "common/swap.h"
6#include "common/thread.h"
7#include "input_common/helpers/joycon_driver.h"
8#include "input_common/helpers/joycon_protocol/calibration.h"
9#include "input_common/helpers/joycon_protocol/generic_functions.h"
10#include "input_common/helpers/joycon_protocol/irs.h"
11#include "input_common/helpers/joycon_protocol/nfc.h"
12#include "input_common/helpers/joycon_protocol/poller.h"
13#include "input_common/helpers/joycon_protocol/ringcon.h"
14#include "input_common/helpers/joycon_protocol/rumble.h"
15
16namespace InputCommon::Joycon {
17JoyconDriver::JoyconDriver(std::size_t port_) : port{port_} {
18 hidapi_handle = std::make_shared<JoyconHandle>();
19}
20
21JoyconDriver::~JoyconDriver() {
22 Stop();
23}
24
25void JoyconDriver::Stop() {
26 is_connected = false;
27 input_thread = {};
28}
29
30DriverResult JoyconDriver::RequestDeviceAccess(SDL_hid_device_info* device_info) {
31 std::scoped_lock lock{mutex};
32
33 handle_device_type = ControllerType::None;
34 GetDeviceType(device_info, handle_device_type);
35 if (handle_device_type == ControllerType::None) {
36 return DriverResult::UnsupportedControllerType;
37 }
38
39 hidapi_handle->handle =
40 SDL_hid_open(device_info->vendor_id, device_info->product_id, device_info->serial_number);
41 std::memcpy(&handle_serial_number, device_info->serial_number, 15);
42 if (!hidapi_handle->handle) {
43 LOG_ERROR(Input, "Yuzu can't gain access to this device: ID {:04X}:{:04X}.",
44 device_info->vendor_id, device_info->product_id);
45 return DriverResult::HandleInUse;
46 }
47 SDL_hid_set_nonblocking(hidapi_handle->handle, 1);
48 return DriverResult::Success;
49}
50
51DriverResult JoyconDriver::InitializeDevice() {
52 if (!hidapi_handle->handle) {
53 return DriverResult::InvalidHandle;
54 }
55 std::scoped_lock lock{mutex};
56 disable_input_thread = true;
57
58 // Reset Counters
59 error_counter = 0;
60 hidapi_handle->packet_counter = 0;
61
62 // Reset external device status
63 starlink_connected = false;
64 ring_connected = false;
65 amiibo_detected = false;
66
67 // Set HW default configuration
68 vibration_enabled = true;
69 motion_enabled = true;
70 hidbus_enabled = false;
71 nfc_enabled = false;
72 passive_enabled = false;
73 irs_enabled = false;
74 gyro_sensitivity = Joycon::GyroSensitivity::DPS2000;
75 gyro_performance = Joycon::GyroPerformance::HZ833;
76 accelerometer_sensitivity = Joycon::AccelerometerSensitivity::G8;
77 accelerometer_performance = Joycon::AccelerometerPerformance::HZ100;
78
79 // Initialize HW Protocols
80 calibration_protocol = std::make_unique<CalibrationProtocol>(hidapi_handle);
81 generic_protocol = std::make_unique<GenericProtocol>(hidapi_handle);
82 irs_protocol = std::make_unique<IrsProtocol>(hidapi_handle);
83 nfc_protocol = std::make_unique<NfcProtocol>(hidapi_handle);
84 ring_protocol = std::make_unique<RingConProtocol>(hidapi_handle);
85 rumble_protocol = std::make_unique<RumbleProtocol>(hidapi_handle);
86
87 // Get fixed joycon info
88 generic_protocol->GetVersionNumber(version);
89 generic_protocol->SetLowPowerMode(false);
90 generic_protocol->GetColor(color);
91 if (handle_device_type == ControllerType::Pro) {
92 // Some 3rd party controllers aren't pro controllers
93 generic_protocol->GetControllerType(device_type);
94 } else {
95 device_type = handle_device_type;
96 }
97 generic_protocol->GetSerialNumber(serial_number);
98 supported_features = GetSupportedFeatures();
99
100 // Get Calibration data
101 calibration_protocol->GetLeftJoyStickCalibration(left_stick_calibration);
102 calibration_protocol->GetRightJoyStickCalibration(right_stick_calibration);
103 calibration_protocol->GetImuCalibration(motion_calibration);
104
105 // Set led status
106 generic_protocol->SetLedBlinkPattern(static_cast<u8>(1 + port));
107
108 // Apply HW configuration
109 SetPollingMode();
110
111 // Initialize joycon poller
112 joycon_poller = std::make_unique<JoyconPoller>(device_type, left_stick_calibration,
113 right_stick_calibration, motion_calibration);
114
115 // Start pooling for data
116 is_connected = true;
117 if (!input_thread_running) {
118 input_thread =
119 std::jthread([this](std::stop_token stop_token) { InputThread(stop_token); });
120 }
121
122 disable_input_thread = false;
123 return DriverResult::Success;
124}
125
126void JoyconDriver::InputThread(std::stop_token stop_token) {
127 LOG_INFO(Input, "Joycon Adapter input thread started");
128 Common::SetCurrentThreadName("JoyconInput");
129 input_thread_running = true;
130
131 // Max update rate is 5ms, ensure we are always able to read a bit faster
132 constexpr int ThreadDelay = 2;
133 std::vector<u8> buffer(MaxBufferSize);
134
135 while (!stop_token.stop_requested()) {
136 int status = 0;
137
138 if (!IsInputThreadValid()) {
139 input_thread.request_stop();
140 continue;
141 }
142
143 // By disabling the input thread we can ensure custom commands will succeed as no package is
144 // skipped
145 if (!disable_input_thread) {
146 status = SDL_hid_read_timeout(hidapi_handle->handle, buffer.data(), buffer.size(),
147 ThreadDelay);
148 } else {
149 std::this_thread::sleep_for(std::chrono::milliseconds(ThreadDelay));
150 }
151
152 if (IsPayloadCorrect(status, buffer)) {
153 OnNewData(buffer);
154 }
155
156 std::this_thread::yield();
157 }
158
159 is_connected = false;
160 input_thread_running = false;
161 LOG_INFO(Input, "Joycon Adapter input thread stopped");
162}
163
164void JoyconDriver::OnNewData(std::span<u8> buffer) {
165 const auto report_mode = static_cast<ReportMode>(buffer[0]);
166
167 // Packages can be a litte bit inconsistent. Average the delta time to provide a smoother motion
168 // experience
169 switch (report_mode) {
170 case ReportMode::STANDARD_FULL_60HZ:
171 case ReportMode::NFC_IR_MODE_60HZ:
172 case ReportMode::SIMPLE_HID_MODE: {
173 const auto now = std::chrono::steady_clock::now();
174 const auto new_delta_time = static_cast<u64>(
175 std::chrono::duration_cast<std::chrono::microseconds>(now - last_update).count());
176 delta_time = ((delta_time * 8) + (new_delta_time * 2)) / 10;
177 last_update = now;
178 joycon_poller->UpdateColor(color);
179 break;
180 }
181 default:
182 break;
183 }
184
185 const MotionStatus motion_status{
186 .is_enabled = motion_enabled,
187 .delta_time = delta_time,
188 .gyro_sensitivity = gyro_sensitivity,
189 .accelerometer_sensitivity = accelerometer_sensitivity,
190 };
191
192 // TODO: Remove this when calibration is properly loaded and not calculated
193 if (ring_connected && report_mode == ReportMode::STANDARD_FULL_60HZ) {
194 InputReportActive data{};
195 memcpy(&data, buffer.data(), sizeof(InputReportActive));
196 calibration_protocol->GetRingCalibration(ring_calibration, data.ring_input);
197 }
198
199 const RingStatus ring_status{
200 .is_enabled = ring_connected,
201 .default_value = ring_calibration.default_value,
202 .max_value = ring_calibration.max_value,
203 .min_value = ring_calibration.min_value,
204 };
205
206 if (irs_protocol->IsEnabled()) {
207 irs_protocol->RequestImage(buffer);
208 joycon_poller->UpdateCamera(irs_protocol->GetImage(), irs_protocol->GetIrsFormat());
209 }
210
211 if (nfc_protocol->IsEnabled()) {
212 if (amiibo_detected) {
213 if (!nfc_protocol->HasAmiibo()) {
214 joycon_poller->UpdateAmiibo({});
215 amiibo_detected = false;
216 return;
217 }
218 }
219
220 if (!amiibo_detected) {
221 std::vector<u8> data(0x21C);
222 const auto result = nfc_protocol->ScanAmiibo(data);
223 if (result == DriverResult::Success) {
224 joycon_poller->UpdateAmiibo(data);
225 amiibo_detected = true;
226 }
227 }
228 }
229
230 switch (report_mode) {
231 case ReportMode::STANDARD_FULL_60HZ:
232 joycon_poller->ReadActiveMode(buffer, motion_status, ring_status);
233 break;
234 case ReportMode::NFC_IR_MODE_60HZ:
235 joycon_poller->ReadNfcIRMode(buffer, motion_status);
236 break;
237 case ReportMode::SIMPLE_HID_MODE:
238 joycon_poller->ReadPassiveMode(buffer);
239 break;
240 case ReportMode::SUBCMD_REPLY:
241 LOG_DEBUG(Input, "Unhandled command reply");
242 break;
243 default:
244 LOG_ERROR(Input, "Report mode not Implemented {}", report_mode);
245 break;
246 }
247}
248
249DriverResult JoyconDriver::SetPollingMode() {
250 disable_input_thread = true;
251
252 rumble_protocol->EnableRumble(vibration_enabled && supported_features.vibration);
253
254 if (motion_enabled && supported_features.motion) {
255 generic_protocol->EnableImu(true);
256 generic_protocol->SetImuConfig(gyro_sensitivity, gyro_performance,
257 accelerometer_sensitivity, accelerometer_performance);
258 } else {
259 generic_protocol->EnableImu(false);
260 }
261
262 if (irs_protocol->IsEnabled()) {
263 irs_protocol->DisableIrs();
264 }
265
266 if (nfc_protocol->IsEnabled()) {
267 amiibo_detected = false;
268 nfc_protocol->DisableNfc();
269 }
270
271 if (ring_protocol->IsEnabled()) {
272 ring_connected = false;
273 ring_protocol->DisableRingCon();
274 }
275
276 if (irs_enabled && supported_features.irs) {
277 auto result = irs_protocol->EnableIrs();
278 if (result == DriverResult::Success) {
279 disable_input_thread = false;
280 return result;
281 }
282 irs_protocol->DisableIrs();
283 LOG_ERROR(Input, "Error enabling IRS");
284 }
285
286 if (nfc_enabled && supported_features.nfc) {
287 auto result = nfc_protocol->EnableNfc();
288 if (result == DriverResult::Success) {
289 result = nfc_protocol->StartNFCPollingMode();
290 }
291 if (result == DriverResult::Success) {
292 disable_input_thread = false;
293 return result;
294 }
295 nfc_protocol->DisableNfc();
296 LOG_ERROR(Input, "Error enabling NFC");
297 }
298
299 if (hidbus_enabled && supported_features.hidbus) {
300 auto result = ring_protocol->EnableRingCon();
301 if (result == DriverResult::Success) {
302 result = ring_protocol->StartRingconPolling();
303 }
304 if (result == DriverResult::Success) {
305 ring_connected = true;
306 disable_input_thread = false;
307 return result;
308 }
309 ring_connected = false;
310 ring_protocol->DisableRingCon();
311 LOG_ERROR(Input, "Error enabling Ringcon");
312 }
313
314 if (passive_enabled && supported_features.passive) {
315 const auto result = generic_protocol->EnablePassiveMode();
316 if (result == DriverResult::Success) {
317 disable_input_thread = false;
318 return result;
319 }
320 LOG_ERROR(Input, "Error enabling passive mode");
321 }
322
323 // Default Mode
324 const auto result = generic_protocol->EnableActiveMode();
325 if (result != DriverResult::Success) {
326 LOG_ERROR(Input, "Error enabling active mode");
327 }
328 // Switch calls this function after enabling active mode
329 generic_protocol->TriggersElapsed();
330
331 disable_input_thread = false;
332 return result;
333}
334
335JoyconDriver::SupportedFeatures JoyconDriver::GetSupportedFeatures() {
336 SupportedFeatures features{
337 .passive = true,
338 .motion = true,
339 .vibration = true,
340 };
341
342 if (device_type == ControllerType::Right) {
343 features.nfc = true;
344 features.irs = true;
345 features.hidbus = true;
346 }
347
348 if (device_type == ControllerType::Pro) {
349 features.nfc = true;
350 }
351 return features;
352}
353
354bool JoyconDriver::IsInputThreadValid() const {
355 if (!is_connected.load()) {
356 return false;
357 }
358 if (hidapi_handle->handle == nullptr) {
359 return false;
360 }
361 // Controller is not responding. Terminate connection
362 if (error_counter > MaxErrorCount) {
363 return false;
364 }
365 return true;
366}
367
368bool JoyconDriver::IsPayloadCorrect(int status, std::span<const u8> buffer) {
369 if (status <= -1) {
370 error_counter++;
371 return false;
372 }
373 // There's no new data
374 if (status == 0) {
375 return false;
376 }
377 // No reply ever starts with zero
378 if (buffer[0] == 0x00) {
379 error_counter++;
380 return false;
381 }
382 error_counter = 0;
383 return true;
384}
385
386DriverResult JoyconDriver::SetVibration(const VibrationValue& vibration) {
387 std::scoped_lock lock{mutex};
388 if (disable_input_thread) {
389 return DriverResult::HandleInUse;
390 }
391 return rumble_protocol->SendVibration(vibration);
392}
393
394DriverResult JoyconDriver::SetLedConfig(u8 led_pattern) {
395 std::scoped_lock lock{mutex};
396 if (disable_input_thread) {
397 return DriverResult::HandleInUse;
398 }
399 return generic_protocol->SetLedPattern(led_pattern);
400}
401
402DriverResult JoyconDriver::SetIrsConfig(IrsMode mode_, IrsResolution format_) {
403 std::scoped_lock lock{mutex};
404 if (disable_input_thread) {
405 return DriverResult::HandleInUse;
406 }
407 disable_input_thread = true;
408 const auto result = irs_protocol->SetIrsConfig(mode_, format_);
409 disable_input_thread = false;
410 return result;
411}
412
413DriverResult JoyconDriver::SetPasiveMode() {
414 std::scoped_lock lock{mutex};
415 motion_enabled = false;
416 hidbus_enabled = false;
417 nfc_enabled = false;
418 passive_enabled = true;
419 irs_enabled = false;
420 return SetPollingMode();
421}
422
423DriverResult JoyconDriver::SetActiveMode() {
424 if (is_ring_disabled_by_irs) {
425 is_ring_disabled_by_irs = false;
426 SetActiveMode();
427 return SetRingConMode();
428 }
429
430 std::scoped_lock lock{mutex};
431 motion_enabled = true;
432 hidbus_enabled = false;
433 nfc_enabled = false;
434 passive_enabled = false;
435 irs_enabled = false;
436 return SetPollingMode();
437}
438
439DriverResult JoyconDriver::SetIrMode() {
440 std::scoped_lock lock{mutex};
441
442 if (!supported_features.irs) {
443 return DriverResult::NotSupported;
444 }
445
446 if (ring_connected) {
447 is_ring_disabled_by_irs = true;
448 }
449
450 motion_enabled = false;
451 hidbus_enabled = false;
452 nfc_enabled = false;
453 passive_enabled = false;
454 irs_enabled = true;
455 return SetPollingMode();
456}
457
458DriverResult JoyconDriver::SetNfcMode() {
459 std::scoped_lock lock{mutex};
460
461 if (!supported_features.nfc) {
462 return DriverResult::NotSupported;
463 }
464
465 motion_enabled = true;
466 hidbus_enabled = false;
467 nfc_enabled = true;
468 passive_enabled = false;
469 irs_enabled = false;
470 return SetPollingMode();
471}
472
473DriverResult JoyconDriver::SetRingConMode() {
474 std::scoped_lock lock{mutex};
475
476 if (!supported_features.hidbus) {
477 return DriverResult::NotSupported;
478 }
479
480 motion_enabled = true;
481 hidbus_enabled = true;
482 nfc_enabled = false;
483 passive_enabled = false;
484 irs_enabled = false;
485
486 const auto result = SetPollingMode();
487
488 if (!ring_connected) {
489 return DriverResult::NoDeviceDetected;
490 }
491
492 return result;
493}
494
495bool JoyconDriver::IsConnected() const {
496 std::scoped_lock lock{mutex};
497 return is_connected.load();
498}
499
500bool JoyconDriver::IsVibrationEnabled() const {
501 std::scoped_lock lock{mutex};
502 return vibration_enabled;
503}
504
505FirmwareVersion JoyconDriver::GetDeviceVersion() const {
506 std::scoped_lock lock{mutex};
507 return version;
508}
509
510Color JoyconDriver::GetDeviceColor() const {
511 std::scoped_lock lock{mutex};
512 return color;
513}
514
515std::size_t JoyconDriver::GetDevicePort() const {
516 std::scoped_lock lock{mutex};
517 return port;
518}
519
520ControllerType JoyconDriver::GetDeviceType() const {
521 std::scoped_lock lock{mutex};
522 return device_type;
523}
524
525ControllerType JoyconDriver::GetHandleDeviceType() const {
526 std::scoped_lock lock{mutex};
527 return handle_device_type;
528}
529
530SerialNumber JoyconDriver::GetSerialNumber() const {
531 std::scoped_lock lock{mutex};
532 return serial_number;
533}
534
535SerialNumber JoyconDriver::GetHandleSerialNumber() const {
536 std::scoped_lock lock{mutex};
537 return handle_serial_number;
538}
539
540void JoyconDriver::SetCallbacks(const JoyconCallbacks& callbacks) {
541 joycon_poller->SetCallbacks(callbacks);
542}
543
544DriverResult JoyconDriver::GetDeviceType(SDL_hid_device_info* device_info,
545 ControllerType& controller_type) {
546 static constexpr std::array<std::pair<u32, ControllerType>, 2> supported_devices{
547 std::pair<u32, ControllerType>{0x2006, ControllerType::Left},
548 {0x2007, ControllerType::Right},
549 };
550 constexpr u16 nintendo_vendor_id = 0x057e;
551
552 controller_type = ControllerType::None;
553 if (device_info->vendor_id != nintendo_vendor_id) {
554 return DriverResult::UnsupportedControllerType;
555 }
556
557 for (const auto& [product_id, type] : supported_devices) {
558 if (device_info->product_id == static_cast<u16>(product_id)) {
559 controller_type = type;
560 return Joycon::DriverResult::Success;
561 }
562 }
563 return Joycon::DriverResult::UnsupportedControllerType;
564}
565
566DriverResult JoyconDriver::GetSerialNumber(SDL_hid_device_info* device_info,
567 SerialNumber& serial_number) {
568 if (device_info->serial_number == nullptr) {
569 return DriverResult::Unknown;
570 }
571 std::memcpy(&serial_number, device_info->serial_number, 15);
572 return Joycon::DriverResult::Success;
573}
574
575} // namespace InputCommon::Joycon
diff --git a/src/input_common/helpers/joycon_driver.h b/src/input_common/helpers/joycon_driver.h
new file mode 100644
index 000000000..c1e189fa5
--- /dev/null
+++ b/src/input_common/helpers/joycon_driver.h
@@ -0,0 +1,150 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include <atomic>
7#include <functional>
8#include <mutex>
9#include <span>
10#include <thread>
11
12#include "input_common/helpers/joycon_protocol/joycon_types.h"
13
14namespace InputCommon::Joycon {
15class CalibrationProtocol;
16class GenericProtocol;
17class IrsProtocol;
18class NfcProtocol;
19class JoyconPoller;
20class RingConProtocol;
21class RumbleProtocol;
22
23class JoyconDriver final {
24public:
25 explicit JoyconDriver(std::size_t port_);
26
27 ~JoyconDriver();
28
29 DriverResult RequestDeviceAccess(SDL_hid_device_info* device_info);
30 DriverResult InitializeDevice();
31 void Stop();
32
33 bool IsConnected() const;
34 bool IsVibrationEnabled() const;
35
36 FirmwareVersion GetDeviceVersion() const;
37 Color GetDeviceColor() const;
38 std::size_t GetDevicePort() const;
39 ControllerType GetDeviceType() const;
40 ControllerType GetHandleDeviceType() const;
41 SerialNumber GetSerialNumber() const;
42 SerialNumber GetHandleSerialNumber() const;
43
44 DriverResult SetVibration(const VibrationValue& vibration);
45 DriverResult SetLedConfig(u8 led_pattern);
46 DriverResult SetIrsConfig(IrsMode mode_, IrsResolution format_);
47 DriverResult SetPasiveMode();
48 DriverResult SetActiveMode();
49 DriverResult SetIrMode();
50 DriverResult SetNfcMode();
51 DriverResult SetRingConMode();
52
53 void SetCallbacks(const JoyconCallbacks& callbacks);
54
55 // Returns device type from hidapi handle
56 static DriverResult GetDeviceType(SDL_hid_device_info* device_info,
57 ControllerType& controller_type);
58
59 // Returns serial number from hidapi handle
60 static DriverResult GetSerialNumber(SDL_hid_device_info* device_info,
61 SerialNumber& serial_number);
62
63private:
64 struct SupportedFeatures {
65 bool passive{};
66 bool hidbus{};
67 bool irs{};
68 bool motion{};
69 bool nfc{};
70 bool vibration{};
71 };
72
73 /// Main thread, actively request new data from the handle
74 void InputThread(std::stop_token stop_token);
75
76 /// Called everytime a valid package arrives
77 void OnNewData(std::span<u8> buffer);
78
79 /// Updates device configuration to enable or disable features
80 DriverResult SetPollingMode();
81
82 /// Returns true if input thread is valid and doesn't need to be stopped
83 bool IsInputThreadValid() const;
84
85 /// Returns true if the data should be interpreted. Otherwise the error counter is incremented
86 bool IsPayloadCorrect(int status, std::span<const u8> buffer);
87
88 /// Returns a list of supported features that can be enabled on this device
89 SupportedFeatures GetSupportedFeatures();
90
91 // Protocol Features
92 std::unique_ptr<CalibrationProtocol> calibration_protocol;
93 std::unique_ptr<GenericProtocol> generic_protocol;
94 std::unique_ptr<IrsProtocol> irs_protocol;
95 std::unique_ptr<NfcProtocol> nfc_protocol;
96 std::unique_ptr<JoyconPoller> joycon_poller;
97 std::unique_ptr<RingConProtocol> ring_protocol;
98 std::unique_ptr<RumbleProtocol> rumble_protocol;
99
100 // Connection status
101 std::atomic<bool> is_connected{};
102 u64 delta_time;
103 std::size_t error_counter{};
104 std::shared_ptr<JoyconHandle> hidapi_handle;
105 std::chrono::time_point<std::chrono::steady_clock> last_update;
106
107 // External device status
108 bool starlink_connected{};
109 bool ring_connected{};
110 bool amiibo_detected{};
111 bool is_ring_disabled_by_irs{};
112
113 // Harware configuration
114 u8 leds{};
115 ReportMode mode{};
116 bool passive_enabled{}; // Low power mode, Ideal for multiple controllers at the same time
117 bool hidbus_enabled{}; // External device support
118 bool irs_enabled{}; // Infrared camera input
119 bool motion_enabled{}; // Enables motion input
120 bool nfc_enabled{}; // Enables Amiibo detection
121 bool vibration_enabled{}; // Allows vibrations
122
123 // Calibration data
124 GyroSensitivity gyro_sensitivity{};
125 GyroPerformance gyro_performance{};
126 AccelerometerSensitivity accelerometer_sensitivity{};
127 AccelerometerPerformance accelerometer_performance{};
128 JoyStickCalibration left_stick_calibration{};
129 JoyStickCalibration right_stick_calibration{};
130 MotionCalibration motion_calibration{};
131 RingCalibration ring_calibration{};
132
133 // Fixed joycon info
134 FirmwareVersion version{};
135 Color color{};
136 std::size_t port{};
137 ControllerType device_type{}; // Device type reported by controller
138 ControllerType handle_device_type{}; // Device type reported by hidapi
139 SerialNumber serial_number{}; // Serial number reported by controller
140 SerialNumber handle_serial_number{}; // Serial number type reported by hidapi
141 SupportedFeatures supported_features{};
142
143 // Thread related
144 mutable std::mutex mutex;
145 std::jthread input_thread;
146 bool input_thread_running{};
147 bool disable_input_thread{};
148};
149
150} // namespace InputCommon::Joycon
diff --git a/src/input_common/helpers/joycon_protocol/calibration.cpp b/src/input_common/helpers/joycon_protocol/calibration.cpp
new file mode 100644
index 000000000..d8f040f75
--- /dev/null
+++ b/src/input_common/helpers/joycon_protocol/calibration.cpp
@@ -0,0 +1,218 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include <cstring>
5
6#include "input_common/helpers/joycon_protocol/calibration.h"
7#include "input_common/helpers/joycon_protocol/joycon_types.h"
8
9namespace InputCommon::Joycon {
10
11CalibrationProtocol::CalibrationProtocol(std::shared_ptr<JoyconHandle> handle)
12 : JoyconCommonProtocol(std::move(handle)) {}
13
14DriverResult CalibrationProtocol::GetLeftJoyStickCalibration(JoyStickCalibration& calibration) {
15 ScopedSetBlocking sb(this);
16 DriverResult result{DriverResult::Success};
17 JoystickLeftSpiCalibration spi_calibration{};
18 bool has_user_calibration = false;
19 calibration = {};
20
21 if (result == DriverResult::Success) {
22 result = HasUserCalibration(SpiAddress::USER_LEFT_MAGIC, has_user_calibration);
23 }
24
25 // Read User defined calibration
26 if (result == DriverResult::Success && has_user_calibration) {
27 result = ReadSPI(SpiAddress::USER_LEFT_DATA, spi_calibration);
28 }
29
30 // Read Factory calibration
31 if (result == DriverResult::Success && !has_user_calibration) {
32 result = ReadSPI(SpiAddress::FACT_LEFT_DATA, spi_calibration);
33 }
34
35 if (result == DriverResult::Success) {
36 calibration.x.center = GetXAxisCalibrationValue(spi_calibration.center);
37 calibration.y.center = GetYAxisCalibrationValue(spi_calibration.center);
38 calibration.x.min = GetXAxisCalibrationValue(spi_calibration.min);
39 calibration.y.min = GetYAxisCalibrationValue(spi_calibration.min);
40 calibration.x.max = GetXAxisCalibrationValue(spi_calibration.max);
41 calibration.y.max = GetYAxisCalibrationValue(spi_calibration.max);
42 }
43
44 // Set a valid default calibration if data is missing
45 ValidateCalibration(calibration);
46
47 return result;
48}
49
50DriverResult CalibrationProtocol::GetRightJoyStickCalibration(JoyStickCalibration& calibration) {
51 ScopedSetBlocking sb(this);
52 DriverResult result{DriverResult::Success};
53 JoystickRightSpiCalibration spi_calibration{};
54 bool has_user_calibration = false;
55 calibration = {};
56
57 if (result == DriverResult::Success) {
58 result = HasUserCalibration(SpiAddress::USER_RIGHT_MAGIC, has_user_calibration);
59 }
60
61 // Read User defined calibration
62 if (result == DriverResult::Success && has_user_calibration) {
63 result = ReadSPI(SpiAddress::USER_RIGHT_DATA, spi_calibration);
64 }
65
66 // Read Factory calibration
67 if (result == DriverResult::Success && !has_user_calibration) {
68 result = ReadSPI(SpiAddress::FACT_RIGHT_DATA, spi_calibration);
69 }
70
71 if (result == DriverResult::Success) {
72 calibration.x.center = GetXAxisCalibrationValue(spi_calibration.center);
73 calibration.y.center = GetYAxisCalibrationValue(spi_calibration.center);
74 calibration.x.min = GetXAxisCalibrationValue(spi_calibration.min);
75 calibration.y.min = GetYAxisCalibrationValue(spi_calibration.min);
76 calibration.x.max = GetXAxisCalibrationValue(spi_calibration.max);
77 calibration.y.max = GetYAxisCalibrationValue(spi_calibration.max);
78 }
79
80 // Set a valid default calibration if data is missing
81 ValidateCalibration(calibration);
82
83 return result;
84}
85
86DriverResult CalibrationProtocol::GetImuCalibration(MotionCalibration& calibration) {
87 ScopedSetBlocking sb(this);
88 DriverResult result{DriverResult::Success};
89 ImuSpiCalibration spi_calibration{};
90 bool has_user_calibration = false;
91 calibration = {};
92
93 if (result == DriverResult::Success) {
94 result = HasUserCalibration(SpiAddress::USER_IMU_MAGIC, has_user_calibration);
95 }
96
97 // Read User defined calibration
98 if (result == DriverResult::Success && has_user_calibration) {
99 result = ReadSPI(SpiAddress::USER_IMU_DATA, spi_calibration);
100 }
101
102 // Read Factory calibration
103 if (result == DriverResult::Success && !has_user_calibration) {
104 result = ReadSPI(SpiAddress::FACT_IMU_DATA, spi_calibration);
105 }
106
107 if (result == DriverResult::Success) {
108 calibration.accelerometer[0].offset = spi_calibration.accelerometer_offset[0];
109 calibration.accelerometer[1].offset = spi_calibration.accelerometer_offset[1];
110 calibration.accelerometer[2].offset = spi_calibration.accelerometer_offset[2];
111
112 calibration.accelerometer[0].scale = spi_calibration.accelerometer_scale[0];
113 calibration.accelerometer[1].scale = spi_calibration.accelerometer_scale[1];
114 calibration.accelerometer[2].scale = spi_calibration.accelerometer_scale[2];
115
116 calibration.gyro[0].offset = spi_calibration.gyroscope_offset[0];
117 calibration.gyro[1].offset = spi_calibration.gyroscope_offset[1];
118 calibration.gyro[2].offset = spi_calibration.gyroscope_offset[2];
119
120 calibration.gyro[0].scale = spi_calibration.gyroscope_scale[0];
121 calibration.gyro[1].scale = spi_calibration.gyroscope_scale[1];
122 calibration.gyro[2].scale = spi_calibration.gyroscope_scale[2];
123 }
124
125 ValidateCalibration(calibration);
126
127 return result;
128}
129
130DriverResult CalibrationProtocol::GetRingCalibration(RingCalibration& calibration,
131 s16 current_value) {
132 constexpr s16 DefaultRingRange{800};
133
134 // TODO: Get default calibration form ring itself
135 if (ring_data_max == 0 && ring_data_min == 0) {
136 ring_data_max = current_value + DefaultRingRange;
137 ring_data_min = current_value - DefaultRingRange;
138 ring_data_default = current_value;
139 }
140 ring_data_max = std::max(ring_data_max, current_value);
141 ring_data_min = std::min(ring_data_min, current_value);
142 calibration = {
143 .default_value = ring_data_default,
144 .max_value = ring_data_max,
145 .min_value = ring_data_min,
146 };
147 return DriverResult::Success;
148}
149
150DriverResult CalibrationProtocol::HasUserCalibration(SpiAddress address,
151 bool& has_user_calibration) {
152 MagicSpiCalibration spi_magic{};
153 const DriverResult result{ReadSPI(address, spi_magic)};
154 has_user_calibration = false;
155 if (result == DriverResult::Success) {
156 has_user_calibration = spi_magic.first == CalibrationMagic::USR_MAGIC_0 &&
157 spi_magic.second == CalibrationMagic::USR_MAGIC_1;
158 }
159 return result;
160}
161
162u16 CalibrationProtocol::GetXAxisCalibrationValue(std::span<u8> block) const {
163 return static_cast<u16>(((block[1] & 0x0F) << 8) | block[0]);
164}
165
166u16 CalibrationProtocol::GetYAxisCalibrationValue(std::span<u8> block) const {
167 return static_cast<u16>((block[2] << 4) | (block[1] >> 4));
168}
169
170void CalibrationProtocol::ValidateCalibration(JoyStickCalibration& calibration) {
171 constexpr u16 DefaultStickCenter{0x800};
172 constexpr u16 DefaultStickRange{0x6cc};
173
174 calibration.x.center = ValidateValue(calibration.x.center, DefaultStickCenter);
175 calibration.x.max = ValidateValue(calibration.x.max, DefaultStickRange);
176 calibration.x.min = ValidateValue(calibration.x.min, DefaultStickRange);
177
178 calibration.y.center = ValidateValue(calibration.y.center, DefaultStickCenter);
179 calibration.y.max = ValidateValue(calibration.y.max, DefaultStickRange);
180 calibration.y.min = ValidateValue(calibration.y.min, DefaultStickRange);
181}
182
183void CalibrationProtocol::ValidateCalibration(MotionCalibration& calibration) {
184 constexpr s16 DefaultAccelerometerScale{0x4000};
185 constexpr s16 DefaultGyroScale{0x3be7};
186 constexpr s16 DefaultOffset{0};
187
188 for (auto& sensor : calibration.accelerometer) {
189 sensor.scale = ValidateValue(sensor.scale, DefaultAccelerometerScale);
190 sensor.offset = ValidateValue(sensor.offset, DefaultOffset);
191 }
192 for (auto& sensor : calibration.gyro) {
193 sensor.scale = ValidateValue(sensor.scale, DefaultGyroScale);
194 sensor.offset = ValidateValue(sensor.offset, DefaultOffset);
195 }
196}
197
198u16 CalibrationProtocol::ValidateValue(u16 value, u16 default_value) const {
199 if (value == 0) {
200 return default_value;
201 }
202 if (value == 0xFFF) {
203 return default_value;
204 }
205 return value;
206}
207
208s16 CalibrationProtocol::ValidateValue(s16 value, s16 default_value) const {
209 if (value == 0) {
210 return default_value;
211 }
212 if (value == 0xFFF) {
213 return default_value;
214 }
215 return value;
216}
217
218} // namespace InputCommon::Joycon
diff --git a/src/input_common/helpers/joycon_protocol/calibration.h b/src/input_common/helpers/joycon_protocol/calibration.h
new file mode 100644
index 000000000..c6fd0f729
--- /dev/null
+++ b/src/input_common/helpers/joycon_protocol/calibration.h
@@ -0,0 +1,82 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4// Based on dkms-hid-nintendo implementation, CTCaer joycon toolkit and dekuNukem reverse
5// engineering https://github.com/nicman23/dkms-hid-nintendo/blob/master/src/hid-nintendo.c
6// https://github.com/CTCaer/jc_toolkit
7// https://github.com/dekuNukem/Nintendo_Switch_Reverse_Engineering
8
9#pragma once
10
11#include <vector>
12
13#include "input_common/helpers/joycon_protocol/common_protocol.h"
14
15namespace InputCommon::Joycon {
16enum class DriverResult;
17struct JoyStickCalibration;
18struct IMUCalibration;
19struct JoyconHandle;
20} // namespace InputCommon::Joycon
21
22namespace InputCommon::Joycon {
23
24/// Driver functions related to retrieving calibration data from the device
25class CalibrationProtocol final : private JoyconCommonProtocol {
26public:
27 explicit CalibrationProtocol(std::shared_ptr<JoyconHandle> handle);
28
29 /**
30 * Sends a request to obtain the left stick calibration from memory
31 * @param is_factory_calibration if true factory values will be returned
32 * @returns JoyStickCalibration of the left joystick
33 */
34 DriverResult GetLeftJoyStickCalibration(JoyStickCalibration& calibration);
35
36 /**
37 * Sends a request to obtain the right stick calibration from memory
38 * @param is_factory_calibration if true factory values will be returned
39 * @returns JoyStickCalibration of the right joystick
40 */
41 DriverResult GetRightJoyStickCalibration(JoyStickCalibration& calibration);
42
43 /**
44 * Sends a request to obtain the motion calibration from memory
45 * @returns ImuCalibration of the motion sensor
46 */
47 DriverResult GetImuCalibration(MotionCalibration& calibration);
48
49 /**
50 * Calculates on run time the proper calibration of the ring controller
51 * @returns RingCalibration of the ring sensor
52 */
53 DriverResult GetRingCalibration(RingCalibration& calibration, s16 current_value);
54
55private:
56 /// Returns true if the specified address corresponds to the magic value of user calibration
57 DriverResult HasUserCalibration(SpiAddress address, bool& has_user_calibration);
58
59 /// Converts a raw calibration block to an u16 value containing the x axis value
60 u16 GetXAxisCalibrationValue(std::span<u8> block) const;
61
62 /// Converts a raw calibration block to an u16 value containing the y axis value
63 u16 GetYAxisCalibrationValue(std::span<u8> block) const;
64
65 /// Ensures that all joystick calibration values are set
66 void ValidateCalibration(JoyStickCalibration& calibration);
67
68 /// Ensures that all motion calibration values are set
69 void ValidateCalibration(MotionCalibration& calibration);
70
71 /// Returns the default value if the value is either zero or 0xFFF
72 u16 ValidateValue(u16 value, u16 default_value) const;
73
74 /// Returns the default value if the value is either zero or 0xFFF
75 s16 ValidateValue(s16 value, s16 default_value) const;
76
77 s16 ring_data_max = 0;
78 s16 ring_data_default = 0;
79 s16 ring_data_min = 0;
80};
81
82} // namespace InputCommon::Joycon
diff --git a/src/input_common/helpers/joycon_protocol/common_protocol.cpp b/src/input_common/helpers/joycon_protocol/common_protocol.cpp
new file mode 100644
index 000000000..2b42a4555
--- /dev/null
+++ b/src/input_common/helpers/joycon_protocol/common_protocol.cpp
@@ -0,0 +1,316 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "common/logging/log.h"
5#include "input_common/helpers/joycon_protocol/common_protocol.h"
6
7namespace InputCommon::Joycon {
8JoyconCommonProtocol::JoyconCommonProtocol(std::shared_ptr<JoyconHandle> hidapi_handle_)
9 : hidapi_handle{std::move(hidapi_handle_)} {}
10
11u8 JoyconCommonProtocol::GetCounter() {
12 hidapi_handle->packet_counter = (hidapi_handle->packet_counter + 1) & 0x0F;
13 return hidapi_handle->packet_counter;
14}
15
16void JoyconCommonProtocol::SetBlocking() {
17 SDL_hid_set_nonblocking(hidapi_handle->handle, 0);
18}
19
20void JoyconCommonProtocol::SetNonBlocking() {
21 SDL_hid_set_nonblocking(hidapi_handle->handle, 1);
22}
23
24DriverResult JoyconCommonProtocol::GetDeviceType(ControllerType& controller_type) {
25 const auto result = ReadSPI(SpiAddress::DEVICE_TYPE, controller_type);
26
27 if (result == DriverResult::Success) {
28 // Fallback to 3rd party pro controllers
29 if (controller_type == ControllerType::None) {
30 controller_type = ControllerType::Pro;
31 }
32 }
33
34 return result;
35}
36
37DriverResult JoyconCommonProtocol::CheckDeviceAccess(SDL_hid_device_info* device_info) {
38 ControllerType controller_type{ControllerType::None};
39 const auto result = GetDeviceType(controller_type);
40
41 if (result != DriverResult::Success || controller_type == ControllerType::None) {
42 return DriverResult::UnsupportedControllerType;
43 }
44
45 hidapi_handle->handle =
46 SDL_hid_open(device_info->vendor_id, device_info->product_id, device_info->serial_number);
47
48 if (!hidapi_handle->handle) {
49 LOG_ERROR(Input, "Yuzu can't gain access to this device: ID {:04X}:{:04X}.",
50 device_info->vendor_id, device_info->product_id);
51 return DriverResult::HandleInUse;
52 }
53
54 SetNonBlocking();
55 return DriverResult::Success;
56}
57
58DriverResult JoyconCommonProtocol::SetReportMode(ReportMode report_mode) {
59 const std::array<u8, 1> buffer{static_cast<u8>(report_mode)};
60 return SendSubCommand(SubCommand::SET_REPORT_MODE, buffer);
61}
62
63DriverResult JoyconCommonProtocol::SendRawData(std::span<const u8> buffer) {
64 const auto result = SDL_hid_write(hidapi_handle->handle, buffer.data(), buffer.size());
65
66 if (result == -1) {
67 return DriverResult::ErrorWritingData;
68 }
69
70 return DriverResult::Success;
71}
72
73DriverResult JoyconCommonProtocol::GetSubCommandResponse(SubCommand sc,
74 SubCommandResponse& output) {
75 constexpr int timeout_mili = 66;
76 constexpr int MaxTries = 15;
77 int tries = 0;
78
79 do {
80 int result = SDL_hid_read_timeout(hidapi_handle->handle, reinterpret_cast<u8*>(&output),
81 sizeof(SubCommandResponse), timeout_mili);
82
83 if (result < 1) {
84 LOG_ERROR(Input, "No response from joycon");
85 }
86 if (tries++ > MaxTries) {
87 return DriverResult::Timeout;
88 }
89 } while (output.input_report.report_mode != ReportMode::SUBCMD_REPLY &&
90 output.sub_command != sc);
91
92 return DriverResult::Success;
93}
94
95DriverResult JoyconCommonProtocol::SendSubCommand(SubCommand sc, std::span<const u8> buffer,
96 SubCommandResponse& output) {
97 SubCommandPacket packet{
98 .output_report = OutputReport::RUMBLE_AND_SUBCMD,
99 .packet_counter = GetCounter(),
100 .sub_command = sc,
101 .command_data = {},
102 };
103
104 if (buffer.size() > packet.command_data.size()) {
105 return DriverResult::InvalidParameters;
106 }
107
108 memcpy(packet.command_data.data(), buffer.data(), buffer.size());
109
110 auto result = SendData(packet);
111
112 if (result != DriverResult::Success) {
113 return result;
114 }
115
116 result = GetSubCommandResponse(sc, output);
117
118 return DriverResult::Success;
119}
120
121DriverResult JoyconCommonProtocol::SendSubCommand(SubCommand sc, std::span<const u8> buffer) {
122 SubCommandResponse output{};
123 return SendSubCommand(sc, buffer, output);
124}
125
126DriverResult JoyconCommonProtocol::SendMCUCommand(SubCommand sc, std::span<const u8> buffer) {
127 SubCommandPacket packet{
128 .output_report = OutputReport::MCU_DATA,
129 .packet_counter = GetCounter(),
130 .sub_command = sc,
131 .command_data = {},
132 };
133
134 if (buffer.size() > packet.command_data.size()) {
135 return DriverResult::InvalidParameters;
136 }
137
138 memcpy(packet.command_data.data(), buffer.data(), buffer.size());
139
140 return SendData(packet);
141}
142
143DriverResult JoyconCommonProtocol::SendVibrationReport(std::span<const u8> buffer) {
144 VibrationPacket packet{
145 .output_report = OutputReport::RUMBLE_ONLY,
146 .packet_counter = GetCounter(),
147 .vibration_data = {},
148 };
149
150 if (buffer.size() > packet.vibration_data.size()) {
151 return DriverResult::InvalidParameters;
152 }
153
154 memcpy(packet.vibration_data.data(), buffer.data(), buffer.size());
155
156 return SendData(packet);
157}
158
159DriverResult JoyconCommonProtocol::ReadRawSPI(SpiAddress addr, std::span<u8> output) {
160 constexpr std::size_t HeaderSize = 5;
161 constexpr std::size_t MaxTries = 10;
162 std::size_t tries = 0;
163 SubCommandResponse response{};
164 std::array<u8, sizeof(ReadSpiPacket)> buffer{};
165 const ReadSpiPacket packet_data{
166 .spi_address = addr,
167 .size = static_cast<u8>(output.size()),
168 };
169
170 memcpy(buffer.data(), &packet_data, sizeof(ReadSpiPacket));
171 do {
172 const auto result = SendSubCommand(SubCommand::SPI_FLASH_READ, buffer, response);
173 if (result != DriverResult::Success) {
174 return result;
175 }
176
177 if (tries++ > MaxTries) {
178 return DriverResult::Timeout;
179 }
180 } while (response.spi_address != addr);
181
182 if (response.command_data.size() < packet_data.size + HeaderSize) {
183 return DriverResult::WrongReply;
184 }
185
186 // Remove header from output
187 memcpy(output.data(), response.command_data.data() + HeaderSize, packet_data.size);
188 return DriverResult::Success;
189}
190
191DriverResult JoyconCommonProtocol::EnableMCU(bool enable) {
192 const std::array<u8, 1> mcu_state{static_cast<u8>(enable ? 1 : 0)};
193 const auto result = SendSubCommand(SubCommand::SET_MCU_STATE, mcu_state);
194
195 if (result != DriverResult::Success) {
196 LOG_ERROR(Input, "Failed with error {}", result);
197 }
198
199 return result;
200}
201
202DriverResult JoyconCommonProtocol::ConfigureMCU(const MCUConfig& config) {
203 LOG_DEBUG(Input, "ConfigureMCU");
204 std::array<u8, sizeof(MCUConfig)> config_buffer;
205 memcpy(config_buffer.data(), &config, sizeof(MCUConfig));
206 config_buffer[37] = CalculateMCU_CRC8(config_buffer.data() + 1, 36);
207
208 const auto result = SendSubCommand(SubCommand::SET_MCU_CONFIG, config_buffer);
209
210 if (result != DriverResult::Success) {
211 LOG_ERROR(Input, "Failed with error {}", result);
212 }
213
214 return result;
215}
216
217DriverResult JoyconCommonProtocol::GetMCUDataResponse(ReportMode report_mode,
218 MCUCommandResponse& output) {
219 constexpr int TimeoutMili = 200;
220 constexpr int MaxTries = 9;
221 int tries = 0;
222
223 do {
224 int result = SDL_hid_read_timeout(hidapi_handle->handle, reinterpret_cast<u8*>(&output),
225 sizeof(MCUCommandResponse), TimeoutMili);
226
227 if (result < 1) {
228 LOG_ERROR(Input, "No response from joycon attempt {}", tries);
229 }
230 if (tries++ > MaxTries) {
231 return DriverResult::Timeout;
232 }
233 } while (output.input_report.report_mode != report_mode ||
234 output.mcu_report == MCUReport::EmptyAwaitingCmd);
235
236 return DriverResult::Success;
237}
238
239DriverResult JoyconCommonProtocol::SendMCUData(ReportMode report_mode, SubCommand sc,
240 std::span<const u8> buffer,
241 MCUCommandResponse& output) {
242 SubCommandPacket packet{
243 .output_report = OutputReport::MCU_DATA,
244 .packet_counter = GetCounter(),
245 .sub_command = sc,
246 .command_data = {},
247 };
248
249 if (buffer.size() > packet.command_data.size()) {
250 return DriverResult::InvalidParameters;
251 }
252
253 memcpy(packet.command_data.data(), buffer.data(), buffer.size());
254
255 auto result = SendData(packet);
256
257 if (result != DriverResult::Success) {
258 return result;
259 }
260
261 result = GetMCUDataResponse(report_mode, output);
262
263 return DriverResult::Success;
264}
265
266DriverResult JoyconCommonProtocol::WaitSetMCUMode(ReportMode report_mode, MCUMode mode) {
267 MCUCommandResponse output{};
268 constexpr std::size_t MaxTries{8};
269 std::size_t tries{};
270
271 do {
272 const std::vector<u8> mcu_data{static_cast<u8>(MCUMode::Standby)};
273 const auto result = SendMCUData(report_mode, SubCommand::STATE, mcu_data, output);
274
275 if (result != DriverResult::Success) {
276 return result;
277 }
278
279 if (tries++ > MaxTries) {
280 return DriverResult::WrongReply;
281 }
282 } while (output.mcu_report != MCUReport::StateReport ||
283 output.mcu_data[6] != static_cast<u8>(mode));
284
285 return DriverResult::Success;
286}
287
288// crc-8-ccitt / polynomial 0x07 look up table
289constexpr std::array<u8, 256> mcu_crc8_table = {
290 0x00, 0x07, 0x0E, 0x09, 0x1C, 0x1B, 0x12, 0x15, 0x38, 0x3F, 0x36, 0x31, 0x24, 0x23, 0x2A, 0x2D,
291 0x70, 0x77, 0x7E, 0x79, 0x6C, 0x6B, 0x62, 0x65, 0x48, 0x4F, 0x46, 0x41, 0x54, 0x53, 0x5A, 0x5D,
292 0xE0, 0xE7, 0xEE, 0xE9, 0xFC, 0xFB, 0xF2, 0xF5, 0xD8, 0xDF, 0xD6, 0xD1, 0xC4, 0xC3, 0xCA, 0xCD,
293 0x90, 0x97, 0x9E, 0x99, 0x8C, 0x8B, 0x82, 0x85, 0xA8, 0xAF, 0xA6, 0xA1, 0xB4, 0xB3, 0xBA, 0xBD,
294 0xC7, 0xC0, 0xC9, 0xCE, 0xDB, 0xDC, 0xD5, 0xD2, 0xFF, 0xF8, 0xF1, 0xF6, 0xE3, 0xE4, 0xED, 0xEA,
295 0xB7, 0xB0, 0xB9, 0xBE, 0xAB, 0xAC, 0xA5, 0xA2, 0x8F, 0x88, 0x81, 0x86, 0x93, 0x94, 0x9D, 0x9A,
296 0x27, 0x20, 0x29, 0x2E, 0x3B, 0x3C, 0x35, 0x32, 0x1F, 0x18, 0x11, 0x16, 0x03, 0x04, 0x0D, 0x0A,
297 0x57, 0x50, 0x59, 0x5E, 0x4B, 0x4C, 0x45, 0x42, 0x6F, 0x68, 0x61, 0x66, 0x73, 0x74, 0x7D, 0x7A,
298 0x89, 0x8E, 0x87, 0x80, 0x95, 0x92, 0x9B, 0x9C, 0xB1, 0xB6, 0xBF, 0xB8, 0xAD, 0xAA, 0xA3, 0xA4,
299 0xF9, 0xFE, 0xF7, 0xF0, 0xE5, 0xE2, 0xEB, 0xEC, 0xC1, 0xC6, 0xCF, 0xC8, 0xDD, 0xDA, 0xD3, 0xD4,
300 0x69, 0x6E, 0x67, 0x60, 0x75, 0x72, 0x7B, 0x7C, 0x51, 0x56, 0x5F, 0x58, 0x4D, 0x4A, 0x43, 0x44,
301 0x19, 0x1E, 0x17, 0x10, 0x05, 0x02, 0x0B, 0x0C, 0x21, 0x26, 0x2F, 0x28, 0x3D, 0x3A, 0x33, 0x34,
302 0x4E, 0x49, 0x40, 0x47, 0x52, 0x55, 0x5C, 0x5B, 0x76, 0x71, 0x78, 0x7F, 0x6A, 0x6D, 0x64, 0x63,
303 0x3E, 0x39, 0x30, 0x37, 0x22, 0x25, 0x2C, 0x2B, 0x06, 0x01, 0x08, 0x0F, 0x1A, 0x1D, 0x14, 0x13,
304 0xAE, 0xA9, 0xA0, 0xA7, 0xB2, 0xB5, 0xBC, 0xBB, 0x96, 0x91, 0x98, 0x9F, 0x8A, 0x8D, 0x84, 0x83,
305 0xDE, 0xD9, 0xD0, 0xD7, 0xC2, 0xC5, 0xCC, 0xCB, 0xE6, 0xE1, 0xE8, 0xEF, 0xFA, 0xFD, 0xF4, 0xF3};
306
307u8 JoyconCommonProtocol::CalculateMCU_CRC8(u8* buffer, u8 size) const {
308 u8 crc8 = 0x0;
309
310 for (int i = 0; i < size; ++i) {
311 crc8 = mcu_crc8_table[static_cast<u8>(crc8 ^ buffer[i])];
312 }
313 return crc8;
314}
315
316} // namespace InputCommon::Joycon
diff --git a/src/input_common/helpers/joycon_protocol/common_protocol.h b/src/input_common/helpers/joycon_protocol/common_protocol.h
new file mode 100644
index 000000000..f44f73ba4
--- /dev/null
+++ b/src/input_common/helpers/joycon_protocol/common_protocol.h
@@ -0,0 +1,201 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4// Based on dkms-hid-nintendo implementation, CTCaer joycon toolkit and dekuNukem reverse
5// engineering https://github.com/nicman23/dkms-hid-nintendo/blob/master/src/hid-nintendo.c
6// https://github.com/CTCaer/jc_toolkit
7// https://github.com/dekuNukem/Nintendo_Switch_Reverse_Engineering
8
9#pragma once
10
11#include <memory>
12#include <span>
13#include <vector>
14
15#include "common/common_types.h"
16#include "input_common/helpers/joycon_protocol/joycon_types.h"
17
18namespace InputCommon::Joycon {
19
20/// Joycon driver functions that handle low level communication
21class JoyconCommonProtocol {
22public:
23 explicit JoyconCommonProtocol(std::shared_ptr<JoyconHandle> hidapi_handle_);
24
25 /**
26 * Sets handle to blocking. In blocking mode, SDL_hid_read() will wait (block) until there is
27 * data to read before returning.
28 */
29 void SetBlocking();
30
31 /**
32 * Sets handle to non blocking. In non-blocking mode calls to SDL_hid_read() will return
33 * immediately with a value of 0 if there is no data to be read
34 */
35 void SetNonBlocking();
36
37 /**
38 * Sends a request to obtain the joycon type from device
39 * @returns controller type of the joycon
40 */
41 DriverResult GetDeviceType(ControllerType& controller_type);
42
43 /**
44 * Verifies and sets the joycon_handle if device is valid
45 * @param device info from the driver
46 * @returns success if the device is valid
47 */
48 DriverResult CheckDeviceAccess(SDL_hid_device_info* device);
49
50 /**
51 * Sends a request to set the polling mode of the joycon
52 * @param report_mode polling mode to be set
53 */
54 DriverResult SetReportMode(Joycon::ReportMode report_mode);
55
56 /**
57 * Sends data to the joycon device
58 * @param buffer data to be send
59 */
60 DriverResult SendRawData(std::span<const u8> buffer);
61
62 template <typename Output>
63 requires std::is_trivially_copyable_v<Output>
64 DriverResult SendData(const Output& output) {
65 std::array<u8, sizeof(Output)> buffer;
66 std::memcpy(buffer.data(), &output, sizeof(Output));
67 return SendRawData(buffer);
68 }
69
70 /**
71 * Waits for incoming data of the joycon device that matchs the subcommand
72 * @param sub_command type of data to be returned
73 * @returns a buffer containing the response
74 */
75 DriverResult GetSubCommandResponse(SubCommand sub_command, SubCommandResponse& output);
76
77 /**
78 * Sends a sub command to the device and waits for it's reply
79 * @param sc sub command to be send
80 * @param buffer data to be send
81 * @returns output buffer containing the response
82 */
83 DriverResult SendSubCommand(SubCommand sc, std::span<const u8> buffer,
84 SubCommandResponse& output);
85
86 /**
87 * Sends a sub command to the device and waits for it's reply and ignores the output
88 * @param sc sub command to be send
89 * @param buffer data to be send
90 */
91 DriverResult SendSubCommand(SubCommand sc, std::span<const u8> buffer);
92
93 /**
94 * Sends a mcu command to the device
95 * @param sc sub command to be send
96 * @param buffer data to be send
97 */
98 DriverResult SendMCUCommand(SubCommand sc, std::span<const u8> buffer);
99
100 /**
101 * Sends vibration data to the joycon
102 * @param buffer data to be send
103 */
104 DriverResult SendVibrationReport(std::span<const u8> buffer);
105
106 /**
107 * Reads the SPI memory stored on the joycon
108 * @param Initial address location
109 * @returns output buffer containing the response
110 */
111 DriverResult ReadRawSPI(SpiAddress addr, std::span<u8> output);
112
113 /**
114 * Reads the SPI memory stored on the joycon
115 * @param Initial address location
116 * @returns output object containing the response
117 */
118 template <typename Output>
119 requires std::is_trivially_copyable_v<Output>
120 DriverResult ReadSPI(SpiAddress addr, Output& output) {
121 std::array<u8, sizeof(Output)> buffer;
122 output = {};
123
124 const auto result = ReadRawSPI(addr, buffer);
125 if (result != DriverResult::Success) {
126 return result;
127 }
128
129 std::memcpy(&output, buffer.data(), sizeof(Output));
130 return DriverResult::Success;
131 }
132
133 /**
134 * Enables MCU chip on the joycon
135 * @param enable if true the chip will be enabled
136 */
137 DriverResult EnableMCU(bool enable);
138
139 /**
140 * Configures the MCU to the correspoinding mode
141 * @param MCUConfig configuration
142 */
143 DriverResult ConfigureMCU(const MCUConfig& config);
144
145 /**
146 * Waits until there's MCU data available. On timeout returns error
147 * @param report mode of the expected reply
148 * @returns a buffer containing the response
149 */
150 DriverResult GetMCUDataResponse(ReportMode report_mode_, MCUCommandResponse& output);
151
152 /**
153 * Sends data to the MCU chip and waits for it's reply
154 * @param report mode of the expected reply
155 * @param sub command to be send
156 * @param buffer data to be send
157 * @returns output buffer containing the response
158 */
159 DriverResult SendMCUData(ReportMode report_mode, SubCommand sc, std::span<const u8> buffer,
160 MCUCommandResponse& output);
161
162 /**
163 * Wait's until the MCU chip is on the specified mode
164 * @param report mode of the expected reply
165 * @param MCUMode configuration
166 */
167 DriverResult WaitSetMCUMode(ReportMode report_mode, MCUMode mode);
168
169 /**
170 * Calculates the checksum from the MCU data
171 * @param buffer containing the data to be send
172 * @param size of the buffer in bytes
173 * @returns byte with the correct checksum
174 */
175 u8 CalculateMCU_CRC8(u8* buffer, u8 size) const;
176
177private:
178 /**
179 * Increments and returns the packet counter of the handle
180 * @param joycon_handle device to send the data
181 * @returns packet counter value
182 */
183 u8 GetCounter();
184
185 std::shared_ptr<JoyconHandle> hidapi_handle;
186};
187
188class ScopedSetBlocking {
189public:
190 explicit ScopedSetBlocking(JoyconCommonProtocol* self) : m_self{self} {
191 m_self->SetBlocking();
192 }
193
194 ~ScopedSetBlocking() {
195 m_self->SetNonBlocking();
196 }
197
198private:
199 JoyconCommonProtocol* m_self{};
200};
201} // namespace InputCommon::Joycon
diff --git a/src/input_common/helpers/joycon_protocol/generic_functions.cpp b/src/input_common/helpers/joycon_protocol/generic_functions.cpp
new file mode 100644
index 000000000..548a4b9e3
--- /dev/null
+++ b/src/input_common/helpers/joycon_protocol/generic_functions.cpp
@@ -0,0 +1,136 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "common/logging/log.h"
5#include "input_common/helpers/joycon_protocol/generic_functions.h"
6
7namespace InputCommon::Joycon {
8
9GenericProtocol::GenericProtocol(std::shared_ptr<JoyconHandle> handle)
10 : JoyconCommonProtocol(std::move(handle)) {}
11
12DriverResult GenericProtocol::EnablePassiveMode() {
13 ScopedSetBlocking sb(this);
14 return SetReportMode(ReportMode::SIMPLE_HID_MODE);
15}
16
17DriverResult GenericProtocol::EnableActiveMode() {
18 ScopedSetBlocking sb(this);
19 return SetReportMode(ReportMode::STANDARD_FULL_60HZ);
20}
21
22DriverResult GenericProtocol::SetLowPowerMode(bool enable) {
23 ScopedSetBlocking sb(this);
24 const std::array<u8, 1> buffer{static_cast<u8>(enable ? 1 : 0)};
25 return SendSubCommand(SubCommand::LOW_POWER_MODE, buffer);
26}
27
28DriverResult GenericProtocol::TriggersElapsed() {
29 ScopedSetBlocking sb(this);
30 return SendSubCommand(SubCommand::TRIGGERS_ELAPSED, {});
31}
32
33DriverResult GenericProtocol::GetDeviceInfo(DeviceInfo& device_info) {
34 ScopedSetBlocking sb(this);
35 SubCommandResponse output{};
36
37 const auto result = SendSubCommand(SubCommand::REQ_DEV_INFO, {}, output);
38
39 device_info = {};
40 if (result == DriverResult::Success) {
41 device_info = output.device_info;
42 }
43
44 return result;
45}
46
47DriverResult GenericProtocol::GetControllerType(ControllerType& controller_type) {
48 return GetDeviceType(controller_type);
49}
50
51DriverResult GenericProtocol::EnableImu(bool enable) {
52 ScopedSetBlocking sb(this);
53 const std::array<u8, 1> buffer{static_cast<u8>(enable ? 1 : 0)};
54 return SendSubCommand(SubCommand::ENABLE_IMU, buffer);
55}
56
57DriverResult GenericProtocol::SetImuConfig(GyroSensitivity gsen, GyroPerformance gfrec,
58 AccelerometerSensitivity asen,
59 AccelerometerPerformance afrec) {
60 ScopedSetBlocking sb(this);
61 const std::array<u8, 4> buffer{static_cast<u8>(gsen), static_cast<u8>(asen),
62 static_cast<u8>(gfrec), static_cast<u8>(afrec)};
63 return SendSubCommand(SubCommand::SET_IMU_SENSITIVITY, buffer);
64}
65
66DriverResult GenericProtocol::GetBattery(u32& battery_level) {
67 // This function is meant to request the high resolution battery status
68 battery_level = 0;
69 return DriverResult::NotSupported;
70}
71
72DriverResult GenericProtocol::GetColor(Color& color) {
73 ScopedSetBlocking sb(this);
74 std::array<u8, 12> buffer{};
75 const auto result = ReadRawSPI(SpiAddress::COLOR_DATA, buffer);
76
77 color = {};
78 if (result == DriverResult::Success) {
79 color.body = static_cast<u32>((buffer[0] << 16) | (buffer[1] << 8) | buffer[2]);
80 color.buttons = static_cast<u32>((buffer[3] << 16) | (buffer[4] << 8) | buffer[5]);
81 color.left_grip = static_cast<u32>((buffer[6] << 16) | (buffer[7] << 8) | buffer[8]);
82 color.right_grip = static_cast<u32>((buffer[9] << 16) | (buffer[10] << 8) | buffer[11]);
83 }
84
85 return result;
86}
87
88DriverResult GenericProtocol::GetSerialNumber(SerialNumber& serial_number) {
89 ScopedSetBlocking sb(this);
90 std::array<u8, 16> buffer{};
91 const auto result = ReadRawSPI(SpiAddress::SERIAL_NUMBER, buffer);
92
93 serial_number = {};
94 if (result == DriverResult::Success) {
95 memcpy(serial_number.data(), buffer.data() + 1, sizeof(SerialNumber));
96 }
97
98 return result;
99}
100
101DriverResult GenericProtocol::GetTemperature(u32& temperature) {
102 // Not all devices have temperature sensor
103 temperature = 25;
104 return DriverResult::NotSupported;
105}
106
107DriverResult GenericProtocol::GetVersionNumber(FirmwareVersion& version) {
108 DeviceInfo device_info{};
109
110 const auto result = GetDeviceInfo(device_info);
111 version = device_info.firmware;
112
113 return result;
114}
115
116DriverResult GenericProtocol::SetHomeLight() {
117 ScopedSetBlocking sb(this);
118 static constexpr std::array<u8, 3> buffer{0x0f, 0xf0, 0x00};
119 return SendSubCommand(SubCommand::SET_HOME_LIGHT, buffer);
120}
121
122DriverResult GenericProtocol::SetLedBusy() {
123 return DriverResult::NotSupported;
124}
125
126DriverResult GenericProtocol::SetLedPattern(u8 leds) {
127 ScopedSetBlocking sb(this);
128 const std::array<u8, 1> buffer{leds};
129 return SendSubCommand(SubCommand::SET_PLAYER_LIGHTS, buffer);
130}
131
132DriverResult GenericProtocol::SetLedBlinkPattern(u8 leds) {
133 return SetLedPattern(static_cast<u8>(leds << 4));
134}
135
136} // namespace InputCommon::Joycon
diff --git a/src/input_common/helpers/joycon_protocol/generic_functions.h b/src/input_common/helpers/joycon_protocol/generic_functions.h
new file mode 100644
index 000000000..424831e81
--- /dev/null
+++ b/src/input_common/helpers/joycon_protocol/generic_functions.h
@@ -0,0 +1,114 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4// Based on dkms-hid-nintendo implementation, CTCaer joycon toolkit and dekuNukem reverse
5// engineering https://github.com/nicman23/dkms-hid-nintendo/blob/master/src/hid-nintendo.c
6// https://github.com/CTCaer/jc_toolkit
7// https://github.com/dekuNukem/Nintendo_Switch_Reverse_Engineering
8
9#pragma once
10
11#include "input_common/helpers/joycon_protocol/common_protocol.h"
12#include "input_common/helpers/joycon_protocol/joycon_types.h"
13
14namespace InputCommon::Joycon {
15
16/// Joycon driver functions that easily implemented
17class GenericProtocol final : private JoyconCommonProtocol {
18public:
19 explicit GenericProtocol(std::shared_ptr<JoyconHandle> handle);
20
21 /// Enables passive mode. This mode only sends button data on change. Sticks will return digital
22 /// data instead of analog. Motion will be disabled
23 DriverResult EnablePassiveMode();
24
25 /// Enables active mode. This mode will return the current status every 5-15ms
26 DriverResult EnableActiveMode();
27
28 /// Enables or disables the low power mode
29 DriverResult SetLowPowerMode(bool enable);
30
31 /// Unknown function used by the switch
32 DriverResult TriggersElapsed();
33
34 /**
35 * Sends a request to obtain the joycon firmware and mac from handle
36 * @returns controller device info
37 */
38 DriverResult GetDeviceInfo(DeviceInfo& controller_type);
39
40 /**
41 * Sends a request to obtain the joycon type from handle
42 * @returns controller type of the joycon
43 */
44 DriverResult GetControllerType(ControllerType& controller_type);
45
46 /**
47 * Enables motion input
48 * @param enable if true motion data will be enabled
49 */
50 DriverResult EnableImu(bool enable);
51
52 /**
53 * Configures the motion sensor with the specified parameters
54 * @param gsen gyroscope sensor sensitvity in degrees per second
55 * @param gfrec gyroscope sensor frequency in hertz
56 * @param asen accelerometer sensitivity in G force
57 * @param afrec accelerometer frequency in hertz
58 */
59 DriverResult SetImuConfig(GyroSensitivity gsen, GyroPerformance gfrec,
60 AccelerometerSensitivity asen, AccelerometerPerformance afrec);
61
62 /**
63 * Request battery level from the device
64 * @returns battery level
65 */
66 DriverResult GetBattery(u32& battery_level);
67
68 /**
69 * Request joycon colors from the device
70 * @returns colors of the body and buttons
71 */
72 DriverResult GetColor(Color& color);
73
74 /**
75 * Request joycon serial number from the device
76 * @returns 16 byte serial number
77 */
78 DriverResult GetSerialNumber(SerialNumber& serial_number);
79
80 /**
81 * Request joycon serial number from the device
82 * @returns 16 byte serial number
83 */
84 DriverResult GetTemperature(u32& temperature);
85
86 /**
87 * Request joycon serial number from the device
88 * @returns 16 byte serial number
89 */
90 DriverResult GetVersionNumber(FirmwareVersion& version);
91
92 /**
93 * Sets home led behaviour
94 */
95 DriverResult SetHomeLight();
96
97 /**
98 * Sets home led into a slow breathing state
99 */
100 DriverResult SetLedBusy();
101
102 /**
103 * Sets the 4 player leds on the joycon on a solid state
104 * @params bit flag containing the led state
105 */
106 DriverResult SetLedPattern(u8 leds);
107
108 /**
109 * Sets the 4 player leds on the joycon on a blinking state
110 * @returns bit flag containing the led state
111 */
112 DriverResult SetLedBlinkPattern(u8 leds);
113};
114} // namespace InputCommon::Joycon
diff --git a/src/input_common/helpers/joycon_protocol/irs.cpp b/src/input_common/helpers/joycon_protocol/irs.cpp
new file mode 100644
index 000000000..731fd5981
--- /dev/null
+++ b/src/input_common/helpers/joycon_protocol/irs.cpp
@@ -0,0 +1,299 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include <thread>
5#include "common/logging/log.h"
6#include "input_common/helpers/joycon_protocol/irs.h"
7
8namespace InputCommon::Joycon {
9
10IrsProtocol::IrsProtocol(std::shared_ptr<JoyconHandle> handle)
11 : JoyconCommonProtocol(std::move(handle)) {}
12
13DriverResult IrsProtocol::EnableIrs() {
14 LOG_INFO(Input, "Enable IRS");
15 ScopedSetBlocking sb(this);
16 DriverResult result{DriverResult::Success};
17
18 if (result == DriverResult::Success) {
19 result = SetReportMode(ReportMode::NFC_IR_MODE_60HZ);
20 }
21 if (result == DriverResult::Success) {
22 result = EnableMCU(true);
23 }
24 if (result == DriverResult::Success) {
25 result = WaitSetMCUMode(ReportMode::NFC_IR_MODE_60HZ, MCUMode::Standby);
26 }
27 if (result == DriverResult::Success) {
28 const MCUConfig config{
29 .command = MCUCommand::ConfigureMCU,
30 .sub_command = MCUSubCommand::SetMCUMode,
31 .mode = MCUMode::IR,
32 .crc = {},
33 };
34
35 result = ConfigureMCU(config);
36 }
37 if (result == DriverResult::Success) {
38 result = WaitSetMCUMode(ReportMode::NFC_IR_MODE_60HZ, MCUMode::IR);
39 }
40 if (result == DriverResult::Success) {
41 result = ConfigureIrs();
42 }
43 if (result == DriverResult::Success) {
44 result = WriteRegistersStep1();
45 }
46 if (result == DriverResult::Success) {
47 result = WriteRegistersStep2();
48 }
49
50 is_enabled = true;
51
52 return result;
53}
54
55DriverResult IrsProtocol::DisableIrs() {
56 LOG_DEBUG(Input, "Disable IRS");
57 ScopedSetBlocking sb(this);
58 DriverResult result{DriverResult::Success};
59
60 if (result == DriverResult::Success) {
61 result = EnableMCU(false);
62 }
63
64 is_enabled = false;
65
66 return result;
67}
68
69DriverResult IrsProtocol::SetIrsConfig(IrsMode mode, IrsResolution format) {
70 irs_mode = mode;
71 switch (format) {
72 case IrsResolution::Size320x240:
73 resolution_code = IrsResolutionCode::Size320x240;
74 fragments = IrsFragments::Size320x240;
75 resolution = IrsResolution::Size320x240;
76 break;
77 case IrsResolution::Size160x120:
78 resolution_code = IrsResolutionCode::Size160x120;
79 fragments = IrsFragments::Size160x120;
80 resolution = IrsResolution::Size160x120;
81 break;
82 case IrsResolution::Size80x60:
83 resolution_code = IrsResolutionCode::Size80x60;
84 fragments = IrsFragments::Size80x60;
85 resolution = IrsResolution::Size80x60;
86 break;
87 case IrsResolution::Size20x15:
88 resolution_code = IrsResolutionCode::Size20x15;
89 fragments = IrsFragments::Size20x15;
90 resolution = IrsResolution::Size20x15;
91 break;
92 case IrsResolution::Size40x30:
93 default:
94 resolution_code = IrsResolutionCode::Size40x30;
95 fragments = IrsFragments::Size40x30;
96 resolution = IrsResolution::Size40x30;
97 break;
98 }
99
100 // Restart feature
101 if (is_enabled) {
102 DisableIrs();
103 return EnableIrs();
104 }
105
106 return DriverResult::Success;
107}
108
109DriverResult IrsProtocol::RequestImage(std::span<u8> buffer) {
110 const u8 next_packet_fragment =
111 static_cast<u8>((packet_fragment + 1) % (static_cast<u8>(fragments) + 1));
112
113 if (buffer[0] == 0x31 && buffer[49] == 0x03) {
114 u8 new_packet_fragment = buffer[52];
115 if (new_packet_fragment == next_packet_fragment) {
116 packet_fragment = next_packet_fragment;
117 memcpy(buf_image.data() + (300 * packet_fragment), buffer.data() + 59, 300);
118
119 return RequestFrame(packet_fragment);
120 }
121
122 if (new_packet_fragment == packet_fragment) {
123 return RequestFrame(packet_fragment);
124 }
125
126 return ResendFrame(next_packet_fragment);
127 }
128
129 return RequestFrame(packet_fragment);
130}
131
132DriverResult IrsProtocol::ConfigureIrs() {
133 LOG_DEBUG(Input, "Configure IRS");
134 constexpr std::size_t max_tries = 28;
135 SubCommandResponse output{};
136 std::size_t tries = 0;
137
138 const IrsConfigure irs_configuration{
139 .command = MCUCommand::ConfigureIR,
140 .sub_command = MCUSubCommand::SetDeviceMode,
141 .irs_mode = IrsMode::ImageTransfer,
142 .number_of_fragments = fragments,
143 .mcu_major_version = 0x0500,
144 .mcu_minor_version = 0x1800,
145 .crc = {},
146 };
147 buf_image.resize((static_cast<u8>(fragments) + 1) * 300);
148
149 std::array<u8, sizeof(IrsConfigure)> request_data{};
150 memcpy(request_data.data(), &irs_configuration, sizeof(IrsConfigure));
151 request_data[37] = CalculateMCU_CRC8(request_data.data() + 1, 36);
152 do {
153 const auto result = SendSubCommand(SubCommand::SET_MCU_CONFIG, request_data, output);
154
155 if (result != DriverResult::Success) {
156 return result;
157 }
158 if (tries++ >= max_tries) {
159 return DriverResult::WrongReply;
160 }
161 } while (output.command_data[0] != 0x0b);
162
163 return DriverResult::Success;
164}
165
166DriverResult IrsProtocol::WriteRegistersStep1() {
167 LOG_DEBUG(Input, "WriteRegistersStep1");
168 DriverResult result{DriverResult::Success};
169 constexpr std::size_t max_tries = 28;
170 SubCommandResponse output{};
171 std::size_t tries = 0;
172
173 const IrsWriteRegisters irs_registers{
174 .command = MCUCommand::ConfigureIR,
175 .sub_command = MCUSubCommand::WriteDeviceRegisters,
176 .number_of_registers = 0x9,
177 .registers =
178 {
179 IrsRegister{IrRegistersAddress::Resolution, static_cast<u8>(resolution_code)},
180 {IrRegistersAddress::ExposureLSB, static_cast<u8>(exposure & 0xff)},
181 {IrRegistersAddress::ExposureMSB, static_cast<u8>(exposure >> 8)},
182 {IrRegistersAddress::ExposureTime, 0x00},
183 {IrRegistersAddress::Leds, static_cast<u8>(leds)},
184 {IrRegistersAddress::DigitalGainLSB, static_cast<u8>((digital_gain & 0x0f) << 4)},
185 {IrRegistersAddress::DigitalGainMSB, static_cast<u8>((digital_gain & 0xf0) >> 4)},
186 {IrRegistersAddress::LedFilter, static_cast<u8>(led_filter)},
187 {IrRegistersAddress::WhitePixelThreshold, 0xc8},
188 },
189 .crc = {},
190 };
191
192 std::array<u8, sizeof(IrsWriteRegisters)> request_data{};
193 memcpy(request_data.data(), &irs_registers, sizeof(IrsWriteRegisters));
194 request_data[37] = CalculateMCU_CRC8(request_data.data() + 1, 36);
195
196 std::array<u8, 38> mcu_request{0x02};
197 mcu_request[36] = CalculateMCU_CRC8(mcu_request.data(), 36);
198 mcu_request[37] = 0xFF;
199
200 if (result != DriverResult::Success) {
201 return result;
202 }
203
204 do {
205 result = SendSubCommand(SubCommand::SET_MCU_CONFIG, request_data, output);
206
207 // First time we need to set the report mode
208 if (result == DriverResult::Success && tries == 0) {
209 result = SendMCUCommand(SubCommand::SET_REPORT_MODE, mcu_request);
210 }
211 if (result == DriverResult::Success && tries == 0) {
212 GetSubCommandResponse(SubCommand::SET_MCU_CONFIG, output);
213 }
214
215 if (result != DriverResult::Success) {
216 return result;
217 }
218 if (tries++ >= max_tries) {
219 return DriverResult::WrongReply;
220 }
221 } while (!(output.command_data[0] == 0x13 && output.command_data[2] == 0x07) &&
222 output.command_data[0] != 0x23);
223
224 return DriverResult::Success;
225}
226
227DriverResult IrsProtocol::WriteRegistersStep2() {
228 LOG_DEBUG(Input, "WriteRegistersStep2");
229 constexpr std::size_t max_tries = 28;
230 SubCommandResponse output{};
231 std::size_t tries = 0;
232
233 const IrsWriteRegisters irs_registers{
234 .command = MCUCommand::ConfigureIR,
235 .sub_command = MCUSubCommand::WriteDeviceRegisters,
236 .number_of_registers = 0x8,
237 .registers =
238 {
239 IrsRegister{IrRegistersAddress::LedIntensitiyMSB,
240 static_cast<u8>(led_intensity >> 8)},
241 {IrRegistersAddress::LedIntensitiyLSB, static_cast<u8>(led_intensity & 0xff)},
242 {IrRegistersAddress::ImageFlip, static_cast<u8>(image_flip)},
243 {IrRegistersAddress::DenoiseSmoothing, static_cast<u8>((denoise >> 16) & 0xff)},
244 {IrRegistersAddress::DenoiseEdge, static_cast<u8>((denoise >> 8) & 0xff)},
245 {IrRegistersAddress::DenoiseColor, static_cast<u8>(denoise & 0xff)},
246 {IrRegistersAddress::UpdateTime, 0x2d},
247 {IrRegistersAddress::FinalizeConfig, 0x01},
248 },
249 .crc = {},
250 };
251
252 std::array<u8, sizeof(IrsWriteRegisters)> request_data{};
253 memcpy(request_data.data(), &irs_registers, sizeof(IrsWriteRegisters));
254 request_data[37] = CalculateMCU_CRC8(request_data.data() + 1, 36);
255 do {
256 const auto result = SendSubCommand(SubCommand::SET_MCU_CONFIG, request_data, output);
257
258 if (result != DriverResult::Success) {
259 return result;
260 }
261 if (tries++ >= max_tries) {
262 return DriverResult::WrongReply;
263 }
264 } while (output.command_data[0] != 0x13 && output.command_data[0] != 0x23);
265
266 return DriverResult::Success;
267}
268
269DriverResult IrsProtocol::RequestFrame(u8 frame) {
270 std::array<u8, 38> mcu_request{};
271 mcu_request[3] = frame;
272 mcu_request[36] = CalculateMCU_CRC8(mcu_request.data(), 36);
273 mcu_request[37] = 0xFF;
274 return SendMCUCommand(SubCommand::SET_REPORT_MODE, mcu_request);
275}
276
277DriverResult IrsProtocol::ResendFrame(u8 frame) {
278 std::array<u8, 38> mcu_request{};
279 mcu_request[1] = 0x1;
280 mcu_request[2] = frame;
281 mcu_request[3] = 0x0;
282 mcu_request[36] = CalculateMCU_CRC8(mcu_request.data(), 36);
283 mcu_request[37] = 0xFF;
284 return SendMCUCommand(SubCommand::SET_REPORT_MODE, mcu_request);
285}
286
287std::vector<u8> IrsProtocol::GetImage() const {
288 return buf_image;
289}
290
291IrsResolution IrsProtocol::GetIrsFormat() const {
292 return resolution;
293}
294
295bool IrsProtocol::IsEnabled() const {
296 return is_enabled;
297}
298
299} // namespace InputCommon::Joycon
diff --git a/src/input_common/helpers/joycon_protocol/irs.h b/src/input_common/helpers/joycon_protocol/irs.h
new file mode 100644
index 000000000..76dfa02ea
--- /dev/null
+++ b/src/input_common/helpers/joycon_protocol/irs.h
@@ -0,0 +1,63 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4// Based on dkms-hid-nintendo implementation, CTCaer joycon toolkit and dekuNukem reverse
5// engineering https://github.com/nicman23/dkms-hid-nintendo/blob/master/src/hid-nintendo.c
6// https://github.com/CTCaer/jc_toolkit
7// https://github.com/dekuNukem/Nintendo_Switch_Reverse_Engineering
8
9#pragma once
10
11#include <vector>
12
13#include "input_common/helpers/joycon_protocol/common_protocol.h"
14#include "input_common/helpers/joycon_protocol/joycon_types.h"
15
16namespace InputCommon::Joycon {
17
18class IrsProtocol final : private JoyconCommonProtocol {
19public:
20 explicit IrsProtocol(std::shared_ptr<JoyconHandle> handle);
21
22 DriverResult EnableIrs();
23
24 DriverResult DisableIrs();
25
26 DriverResult SetIrsConfig(IrsMode mode, IrsResolution format);
27
28 DriverResult RequestImage(std::span<u8> buffer);
29
30 std::vector<u8> GetImage() const;
31
32 IrsResolution GetIrsFormat() const;
33
34 bool IsEnabled() const;
35
36private:
37 DriverResult ConfigureIrs();
38
39 DriverResult WriteRegistersStep1();
40 DriverResult WriteRegistersStep2();
41
42 DriverResult RequestFrame(u8 frame);
43 DriverResult ResendFrame(u8 frame);
44
45 IrsMode irs_mode{IrsMode::ImageTransfer};
46 IrsResolution resolution{IrsResolution::Size40x30};
47 IrsResolutionCode resolution_code{IrsResolutionCode::Size40x30};
48 IrsFragments fragments{IrsFragments::Size40x30};
49 IrLeds leds{IrLeds::BrightAndDim};
50 IrExLedFilter led_filter{IrExLedFilter::Enabled};
51 IrImageFlip image_flip{IrImageFlip::Normal};
52 u8 digital_gain{0x01};
53 u16 exposure{0x2490};
54 u16 led_intensity{0x0f10};
55 u32 denoise{0x012344};
56
57 u8 packet_fragment{};
58 std::vector<u8> buf_image; // 8bpp greyscale image.
59
60 bool is_enabled{};
61};
62
63} // namespace InputCommon::Joycon
diff --git a/src/input_common/helpers/joycon_protocol/joycon_types.h b/src/input_common/helpers/joycon_protocol/joycon_types.h
new file mode 100644
index 000000000..b91934990
--- /dev/null
+++ b/src/input_common/helpers/joycon_protocol/joycon_types.h
@@ -0,0 +1,697 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4// Based on dkms-hid-nintendo implementation, CTCaer joycon toolkit and dekuNukem reverse
5// engineering https://github.com/nicman23/dkms-hid-nintendo/blob/master/src/hid-nintendo.c
6// https://github.com/CTCaer/jc_toolkit
7// https://github.com/dekuNukem/Nintendo_Switch_Reverse_Engineering
8
9#pragma once
10
11#include <array>
12#include <functional>
13#include <SDL_hidapi.h>
14
15#include "common/bit_field.h"
16#include "common/common_funcs.h"
17#include "common/common_types.h"
18
19namespace InputCommon::Joycon {
20constexpr u32 MaxErrorCount = 50;
21constexpr u32 MaxBufferSize = 368;
22constexpr std::array<u8, 8> DefaultVibrationBuffer{0x0, 0x1, 0x40, 0x40, 0x0, 0x1, 0x40, 0x40};
23
24using MacAddress = std::array<u8, 6>;
25using SerialNumber = std::array<u8, 15>;
26
27enum class ControllerType : u8 {
28 None = 0x00,
29 Left = 0x01,
30 Right = 0x02,
31 Pro = 0x03,
32 Dual = 0x05, // TODO: Verify this id
33 LarkHvc1 = 0x07,
34 LarkHvc2 = 0x08,
35 LarkNesLeft = 0x09,
36 LarkNesRight = 0x0A,
37 Lucia = 0x0B,
38 Lagon = 0x0C,
39 Lager = 0x0D,
40};
41
42enum class PadAxes {
43 LeftStickX,
44 LeftStickY,
45 RightStickX,
46 RightStickY,
47 Undefined,
48};
49
50enum class PadMotion {
51 LeftMotion,
52 RightMotion,
53 Undefined,
54};
55
56enum class PadButton : u32 {
57 Down = 0x000001,
58 Up = 0x000002,
59 Right = 0x000004,
60 Left = 0x000008,
61 LeftSR = 0x000010,
62 LeftSL = 0x000020,
63 L = 0x000040,
64 ZL = 0x000080,
65 Y = 0x000100,
66 X = 0x000200,
67 B = 0x000400,
68 A = 0x000800,
69 RightSR = 0x001000,
70 RightSL = 0x002000,
71 R = 0x004000,
72 ZR = 0x008000,
73 Minus = 0x010000,
74 Plus = 0x020000,
75 StickR = 0x040000,
76 StickL = 0x080000,
77 Home = 0x100000,
78 Capture = 0x200000,
79};
80
81enum class PasivePadButton : u32 {
82 Down_A = 0x0001,
83 Right_X = 0x0002,
84 Left_B = 0x0004,
85 Up_Y = 0x0008,
86 SL = 0x0010,
87 SR = 0x0020,
88 Minus = 0x0100,
89 Plus = 0x0200,
90 StickL = 0x0400,
91 StickR = 0x0800,
92 Home = 0x1000,
93 Capture = 0x2000,
94 L_R = 0x4000,
95 ZL_ZR = 0x8000,
96};
97
98enum class OutputReport : u8 {
99 RUMBLE_AND_SUBCMD = 0x01,
100 FW_UPDATE_PKT = 0x03,
101 RUMBLE_ONLY = 0x10,
102 MCU_DATA = 0x11,
103 USB_CMD = 0x80,
104};
105
106enum class FeatureReport : u8 {
107 Last_SUBCMD = 0x02,
108 OTA_GW_UPGRADE = 0x70,
109 SETUP_MEM_READ = 0x71,
110 MEM_READ = 0x72,
111 ERASE_MEM_SECTOR = 0x73,
112 MEM_WRITE = 0x74,
113 LAUNCH = 0x75,
114};
115
116enum class SubCommand : u8 {
117 STATE = 0x00,
118 MANUAL_BT_PAIRING = 0x01,
119 REQ_DEV_INFO = 0x02,
120 SET_REPORT_MODE = 0x03,
121 TRIGGERS_ELAPSED = 0x04,
122 GET_PAGE_LIST_STATE = 0x05,
123 SET_HCI_STATE = 0x06,
124 RESET_PAIRING_INFO = 0x07,
125 LOW_POWER_MODE = 0x08,
126 SPI_FLASH_READ = 0x10,
127 SPI_FLASH_WRITE = 0x11,
128 SPI_SECTOR_ERASE = 0x12,
129 RESET_MCU = 0x20,
130 SET_MCU_CONFIG = 0x21,
131 SET_MCU_STATE = 0x22,
132 SET_PLAYER_LIGHTS = 0x30,
133 GET_PLAYER_LIGHTS = 0x31,
134 SET_HOME_LIGHT = 0x38,
135 ENABLE_IMU = 0x40,
136 SET_IMU_SENSITIVITY = 0x41,
137 WRITE_IMU_REG = 0x42,
138 READ_IMU_REG = 0x43,
139 ENABLE_VIBRATION = 0x48,
140 GET_REGULATED_VOLTAGE = 0x50,
141 SET_EXTERNAL_CONFIG = 0x58,
142 GET_EXTERNAL_DEVICE_INFO = 0x59,
143 ENABLE_EXTERNAL_POLLING = 0x5A,
144 DISABLE_EXTERNAL_POLLING = 0x5B,
145 SET_EXTERNAL_FORMAT_CONFIG = 0x5C,
146};
147
148enum class UsbSubCommand : u8 {
149 CONN_STATUS = 0x01,
150 HADSHAKE = 0x02,
151 BAUDRATE_3M = 0x03,
152 NO_TIMEOUT = 0x04,
153 EN_TIMEOUT = 0x05,
154 RESET = 0x06,
155 PRE_HANDSHAKE = 0x91,
156 SEND_UART = 0x92,
157};
158
159enum class CalibrationMagic : u8 {
160 USR_MAGIC_0 = 0xB2,
161 USR_MAGIC_1 = 0xA1,
162};
163
164enum class SpiAddress : u16 {
165 MAGIC = 0x0000,
166 MAC_ADDRESS = 0x0015,
167 PAIRING_INFO = 0x2000,
168 SHIPMENT = 0x5000,
169 SERIAL_NUMBER = 0x6000,
170 DEVICE_TYPE = 0x6012,
171 FORMAT_VERSION = 0x601B,
172 FACT_IMU_DATA = 0x6020,
173 FACT_LEFT_DATA = 0x603d,
174 FACT_RIGHT_DATA = 0x6046,
175 COLOR_DATA = 0x6050,
176 DESIGN_VARIATION = 0x605C,
177 SENSOR_DATA = 0x6080,
178 USER_LEFT_MAGIC = 0x8010,
179 USER_LEFT_DATA = 0x8012,
180 USER_RIGHT_MAGIC = 0x801B,
181 USER_RIGHT_DATA = 0x801D,
182 USER_IMU_MAGIC = 0x8026,
183 USER_IMU_DATA = 0x8028,
184};
185
186enum class ReportMode : u8 {
187 ACTIVE_POLLING_NFC_IR_CAMERA_DATA = 0x00,
188 ACTIVE_POLLING_NFC_IR_CAMERA_CONFIGURATION = 0x01,
189 ACTIVE_POLLING_NFC_IR_CAMERA_DATA_CONFIGURATION = 0x02,
190 ACTIVE_POLLING_IR_CAMERA_DATA = 0x03,
191 SUBCMD_REPLY = 0x21,
192 MCU_UPDATE_STATE = 0x23,
193 STANDARD_FULL_60HZ = 0x30,
194 NFC_IR_MODE_60HZ = 0x31,
195 SIMPLE_HID_MODE = 0x3F,
196 INPUT_USB_RESPONSE = 0x81,
197};
198
199enum class GyroSensitivity : u8 {
200 DPS250,
201 DPS500,
202 DPS1000,
203 DPS2000, // Default
204};
205
206enum class AccelerometerSensitivity : u8 {
207 G8, // Default
208 G4,
209 G2,
210 G16,
211};
212
213enum class GyroPerformance : u8 {
214 HZ833,
215 HZ208, // Default
216};
217
218enum class AccelerometerPerformance : u8 {
219 HZ200,
220 HZ100, // Default
221};
222
223enum class MCUCommand : u8 {
224 ConfigureMCU = 0x21,
225 ConfigureIR = 0x23,
226};
227
228enum class MCUSubCommand : u8 {
229 SetMCUMode = 0x0,
230 SetDeviceMode = 0x1,
231 ReadDeviceMode = 0x02,
232 WriteDeviceRegisters = 0x4,
233};
234
235enum class MCUMode : u8 {
236 Suspend = 0,
237 Standby = 1,
238 Ringcon = 3,
239 NFC = 4,
240 IR = 5,
241 MaybeFWUpdate = 6,
242};
243
244enum class MCURequest : u8 {
245 GetMCUStatus = 1,
246 GetNFCData = 2,
247 GetIRData = 3,
248};
249
250enum class MCUReport : u8 {
251 Empty = 0x00,
252 StateReport = 0x01,
253 IRData = 0x03,
254 BusyInitializing = 0x0b,
255 IRStatus = 0x13,
256 IRRegisters = 0x1b,
257 NFCState = 0x2a,
258 NFCReadData = 0x3a,
259 EmptyAwaitingCmd = 0xff,
260};
261
262enum class MCUPacketFlag : u8 {
263 MorePacketsRemaining = 0x00,
264 LastCommandPacket = 0x08,
265};
266
267enum class NFCReadCommand : u8 {
268 CancelAll = 0x00,
269 StartPolling = 0x01,
270 StopPolling = 0x02,
271 StartWaitingRecieve = 0x04,
272 Ntag = 0x06,
273 Mifare = 0x0F,
274};
275
276enum class NFCTagType : u8 {
277 AllTags = 0x00,
278 Ntag215 = 0x01,
279};
280
281enum class NFCPages {
282 Block0 = 0,
283 Block45 = 45,
284 Block135 = 135,
285 Block231 = 231,
286};
287
288enum class NFCStatus : u8 {
289 LastPackage = 0x04,
290 TagLost = 0x07,
291};
292
293enum class IrsMode : u8 {
294 None = 0x02,
295 Moment = 0x03,
296 Dpd = 0x04,
297 Clustering = 0x06,
298 ImageTransfer = 0x07,
299 Silhouette = 0x08,
300 TeraImage = 0x09,
301 SilhouetteTeraImage = 0x0A,
302};
303
304enum class IrsResolution {
305 Size320x240,
306 Size160x120,
307 Size80x60,
308 Size40x30,
309 Size20x15,
310 None,
311};
312
313enum class IrsResolutionCode : u8 {
314 Size320x240 = 0x00, // Full pixel array
315 Size160x120 = 0x50, // Sensor Binning [2 X 2]
316 Size80x60 = 0x64, // Sensor Binning [4 x 2] and Skipping [1 x 2]
317 Size40x30 = 0x69, // Sensor Binning [4 x 2] and Skipping [2 x 4]
318 Size20x15 = 0x6A, // Sensor Binning [4 x 2] and Skipping [4 x 4]
319};
320
321// Size of image divided by 300
322enum class IrsFragments : u8 {
323 Size20x15 = 0x00,
324 Size40x30 = 0x03,
325 Size80x60 = 0x0f,
326 Size160x120 = 0x3f,
327 Size320x240 = 0xFF,
328};
329
330enum class IrLeds : u8 {
331 BrightAndDim = 0x00,
332 Bright = 0x20,
333 Dim = 0x10,
334 None = 0x30,
335};
336
337enum class IrExLedFilter : u8 {
338 Disabled = 0x00,
339 Enabled = 0x03,
340};
341
342enum class IrImageFlip : u8 {
343 Normal = 0x00,
344 Inverted = 0x02,
345};
346
347enum class IrRegistersAddress : u16 {
348 UpdateTime = 0x0400,
349 FinalizeConfig = 0x0700,
350 LedFilter = 0x0e00,
351 Leds = 0x1000,
352 LedIntensitiyMSB = 0x1100,
353 LedIntensitiyLSB = 0x1200,
354 ImageFlip = 0x2d00,
355 Resolution = 0x2e00,
356 DigitalGainLSB = 0x2e01,
357 DigitalGainMSB = 0x2f01,
358 ExposureLSB = 0x3001,
359 ExposureMSB = 0x3101,
360 ExposureTime = 0x3201,
361 WhitePixelThreshold = 0x4301,
362 DenoiseSmoothing = 0x6701,
363 DenoiseEdge = 0x6801,
364 DenoiseColor = 0x6901,
365};
366
367enum class ExternalDeviceId : u16 {
368 RingController = 0x2000,
369 Starlink = 0x2800,
370};
371
372enum class DriverResult {
373 Success,
374 WrongReply,
375 Timeout,
376 InvalidParameters,
377 UnsupportedControllerType,
378 HandleInUse,
379 ErrorReadingData,
380 ErrorWritingData,
381 NoDeviceDetected,
382 InvalidHandle,
383 NotSupported,
384 Disabled,
385 Unknown,
386};
387
388struct MotionSensorCalibration {
389 s16 offset;
390 s16 scale;
391};
392
393struct MotionCalibration {
394 std::array<MotionSensorCalibration, 3> accelerometer;
395 std::array<MotionSensorCalibration, 3> gyro;
396};
397
398// Basic motion data containing data from the sensors and a timestamp in microseconds
399struct MotionData {
400 float gyro_x{};
401 float gyro_y{};
402 float gyro_z{};
403 float accel_x{};
404 float accel_y{};
405 float accel_z{};
406 u64 delta_timestamp{};
407};
408
409// Output from SPI read command containing user calibration magic
410struct MagicSpiCalibration {
411 CalibrationMagic first;
412 CalibrationMagic second;
413};
414static_assert(sizeof(MagicSpiCalibration) == 0x2, "MagicSpiCalibration is an invalid size");
415
416// Output from SPI read command containing left joystick calibration
417struct JoystickLeftSpiCalibration {
418 std::array<u8, 3> max;
419 std::array<u8, 3> center;
420 std::array<u8, 3> min;
421};
422static_assert(sizeof(JoystickLeftSpiCalibration) == 0x9,
423 "JoystickLeftSpiCalibration is an invalid size");
424
425// Output from SPI read command containing right joystick calibration
426struct JoystickRightSpiCalibration {
427 std::array<u8, 3> center;
428 std::array<u8, 3> min;
429 std::array<u8, 3> max;
430};
431static_assert(sizeof(JoystickRightSpiCalibration) == 0x9,
432 "JoystickRightSpiCalibration is an invalid size");
433
434struct JoyStickAxisCalibration {
435 u16 max;
436 u16 min;
437 u16 center;
438};
439
440struct JoyStickCalibration {
441 JoyStickAxisCalibration x;
442 JoyStickAxisCalibration y;
443};
444
445struct ImuSpiCalibration {
446 std::array<s16, 3> accelerometer_offset;
447 std::array<s16, 3> accelerometer_scale;
448 std::array<s16, 3> gyroscope_offset;
449 std::array<s16, 3> gyroscope_scale;
450};
451static_assert(sizeof(ImuSpiCalibration) == 0x18, "ImuSpiCalibration is an invalid size");
452
453struct RingCalibration {
454 s16 default_value;
455 s16 max_value;
456 s16 min_value;
457};
458
459struct Color {
460 u32 body;
461 u32 buttons;
462 u32 left_grip;
463 u32 right_grip;
464};
465
466struct Battery {
467 union {
468 u8 raw{};
469
470 BitField<0, 4, u8> unknown;
471 BitField<4, 1, u8> charging;
472 BitField<5, 3, u8> status;
473 };
474};
475
476struct VibrationValue {
477 f32 low_amplitude;
478 f32 low_frequency;
479 f32 high_amplitude;
480 f32 high_frequency;
481};
482
483struct JoyconHandle {
484 SDL_hid_device* handle = nullptr;
485 u8 packet_counter{};
486};
487
488struct MCUConfig {
489 MCUCommand command;
490 MCUSubCommand sub_command;
491 MCUMode mode;
492 INSERT_PADDING_BYTES(0x22);
493 u8 crc;
494};
495static_assert(sizeof(MCUConfig) == 0x26, "MCUConfig is an invalid size");
496
497#pragma pack(push, 1)
498struct InputReportPassive {
499 ReportMode report_mode;
500 u16 button_input;
501 u8 stick_state;
502 std::array<u8, 10> unknown_data;
503};
504static_assert(sizeof(InputReportPassive) == 0xE, "InputReportPassive is an invalid size");
505
506struct InputReportActive {
507 ReportMode report_mode;
508 u8 packet_id;
509 Battery battery_status;
510 std::array<u8, 3> button_input;
511 std::array<u8, 3> left_stick_state;
512 std::array<u8, 3> right_stick_state;
513 u8 vibration_code;
514 std::array<s16, 6 * 2> motion_input;
515 INSERT_PADDING_BYTES(0x2);
516 s16 ring_input;
517};
518static_assert(sizeof(InputReportActive) == 0x29, "InputReportActive is an invalid size");
519
520struct InputReportNfcIr {
521 ReportMode report_mode;
522 u8 packet_id;
523 Battery battery_status;
524 std::array<u8, 3> button_input;
525 std::array<u8, 3> left_stick_state;
526 std::array<u8, 3> right_stick_state;
527 u8 vibration_code;
528 std::array<s16, 6 * 2> motion_input;
529 INSERT_PADDING_BYTES(0x4);
530};
531static_assert(sizeof(InputReportNfcIr) == 0x29, "InputReportNfcIr is an invalid size");
532#pragma pack(pop)
533
534struct NFCReadBlock {
535 u8 start;
536 u8 end;
537};
538static_assert(sizeof(NFCReadBlock) == 0x2, "NFCReadBlock is an invalid size");
539
540struct NFCReadBlockCommand {
541 u8 block_count{};
542 std::array<NFCReadBlock, 4> blocks{};
543};
544static_assert(sizeof(NFCReadBlockCommand) == 0x9, "NFCReadBlockCommand is an invalid size");
545
546struct NFCReadCommandData {
547 u8 unknown;
548 u8 uuid_length;
549 u8 unknown_2;
550 std::array<u8, 6> uid;
551 NFCTagType tag_type;
552 NFCReadBlockCommand read_block;
553};
554static_assert(sizeof(NFCReadCommandData) == 0x13, "NFCReadCommandData is an invalid size");
555
556struct NFCPollingCommandData {
557 u8 enable_mifare;
558 u8 unknown_1;
559 u8 unknown_2;
560 u8 unknown_3;
561 u8 unknown_4;
562};
563static_assert(sizeof(NFCPollingCommandData) == 0x05, "NFCPollingCommandData is an invalid size");
564
565struct NFCRequestState {
566 MCUSubCommand sub_command;
567 NFCReadCommand command_argument;
568 u8 packet_id;
569 INSERT_PADDING_BYTES(0x1);
570 MCUPacketFlag packet_flag;
571 u8 data_length;
572 union {
573 std::array<u8, 0x1F> raw_data;
574 NFCReadCommandData nfc_read;
575 NFCPollingCommandData nfc_polling;
576 };
577 u8 crc;
578};
579static_assert(sizeof(NFCRequestState) == 0x26, "NFCRequestState is an invalid size");
580
581struct IrsConfigure {
582 MCUCommand command;
583 MCUSubCommand sub_command;
584 IrsMode irs_mode;
585 IrsFragments number_of_fragments;
586 u16 mcu_major_version;
587 u16 mcu_minor_version;
588 INSERT_PADDING_BYTES(0x1D);
589 u8 crc;
590};
591static_assert(sizeof(IrsConfigure) == 0x26, "IrsConfigure is an invalid size");
592
593#pragma pack(push, 1)
594struct IrsRegister {
595 IrRegistersAddress address;
596 u8 value;
597};
598static_assert(sizeof(IrsRegister) == 0x3, "IrsRegister is an invalid size");
599
600struct IrsWriteRegisters {
601 MCUCommand command;
602 MCUSubCommand sub_command;
603 u8 number_of_registers;
604 std::array<IrsRegister, 9> registers;
605 INSERT_PADDING_BYTES(0x7);
606 u8 crc;
607};
608static_assert(sizeof(IrsWriteRegisters) == 0x26, "IrsWriteRegisters is an invalid size");
609#pragma pack(pop)
610
611struct FirmwareVersion {
612 u8 major;
613 u8 minor;
614};
615static_assert(sizeof(FirmwareVersion) == 0x2, "FirmwareVersion is an invalid size");
616
617struct DeviceInfo {
618 FirmwareVersion firmware;
619 std::array<u8, 2> unknown_1;
620 MacAddress mac_address;
621 std::array<u8, 2> unknown_2;
622};
623static_assert(sizeof(DeviceInfo) == 0xC, "DeviceInfo is an invalid size");
624
625struct MotionStatus {
626 bool is_enabled;
627 u64 delta_time;
628 GyroSensitivity gyro_sensitivity;
629 AccelerometerSensitivity accelerometer_sensitivity;
630};
631
632struct RingStatus {
633 bool is_enabled;
634 s16 default_value;
635 s16 max_value;
636 s16 min_value;
637};
638
639struct VibrationPacket {
640 OutputReport output_report;
641 u8 packet_counter;
642 std::array<u8, 0x8> vibration_data;
643};
644static_assert(sizeof(VibrationPacket) == 0xA, "VibrationPacket is an invalid size");
645
646struct SubCommandPacket {
647 OutputReport output_report;
648 u8 packet_counter;
649 INSERT_PADDING_BYTES(0x8); // This contains vibration data
650 SubCommand sub_command;
651 std::array<u8, 0x26> command_data;
652};
653static_assert(sizeof(SubCommandPacket) == 0x31, "SubCommandPacket is an invalid size");
654
655#pragma pack(push, 1)
656struct ReadSpiPacket {
657 SpiAddress spi_address;
658 INSERT_PADDING_BYTES(0x2);
659 u8 size;
660};
661static_assert(sizeof(ReadSpiPacket) == 0x5, "ReadSpiPacket is an invalid size");
662
663struct SubCommandResponse {
664 InputReportPassive input_report;
665 SubCommand sub_command;
666 union {
667 std::array<u8, 0x30> command_data;
668 SpiAddress spi_address; // Reply from SPI_FLASH_READ subcommand
669 ExternalDeviceId external_device_id; // Reply from GET_EXTERNAL_DEVICE_INFO subcommand
670 DeviceInfo device_info; // Reply from REQ_DEV_INFO subcommand
671 };
672 u8 crc; // This is never used
673};
674static_assert(sizeof(SubCommandResponse) == 0x40, "SubCommandResponse is an invalid size");
675#pragma pack(pop)
676
677struct MCUCommandResponse {
678 InputReportNfcIr input_report;
679 INSERT_PADDING_BYTES(0x8);
680 MCUReport mcu_report;
681 std::array<u8, 0x13D> mcu_data;
682 u8 crc;
683};
684static_assert(sizeof(MCUCommandResponse) == 0x170, "MCUCommandResponse is an invalid size");
685
686struct JoyconCallbacks {
687 std::function<void(Battery)> on_battery_data;
688 std::function<void(Color)> on_color_data;
689 std::function<void(int, bool)> on_button_data;
690 std::function<void(int, f32)> on_stick_data;
691 std::function<void(int, const MotionData&)> on_motion_data;
692 std::function<void(f32)> on_ring_data;
693 std::function<void(const std::vector<u8>&)> on_amiibo_data;
694 std::function<void(const std::vector<u8>&, IrsResolution)> on_camera_data;
695};
696
697} // namespace InputCommon::Joycon
diff --git a/src/input_common/helpers/joycon_protocol/nfc.cpp b/src/input_common/helpers/joycon_protocol/nfc.cpp
new file mode 100644
index 000000000..eeba82986
--- /dev/null
+++ b/src/input_common/helpers/joycon_protocol/nfc.cpp
@@ -0,0 +1,406 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include <thread>
5#include "common/logging/log.h"
6#include "input_common/helpers/joycon_protocol/nfc.h"
7
8namespace InputCommon::Joycon {
9
10NfcProtocol::NfcProtocol(std::shared_ptr<JoyconHandle> handle)
11 : JoyconCommonProtocol(std::move(handle)) {}
12
13DriverResult NfcProtocol::EnableNfc() {
14 LOG_INFO(Input, "Enable NFC");
15 ScopedSetBlocking sb(this);
16 DriverResult result{DriverResult::Success};
17
18 if (result == DriverResult::Success) {
19 result = SetReportMode(ReportMode::NFC_IR_MODE_60HZ);
20 }
21 if (result == DriverResult::Success) {
22 result = EnableMCU(true);
23 }
24 if (result == DriverResult::Success) {
25 result = WaitSetMCUMode(ReportMode::NFC_IR_MODE_60HZ, MCUMode::Standby);
26 }
27 if (result == DriverResult::Success) {
28 const MCUConfig config{
29 .command = MCUCommand::ConfigureMCU,
30 .sub_command = MCUSubCommand::SetMCUMode,
31 .mode = MCUMode::NFC,
32 .crc = {},
33 };
34
35 result = ConfigureMCU(config);
36 }
37
38 return result;
39}
40
41DriverResult NfcProtocol::DisableNfc() {
42 LOG_DEBUG(Input, "Disable NFC");
43 ScopedSetBlocking sb(this);
44 DriverResult result{DriverResult::Success};
45
46 if (result == DriverResult::Success) {
47 result = EnableMCU(false);
48 }
49
50 is_enabled = false;
51
52 return result;
53}
54
55DriverResult NfcProtocol::StartNFCPollingMode() {
56 LOG_DEBUG(Input, "Start NFC pooling Mode");
57 ScopedSetBlocking sb(this);
58 DriverResult result{DriverResult::Success};
59 TagFoundData tag_data{};
60
61 if (result == DriverResult::Success) {
62 result = WaitSetMCUMode(ReportMode::NFC_IR_MODE_60HZ, MCUMode::NFC);
63 }
64 if (result == DriverResult::Success) {
65 result = WaitUntilNfcIsReady();
66 }
67 if (result == DriverResult::Success) {
68 is_enabled = true;
69 }
70
71 return result;
72}
73
74DriverResult NfcProtocol::ScanAmiibo(std::vector<u8>& data) {
75 LOG_DEBUG(Input, "Start NFC pooling Mode");
76 ScopedSetBlocking sb(this);
77 DriverResult result{DriverResult::Success};
78 TagFoundData tag_data{};
79
80 if (result == DriverResult::Success) {
81 result = StartPolling(tag_data);
82 }
83 if (result == DriverResult::Success) {
84 result = ReadTag(tag_data);
85 }
86 if (result == DriverResult::Success) {
87 result = WaitUntilNfcIsReady();
88 }
89 if (result == DriverResult::Success) {
90 result = StartPolling(tag_data);
91 }
92 if (result == DriverResult::Success) {
93 result = GetAmiiboData(data);
94 }
95
96 return result;
97}
98
99bool NfcProtocol::HasAmiibo() {
100 ScopedSetBlocking sb(this);
101 DriverResult result{DriverResult::Success};
102 TagFoundData tag_data{};
103
104 if (result == DriverResult::Success) {
105 result = StartPolling(tag_data);
106 }
107
108 return result == DriverResult::Success;
109}
110
111DriverResult NfcProtocol::WaitUntilNfcIsReady() {
112 constexpr std::size_t timeout_limit = 10;
113 MCUCommandResponse output{};
114 std::size_t tries = 0;
115
116 do {
117 auto result = SendStartWaitingRecieveRequest(output);
118
119 if (result != DriverResult::Success) {
120 return result;
121 }
122 if (tries++ > timeout_limit) {
123 return DriverResult::Timeout;
124 }
125 } while (output.mcu_report != MCUReport::NFCState ||
126 (output.mcu_data[1] << 8) + output.mcu_data[0] != 0x0500 ||
127 output.mcu_data[5] != 0x31 || output.mcu_data[6] != 0x00);
128
129 return DriverResult::Success;
130}
131
132DriverResult NfcProtocol::StartPolling(TagFoundData& data) {
133 LOG_DEBUG(Input, "Start Polling for tag");
134 constexpr std::size_t timeout_limit = 7;
135 MCUCommandResponse output{};
136 std::size_t tries = 0;
137
138 do {
139 const auto result = SendStartPollingRequest(output);
140 if (result != DriverResult::Success) {
141 return result;
142 }
143 if (tries++ > timeout_limit) {
144 return DriverResult::Timeout;
145 }
146 } while (output.mcu_report != MCUReport::NFCState ||
147 (output.mcu_data[1] << 8) + output.mcu_data[0] != 0x0500 ||
148 output.mcu_data[6] != 0x09);
149
150 data.type = output.mcu_data[12];
151 data.uuid.resize(output.mcu_data[14]);
152 memcpy(data.uuid.data(), output.mcu_data.data() + 15, data.uuid.size());
153
154 return DriverResult::Success;
155}
156
157DriverResult NfcProtocol::ReadTag(const TagFoundData& data) {
158 constexpr std::size_t timeout_limit = 10;
159 MCUCommandResponse output{};
160 std::size_t tries = 0;
161
162 std::string uuid_string;
163 for (auto& content : data.uuid) {
164 uuid_string += fmt::format(" {:02x}", content);
165 }
166
167 LOG_INFO(Input, "Tag detected, type={}, uuid={}", data.type, uuid_string);
168
169 tries = 0;
170 NFCPages ntag_pages = NFCPages::Block0;
171 // Read Tag data
172 while (true) {
173 auto result = SendReadAmiiboRequest(output, ntag_pages);
174 const auto nfc_status = static_cast<NFCStatus>(output.mcu_data[6]);
175
176 if (result != DriverResult::Success) {
177 return result;
178 }
179
180 if ((output.mcu_report == MCUReport::NFCReadData ||
181 output.mcu_report == MCUReport::NFCState) &&
182 nfc_status == NFCStatus::TagLost) {
183 return DriverResult::ErrorReadingData;
184 }
185
186 if (output.mcu_report == MCUReport::NFCReadData && output.mcu_data[1] == 0x07 &&
187 output.mcu_data[2] == 0x01) {
188 if (data.type != 2) {
189 continue;
190 }
191 switch (output.mcu_data[24]) {
192 case 0:
193 ntag_pages = NFCPages::Block135;
194 break;
195 case 3:
196 ntag_pages = NFCPages::Block45;
197 break;
198 case 4:
199 ntag_pages = NFCPages::Block231;
200 break;
201 default:
202 return DriverResult::ErrorReadingData;
203 }
204 continue;
205 }
206
207 if (output.mcu_report == MCUReport::NFCState && nfc_status == NFCStatus::LastPackage) {
208 // finished
209 SendStopPollingRequest(output);
210 return DriverResult::Success;
211 }
212
213 // Ignore other state reports
214 if (output.mcu_report == MCUReport::NFCState) {
215 continue;
216 }
217
218 if (tries++ > timeout_limit) {
219 return DriverResult::Timeout;
220 }
221 }
222
223 return DriverResult::Success;
224}
225
226DriverResult NfcProtocol::GetAmiiboData(std::vector<u8>& ntag_data) {
227 constexpr std::size_t timeout_limit = 10;
228 MCUCommandResponse output{};
229 std::size_t tries = 0;
230
231 NFCPages ntag_pages = NFCPages::Block135;
232 std::size_t ntag_buffer_pos = 0;
233 // Read Tag data
234 while (true) {
235 auto result = SendReadAmiiboRequest(output, ntag_pages);
236 const auto nfc_status = static_cast<NFCStatus>(output.mcu_data[6]);
237
238 if (result != DriverResult::Success) {
239 return result;
240 }
241
242 if ((output.mcu_report == MCUReport::NFCReadData ||
243 output.mcu_report == MCUReport::NFCState) &&
244 nfc_status == NFCStatus::TagLost) {
245 return DriverResult::ErrorReadingData;
246 }
247
248 if (output.mcu_report == MCUReport::NFCReadData && output.mcu_data[1] == 0x07) {
249 std::size_t payload_size = (output.mcu_data[4] << 8 | output.mcu_data[5]) & 0x7FF;
250 if (output.mcu_data[2] == 0x01) {
251 memcpy(ntag_data.data() + ntag_buffer_pos, output.mcu_data.data() + 66,
252 payload_size - 60);
253 ntag_buffer_pos += payload_size - 60;
254 } else {
255 memcpy(ntag_data.data() + ntag_buffer_pos, output.mcu_data.data() + 6,
256 payload_size);
257 }
258 continue;
259 }
260
261 if (output.mcu_report == MCUReport::NFCState && nfc_status == NFCStatus::LastPackage) {
262 LOG_INFO(Input, "Finished reading amiibo");
263 return DriverResult::Success;
264 }
265
266 // Ignore other state reports
267 if (output.mcu_report == MCUReport::NFCState) {
268 continue;
269 }
270
271 if (tries++ > timeout_limit) {
272 return DriverResult::Timeout;
273 }
274 }
275
276 return DriverResult::Success;
277}
278
279DriverResult NfcProtocol::SendStartPollingRequest(MCUCommandResponse& output) {
280 NFCRequestState request{
281 .sub_command = MCUSubCommand::ReadDeviceMode,
282 .command_argument = NFCReadCommand::StartPolling,
283 .packet_id = 0x0,
284 .packet_flag = MCUPacketFlag::LastCommandPacket,
285 .data_length = sizeof(NFCPollingCommandData),
286 .nfc_polling =
287 {
288 .enable_mifare = 0x01,
289 .unknown_1 = 0x00,
290 .unknown_2 = 0x00,
291 .unknown_3 = 0x2c,
292 .unknown_4 = 0x01,
293 },
294 .crc = {},
295 };
296
297 std::array<u8, sizeof(NFCRequestState)> request_data{};
298 memcpy(request_data.data(), &request, sizeof(NFCRequestState));
299 request_data[37] = CalculateMCU_CRC8(request_data.data() + 1, 36);
300 return SendMCUData(ReportMode::NFC_IR_MODE_60HZ, SubCommand::STATE, request_data, output);
301}
302
303DriverResult NfcProtocol::SendStopPollingRequest(MCUCommandResponse& output) {
304 NFCRequestState request{
305 .sub_command = MCUSubCommand::ReadDeviceMode,
306 .command_argument = NFCReadCommand::StopPolling,
307 .packet_id = 0x0,
308 .packet_flag = MCUPacketFlag::LastCommandPacket,
309 .data_length = 0,
310 .raw_data = {},
311 .crc = {},
312 };
313
314 std::array<u8, sizeof(NFCRequestState)> request_data{};
315 memcpy(request_data.data(), &request, sizeof(NFCRequestState));
316 request_data[37] = CalculateMCU_CRC8(request_data.data() + 1, 36);
317 return SendMCUData(ReportMode::NFC_IR_MODE_60HZ, SubCommand::STATE, request_data, output);
318}
319
320DriverResult NfcProtocol::SendStartWaitingRecieveRequest(MCUCommandResponse& output) {
321 NFCRequestState request{
322 .sub_command = MCUSubCommand::ReadDeviceMode,
323 .command_argument = NFCReadCommand::StartWaitingRecieve,
324 .packet_id = 0x0,
325 .packet_flag = MCUPacketFlag::LastCommandPacket,
326 .data_length = 0,
327 .raw_data = {},
328 .crc = {},
329 };
330
331 std::vector<u8> request_data(sizeof(NFCRequestState));
332 memcpy(request_data.data(), &request, sizeof(NFCRequestState));
333 request_data[37] = CalculateMCU_CRC8(request_data.data() + 1, 36);
334 return SendMCUData(ReportMode::NFC_IR_MODE_60HZ, SubCommand::STATE, request_data, output);
335}
336
337DriverResult NfcProtocol::SendReadAmiiboRequest(MCUCommandResponse& output, NFCPages ntag_pages) {
338 NFCRequestState request{
339 .sub_command = MCUSubCommand::ReadDeviceMode,
340 .command_argument = NFCReadCommand::Ntag,
341 .packet_id = 0x0,
342 .packet_flag = MCUPacketFlag::LastCommandPacket,
343 .data_length = sizeof(NFCReadCommandData),
344 .nfc_read =
345 {
346 .unknown = 0xd0,
347 .uuid_length = 0x07,
348 .unknown_2 = 0x00,
349 .uid = {},
350 .tag_type = NFCTagType::AllTags,
351 .read_block = GetReadBlockCommand(ntag_pages),
352 },
353 .crc = {},
354 };
355
356 std::array<u8, sizeof(NFCRequestState)> request_data{};
357 memcpy(request_data.data(), &request, sizeof(NFCRequestState));
358 request_data[37] = CalculateMCU_CRC8(request_data.data() + 1, 36);
359 return SendMCUData(ReportMode::NFC_IR_MODE_60HZ, SubCommand::STATE, request_data, output);
360}
361
362NFCReadBlockCommand NfcProtocol::GetReadBlockCommand(NFCPages pages) const {
363 switch (pages) {
364 case NFCPages::Block0:
365 return {
366 .block_count = 1,
367 };
368 case NFCPages::Block45:
369 return {
370 .block_count = 1,
371 .blocks =
372 {
373 NFCReadBlock{0x00, 0x2C},
374 },
375 };
376 case NFCPages::Block135:
377 return {
378 .block_count = 3,
379 .blocks =
380 {
381 NFCReadBlock{0x00, 0x3b},
382 {0x3c, 0x77},
383 {0x78, 0x86},
384 },
385 };
386 case NFCPages::Block231:
387 return {
388 .block_count = 4,
389 .blocks =
390 {
391 NFCReadBlock{0x00, 0x3b},
392 {0x3c, 0x77},
393 {0x78, 0x83},
394 {0xb4, 0xe6},
395 },
396 };
397 default:
398 return {};
399 };
400}
401
402bool NfcProtocol::IsEnabled() const {
403 return is_enabled;
404}
405
406} // namespace InputCommon::Joycon
diff --git a/src/input_common/helpers/joycon_protocol/nfc.h b/src/input_common/helpers/joycon_protocol/nfc.h
new file mode 100644
index 000000000..11e263e07
--- /dev/null
+++ b/src/input_common/helpers/joycon_protocol/nfc.h
@@ -0,0 +1,61 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4// Based on dkms-hid-nintendo implementation, CTCaer joycon toolkit and dekuNukem reverse
5// engineering https://github.com/nicman23/dkms-hid-nintendo/blob/master/src/hid-nintendo.c
6// https://github.com/CTCaer/jc_toolkit
7// https://github.com/dekuNukem/Nintendo_Switch_Reverse_Engineering
8
9#pragma once
10
11#include <vector>
12
13#include "input_common/helpers/joycon_protocol/common_protocol.h"
14#include "input_common/helpers/joycon_protocol/joycon_types.h"
15
16namespace InputCommon::Joycon {
17
18class NfcProtocol final : private JoyconCommonProtocol {
19public:
20 explicit NfcProtocol(std::shared_ptr<JoyconHandle> handle);
21
22 DriverResult EnableNfc();
23
24 DriverResult DisableNfc();
25
26 DriverResult StartNFCPollingMode();
27
28 DriverResult ScanAmiibo(std::vector<u8>& data);
29
30 bool HasAmiibo();
31
32 bool IsEnabled() const;
33
34private:
35 struct TagFoundData {
36 u8 type;
37 std::vector<u8> uuid;
38 };
39
40 DriverResult WaitUntilNfcIsReady();
41
42 DriverResult StartPolling(TagFoundData& data);
43
44 DriverResult ReadTag(const TagFoundData& data);
45
46 DriverResult GetAmiiboData(std::vector<u8>& data);
47
48 DriverResult SendStartPollingRequest(MCUCommandResponse& output);
49
50 DriverResult SendStopPollingRequest(MCUCommandResponse& output);
51
52 DriverResult SendStartWaitingRecieveRequest(MCUCommandResponse& output);
53
54 DriverResult SendReadAmiiboRequest(MCUCommandResponse& output, NFCPages ntag_pages);
55
56 NFCReadBlockCommand GetReadBlockCommand(NFCPages pages) const;
57
58 bool is_enabled{};
59};
60
61} // namespace InputCommon::Joycon
diff --git a/src/input_common/helpers/joycon_protocol/poller.cpp b/src/input_common/helpers/joycon_protocol/poller.cpp
new file mode 100644
index 000000000..9bb15e935
--- /dev/null
+++ b/src/input_common/helpers/joycon_protocol/poller.cpp
@@ -0,0 +1,337 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "common/logging/log.h"
5#include "input_common/helpers/joycon_protocol/poller.h"
6
7namespace InputCommon::Joycon {
8
9JoyconPoller::JoyconPoller(ControllerType device_type_, JoyStickCalibration left_stick_calibration_,
10 JoyStickCalibration right_stick_calibration_,
11 MotionCalibration motion_calibration_)
12 : device_type{device_type_}, left_stick_calibration{left_stick_calibration_},
13 right_stick_calibration{right_stick_calibration_}, motion_calibration{motion_calibration_} {}
14
15void JoyconPoller::SetCallbacks(const Joycon::JoyconCallbacks& callbacks_) {
16 callbacks = std::move(callbacks_);
17}
18
19void JoyconPoller::ReadActiveMode(std::span<u8> buffer, const MotionStatus& motion_status,
20 const RingStatus& ring_status) {
21 InputReportActive data{};
22 memcpy(&data, buffer.data(), sizeof(InputReportActive));
23
24 switch (device_type) {
25 case Joycon::ControllerType::Left:
26 UpdateActiveLeftPadInput(data, motion_status);
27 break;
28 case Joycon::ControllerType::Right:
29 UpdateActiveRightPadInput(data, motion_status);
30 break;
31 case Joycon::ControllerType::Pro:
32 UpdateActiveProPadInput(data, motion_status);
33 break;
34 default:
35 break;
36 }
37
38 if (ring_status.is_enabled) {
39 UpdateRing(data.ring_input, ring_status);
40 }
41
42 callbacks.on_battery_data(data.battery_status);
43}
44
45void JoyconPoller::ReadPassiveMode(std::span<u8> buffer) {
46 InputReportPassive data{};
47 memcpy(&data, buffer.data(), sizeof(InputReportPassive));
48
49 switch (device_type) {
50 case Joycon::ControllerType::Left:
51 UpdatePasiveLeftPadInput(data);
52 break;
53 case Joycon::ControllerType::Right:
54 UpdatePasiveRightPadInput(data);
55 break;
56 case Joycon::ControllerType::Pro:
57 UpdatePasiveProPadInput(data);
58 break;
59 default:
60 break;
61 }
62}
63
64void JoyconPoller::ReadNfcIRMode(std::span<u8> buffer, const MotionStatus& motion_status) {
65 // This mode is compatible with the active mode
66 ReadActiveMode(buffer, motion_status, {});
67}
68
69void JoyconPoller::UpdateColor(const Color& color) {
70 callbacks.on_color_data(color);
71}
72
73void JoyconPoller::UpdateAmiibo(const std::vector<u8>& amiibo_data) {
74 callbacks.on_amiibo_data(amiibo_data);
75}
76
77void JoyconPoller::UpdateCamera(const std::vector<u8>& camera_data, IrsResolution format) {
78 callbacks.on_camera_data(camera_data, format);
79}
80
81void JoyconPoller::UpdateRing(s16 value, const RingStatus& ring_status) {
82 float normalized_value = static_cast<float>(value - ring_status.default_value);
83 if (normalized_value > 0) {
84 normalized_value = normalized_value /
85 static_cast<float>(ring_status.max_value - ring_status.default_value);
86 }
87 if (normalized_value < 0) {
88 normalized_value = normalized_value /
89 static_cast<float>(ring_status.default_value - ring_status.min_value);
90 }
91 callbacks.on_ring_data(normalized_value);
92}
93
94void JoyconPoller::UpdateActiveLeftPadInput(const InputReportActive& input,
95 const MotionStatus& motion_status) {
96 static constexpr std::array<Joycon::PadButton, 11> left_buttons{
97 Joycon::PadButton::Down, Joycon::PadButton::Up, Joycon::PadButton::Right,
98 Joycon::PadButton::Left, Joycon::PadButton::LeftSL, Joycon::PadButton::LeftSR,
99 Joycon::PadButton::L, Joycon::PadButton::ZL, Joycon::PadButton::Minus,
100 Joycon::PadButton::Capture, Joycon::PadButton::StickL,
101 };
102
103 const u32 raw_button =
104 static_cast<u32>(input.button_input[2] | ((input.button_input[1] & 0b00101001) << 16));
105 for (std::size_t i = 0; i < left_buttons.size(); ++i) {
106 const bool button_status = (raw_button & static_cast<u32>(left_buttons[i])) != 0;
107 const int button = static_cast<int>(left_buttons[i]);
108 callbacks.on_button_data(button, button_status);
109 }
110
111 const u16 raw_left_axis_x =
112 static_cast<u16>(input.left_stick_state[0] | ((input.left_stick_state[1] & 0xf) << 8));
113 const u16 raw_left_axis_y =
114 static_cast<u16>((input.left_stick_state[1] >> 4) | (input.left_stick_state[2] << 4));
115 const f32 left_axis_x = GetAxisValue(raw_left_axis_x, left_stick_calibration.x);
116 const f32 left_axis_y = GetAxisValue(raw_left_axis_y, left_stick_calibration.y);
117 callbacks.on_stick_data(static_cast<int>(PadAxes::LeftStickX), left_axis_x);
118 callbacks.on_stick_data(static_cast<int>(PadAxes::LeftStickY), left_axis_y);
119
120 if (motion_status.is_enabled) {
121 auto left_motion = GetMotionInput(input, motion_status);
122 // Rotate motion axis to the correct direction
123 left_motion.accel_y = -left_motion.accel_y;
124 left_motion.accel_z = -left_motion.accel_z;
125 left_motion.gyro_x = -left_motion.gyro_x;
126 callbacks.on_motion_data(static_cast<int>(PadMotion::LeftMotion), left_motion);
127 }
128}
129
130void JoyconPoller::UpdateActiveRightPadInput(const InputReportActive& input,
131 const MotionStatus& motion_status) {
132 static constexpr std::array<Joycon::PadButton, 11> right_buttons{
133 Joycon::PadButton::Y, Joycon::PadButton::X, Joycon::PadButton::B,
134 Joycon::PadButton::A, Joycon::PadButton::RightSL, Joycon::PadButton::RightSR,
135 Joycon::PadButton::R, Joycon::PadButton::ZR, Joycon::PadButton::Plus,
136 Joycon::PadButton::Home, Joycon::PadButton::StickR,
137 };
138
139 const u32 raw_button =
140 static_cast<u32>((input.button_input[0] << 8) | (input.button_input[1] << 16));
141 for (std::size_t i = 0; i < right_buttons.size(); ++i) {
142 const bool button_status = (raw_button & static_cast<u32>(right_buttons[i])) != 0;
143 const int button = static_cast<int>(right_buttons[i]);
144 callbacks.on_button_data(button, button_status);
145 }
146
147 const u16 raw_right_axis_x =
148 static_cast<u16>(input.right_stick_state[0] | ((input.right_stick_state[1] & 0xf) << 8));
149 const u16 raw_right_axis_y =
150 static_cast<u16>((input.right_stick_state[1] >> 4) | (input.right_stick_state[2] << 4));
151 const f32 right_axis_x = GetAxisValue(raw_right_axis_x, right_stick_calibration.x);
152 const f32 right_axis_y = GetAxisValue(raw_right_axis_y, right_stick_calibration.y);
153 callbacks.on_stick_data(static_cast<int>(PadAxes::RightStickX), right_axis_x);
154 callbacks.on_stick_data(static_cast<int>(PadAxes::RightStickY), right_axis_y);
155
156 if (motion_status.is_enabled) {
157 auto right_motion = GetMotionInput(input, motion_status);
158 // Rotate motion axis to the correct direction
159 right_motion.accel_x = -right_motion.accel_x;
160 right_motion.accel_y = -right_motion.accel_y;
161 right_motion.gyro_z = -right_motion.gyro_z;
162 callbacks.on_motion_data(static_cast<int>(PadMotion::RightMotion), right_motion);
163 }
164}
165
166void JoyconPoller::UpdateActiveProPadInput(const InputReportActive& input,
167 const MotionStatus& motion_status) {
168 static constexpr std::array<Joycon::PadButton, 18> pro_buttons{
169 Joycon::PadButton::Down, Joycon::PadButton::Up, Joycon::PadButton::Right,
170 Joycon::PadButton::Left, Joycon::PadButton::L, Joycon::PadButton::ZL,
171 Joycon::PadButton::Minus, Joycon::PadButton::Capture, Joycon::PadButton::Y,
172 Joycon::PadButton::X, Joycon::PadButton::B, Joycon::PadButton::A,
173 Joycon::PadButton::R, Joycon::PadButton::ZR, Joycon::PadButton::Plus,
174 Joycon::PadButton::Home, Joycon::PadButton::StickL, Joycon::PadButton::StickR,
175 };
176
177 const u32 raw_button = static_cast<u32>(input.button_input[2] | (input.button_input[0] << 8) |
178 (input.button_input[1] << 16));
179 for (std::size_t i = 0; i < pro_buttons.size(); ++i) {
180 const bool button_status = (raw_button & static_cast<u32>(pro_buttons[i])) != 0;
181 const int button = static_cast<int>(pro_buttons[i]);
182 callbacks.on_button_data(button, button_status);
183 }
184
185 const u16 raw_left_axis_x =
186 static_cast<u16>(input.left_stick_state[0] | ((input.left_stick_state[1] & 0xf) << 8));
187 const u16 raw_left_axis_y =
188 static_cast<u16>((input.left_stick_state[1] >> 4) | (input.left_stick_state[2] << 4));
189 const u16 raw_right_axis_x =
190 static_cast<u16>(input.right_stick_state[0] | ((input.right_stick_state[1] & 0xf) << 8));
191 const u16 raw_right_axis_y =
192 static_cast<u16>((input.right_stick_state[1] >> 4) | (input.right_stick_state[2] << 4));
193
194 const f32 left_axis_x = GetAxisValue(raw_left_axis_x, left_stick_calibration.x);
195 const f32 left_axis_y = GetAxisValue(raw_left_axis_y, left_stick_calibration.y);
196 const f32 right_axis_x = GetAxisValue(raw_right_axis_x, right_stick_calibration.x);
197 const f32 right_axis_y = GetAxisValue(raw_right_axis_y, right_stick_calibration.y);
198 callbacks.on_stick_data(static_cast<int>(PadAxes::LeftStickX), left_axis_x);
199 callbacks.on_stick_data(static_cast<int>(PadAxes::LeftStickY), left_axis_y);
200 callbacks.on_stick_data(static_cast<int>(PadAxes::RightStickX), right_axis_x);
201 callbacks.on_stick_data(static_cast<int>(PadAxes::RightStickY), right_axis_y);
202
203 if (motion_status.is_enabled) {
204 auto pro_motion = GetMotionInput(input, motion_status);
205 pro_motion.gyro_x = -pro_motion.gyro_x;
206 pro_motion.accel_y = -pro_motion.accel_y;
207 pro_motion.accel_z = -pro_motion.accel_z;
208 callbacks.on_motion_data(static_cast<int>(PadMotion::LeftMotion), pro_motion);
209 callbacks.on_motion_data(static_cast<int>(PadMotion::RightMotion), pro_motion);
210 }
211}
212
213void JoyconPoller::UpdatePasiveLeftPadInput(const InputReportPassive& input) {
214 static constexpr std::array<Joycon::PasivePadButton, 11> left_buttons{
215 Joycon::PasivePadButton::Down_A, Joycon::PasivePadButton::Right_X,
216 Joycon::PasivePadButton::Left_B, Joycon::PasivePadButton::Up_Y,
217 Joycon::PasivePadButton::SL, Joycon::PasivePadButton::SR,
218 Joycon::PasivePadButton::L_R, Joycon::PasivePadButton::ZL_ZR,
219 Joycon::PasivePadButton::Minus, Joycon::PasivePadButton::Capture,
220 Joycon::PasivePadButton::StickL,
221 };
222
223 for (auto left_button : left_buttons) {
224 const bool button_status = (input.button_input & static_cast<u32>(left_button)) != 0;
225 const int button = static_cast<int>(left_button);
226 callbacks.on_button_data(button, button_status);
227 }
228}
229
230void JoyconPoller::UpdatePasiveRightPadInput(const InputReportPassive& input) {
231 static constexpr std::array<Joycon::PasivePadButton, 11> right_buttons{
232 Joycon::PasivePadButton::Down_A, Joycon::PasivePadButton::Right_X,
233 Joycon::PasivePadButton::Left_B, Joycon::PasivePadButton::Up_Y,
234 Joycon::PasivePadButton::SL, Joycon::PasivePadButton::SR,
235 Joycon::PasivePadButton::L_R, Joycon::PasivePadButton::ZL_ZR,
236 Joycon::PasivePadButton::Plus, Joycon::PasivePadButton::Home,
237 Joycon::PasivePadButton::StickR,
238 };
239
240 for (auto right_button : right_buttons) {
241 const bool button_status = (input.button_input & static_cast<u32>(right_button)) != 0;
242 const int button = static_cast<int>(right_button);
243 callbacks.on_button_data(button, button_status);
244 }
245}
246
247void JoyconPoller::UpdatePasiveProPadInput(const InputReportPassive& input) {
248 static constexpr std::array<Joycon::PasivePadButton, 14> pro_buttons{
249 Joycon::PasivePadButton::Down_A, Joycon::PasivePadButton::Right_X,
250 Joycon::PasivePadButton::Left_B, Joycon::PasivePadButton::Up_Y,
251 Joycon::PasivePadButton::SL, Joycon::PasivePadButton::SR,
252 Joycon::PasivePadButton::L_R, Joycon::PasivePadButton::ZL_ZR,
253 Joycon::PasivePadButton::Minus, Joycon::PasivePadButton::Plus,
254 Joycon::PasivePadButton::Capture, Joycon::PasivePadButton::Home,
255 Joycon::PasivePadButton::StickL, Joycon::PasivePadButton::StickR,
256 };
257
258 for (auto pro_button : pro_buttons) {
259 const bool button_status = (input.button_input & static_cast<u32>(pro_button)) != 0;
260 const int button = static_cast<int>(pro_button);
261 callbacks.on_button_data(button, button_status);
262 }
263}
264
265f32 JoyconPoller::GetAxisValue(u16 raw_value, Joycon::JoyStickAxisCalibration calibration) const {
266 const f32 value = static_cast<f32>(raw_value - calibration.center);
267 if (value > 0.0f) {
268 return value / calibration.max;
269 }
270 return value / calibration.min;
271}
272
273f32 JoyconPoller::GetAccelerometerValue(s16 raw, const MotionSensorCalibration& cal,
274 AccelerometerSensitivity sensitivity) const {
275 const f32 value = raw * (1.0f / (cal.scale - cal.offset)) * 4;
276 switch (sensitivity) {
277 case Joycon::AccelerometerSensitivity::G2:
278 return value / 4.0f;
279 case Joycon::AccelerometerSensitivity::G4:
280 return value / 2.0f;
281 case Joycon::AccelerometerSensitivity::G8:
282 return value;
283 case Joycon::AccelerometerSensitivity::G16:
284 return value * 2.0f;
285 }
286 return value;
287}
288
289f32 JoyconPoller::GetGyroValue(s16 raw, const MotionSensorCalibration& cal,
290 GyroSensitivity sensitivity) const {
291 const f32 value = (raw - cal.offset) * (936.0f / (cal.scale - cal.offset)) / 360.0f;
292 switch (sensitivity) {
293 case Joycon::GyroSensitivity::DPS250:
294 return value / 8.0f;
295 case Joycon::GyroSensitivity::DPS500:
296 return value / 4.0f;
297 case Joycon::GyroSensitivity::DPS1000:
298 return value / 2.0f;
299 case Joycon::GyroSensitivity::DPS2000:
300 return value;
301 }
302 return value;
303}
304
305s16 JoyconPoller::GetRawIMUValues(std::size_t sensor, size_t axis,
306 const InputReportActive& input) const {
307 return input.motion_input[(sensor * 3) + axis];
308}
309
310MotionData JoyconPoller::GetMotionInput(const InputReportActive& input,
311 const MotionStatus& motion_status) const {
312 MotionData motion{};
313 const auto& accel_cal = motion_calibration.accelerometer;
314 const auto& gyro_cal = motion_calibration.gyro;
315 const s16 raw_accel_x = input.motion_input[1];
316 const s16 raw_accel_y = input.motion_input[0];
317 const s16 raw_accel_z = input.motion_input[2];
318 const s16 raw_gyro_x = input.motion_input[4];
319 const s16 raw_gyro_y = input.motion_input[3];
320 const s16 raw_gyro_z = input.motion_input[5];
321
322 motion.delta_timestamp = motion_status.delta_time;
323 motion.accel_x =
324 GetAccelerometerValue(raw_accel_x, accel_cal[1], motion_status.accelerometer_sensitivity);
325 motion.accel_y =
326 GetAccelerometerValue(raw_accel_y, accel_cal[0], motion_status.accelerometer_sensitivity);
327 motion.accel_z =
328 GetAccelerometerValue(raw_accel_z, accel_cal[2], motion_status.accelerometer_sensitivity);
329 motion.gyro_x = GetGyroValue(raw_gyro_x, gyro_cal[1], motion_status.gyro_sensitivity);
330 motion.gyro_y = GetGyroValue(raw_gyro_y, gyro_cal[0], motion_status.gyro_sensitivity);
331 motion.gyro_z = GetGyroValue(raw_gyro_z, gyro_cal[2], motion_status.gyro_sensitivity);
332
333 // TODO(German77): Return all three samples data
334 return motion;
335}
336
337} // namespace InputCommon::Joycon
diff --git a/src/input_common/helpers/joycon_protocol/poller.h b/src/input_common/helpers/joycon_protocol/poller.h
new file mode 100644
index 000000000..354d41dad
--- /dev/null
+++ b/src/input_common/helpers/joycon_protocol/poller.h
@@ -0,0 +1,81 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4// Based on dkms-hid-nintendo implementation, CTCaer joycon toolkit and dekuNukem reverse
5// engineering https://github.com/nicman23/dkms-hid-nintendo/blob/master/src/hid-nintendo.c
6// https://github.com/CTCaer/jc_toolkit
7// https://github.com/dekuNukem/Nintendo_Switch_Reverse_Engineering
8
9#pragma once
10
11#include <functional>
12#include <span>
13
14#include "input_common/helpers/joycon_protocol/joycon_types.h"
15
16namespace InputCommon::Joycon {
17
18// Handles input packages and triggers the corresponding input events
19class JoyconPoller {
20public:
21 JoyconPoller(ControllerType device_type_, JoyStickCalibration left_stick_calibration_,
22 JoyStickCalibration right_stick_calibration_,
23 MotionCalibration motion_calibration_);
24
25 void SetCallbacks(const Joycon::JoyconCallbacks& callbacks_);
26
27 /// Handles data from passive packages
28 void ReadPassiveMode(std::span<u8> buffer);
29
30 /// Handles data from active packages
31 void ReadActiveMode(std::span<u8> buffer, const MotionStatus& motion_status,
32 const RingStatus& ring_status);
33
34 /// Handles data from nfc or ir packages
35 void ReadNfcIRMode(std::span<u8> buffer, const MotionStatus& motion_status);
36
37 void UpdateColor(const Color& color);
38 void UpdateRing(s16 value, const RingStatus& ring_status);
39 void UpdateAmiibo(const std::vector<u8>& amiibo_data);
40 void UpdateCamera(const std::vector<u8>& amiibo_data, IrsResolution format);
41
42private:
43 void UpdateActiveLeftPadInput(const InputReportActive& input,
44 const MotionStatus& motion_status);
45 void UpdateActiveRightPadInput(const InputReportActive& input,
46 const MotionStatus& motion_status);
47 void UpdateActiveProPadInput(const InputReportActive& input, const MotionStatus& motion_status);
48
49 void UpdatePasiveLeftPadInput(const InputReportPassive& buffer);
50 void UpdatePasiveRightPadInput(const InputReportPassive& buffer);
51 void UpdatePasiveProPadInput(const InputReportPassive& buffer);
52
53 /// Returns a calibrated joystick axis from raw axis data
54 f32 GetAxisValue(u16 raw_value, Joycon::JoyStickAxisCalibration calibration) const;
55
56 /// Returns a calibrated accelerometer axis from raw motion data
57 f32 GetAccelerometerValue(s16 raw, const MotionSensorCalibration& cal,
58 AccelerometerSensitivity sensitivity) const;
59
60 /// Returns a calibrated gyro axis from raw motion data
61 f32 GetGyroValue(s16 raw_value, const MotionSensorCalibration& cal,
62 GyroSensitivity sensitivity) const;
63
64 /// Returns a raw motion value from a buffer
65 s16 GetRawIMUValues(size_t sensor, size_t axis, const InputReportActive& input) const;
66
67 /// Returns motion data from a buffer
68 MotionData GetMotionInput(const InputReportActive& input,
69 const MotionStatus& motion_status) const;
70
71 ControllerType device_type{};
72
73 // Device calibration
74 JoyStickCalibration left_stick_calibration{};
75 JoyStickCalibration right_stick_calibration{};
76 MotionCalibration motion_calibration{};
77
78 Joycon::JoyconCallbacks callbacks{};
79};
80
81} // namespace InputCommon::Joycon
diff --git a/src/input_common/helpers/joycon_protocol/ringcon.cpp b/src/input_common/helpers/joycon_protocol/ringcon.cpp
new file mode 100644
index 000000000..190cef812
--- /dev/null
+++ b/src/input_common/helpers/joycon_protocol/ringcon.cpp
@@ -0,0 +1,115 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "common/logging/log.h"
5#include "input_common/helpers/joycon_protocol/ringcon.h"
6
7namespace InputCommon::Joycon {
8
9RingConProtocol::RingConProtocol(std::shared_ptr<JoyconHandle> handle)
10 : JoyconCommonProtocol(std::move(handle)) {}
11
12DriverResult RingConProtocol::EnableRingCon() {
13 LOG_DEBUG(Input, "Enable Ringcon");
14 ScopedSetBlocking sb(this);
15 DriverResult result{DriverResult::Success};
16
17 if (result == DriverResult::Success) {
18 result = SetReportMode(ReportMode::STANDARD_FULL_60HZ);
19 }
20 if (result == DriverResult::Success) {
21 result = EnableMCU(true);
22 }
23 if (result == DriverResult::Success) {
24 const MCUConfig config{
25 .command = MCUCommand::ConfigureMCU,
26 .sub_command = MCUSubCommand::SetDeviceMode,
27 .mode = MCUMode::Standby,
28 .crc = {},
29 };
30 result = ConfigureMCU(config);
31 }
32
33 return result;
34}
35
36DriverResult RingConProtocol::DisableRingCon() {
37 LOG_DEBUG(Input, "Disable RingCon");
38 ScopedSetBlocking sb(this);
39 DriverResult result{DriverResult::Success};
40
41 if (result == DriverResult::Success) {
42 result = EnableMCU(false);
43 }
44
45 is_enabled = false;
46
47 return result;
48}
49
50DriverResult RingConProtocol::StartRingconPolling() {
51 LOG_DEBUG(Input, "Enable Ringcon");
52 ScopedSetBlocking sb(this);
53 DriverResult result{DriverResult::Success};
54 bool is_connected = false;
55
56 if (result == DriverResult::Success) {
57 result = IsRingConnected(is_connected);
58 }
59 if (result == DriverResult::Success && is_connected) {
60 LOG_INFO(Input, "Ringcon detected");
61 result = ConfigureRing();
62 }
63 if (result == DriverResult::Success) {
64 is_enabled = true;
65 }
66
67 return result;
68}
69
70DriverResult RingConProtocol::IsRingConnected(bool& is_connected) {
71 LOG_DEBUG(Input, "IsRingConnected");
72 constexpr std::size_t max_tries = 28;
73 SubCommandResponse output{};
74 std::size_t tries = 0;
75 is_connected = false;
76
77 do {
78 const auto result = SendSubCommand(SubCommand::GET_EXTERNAL_DEVICE_INFO, {}, output);
79
80 if (result != DriverResult::Success) {
81 return result;
82 }
83
84 if (tries++ >= max_tries) {
85 return DriverResult::NoDeviceDetected;
86 }
87 } while (output.external_device_id != ExternalDeviceId::RingController);
88
89 is_connected = true;
90 return DriverResult::Success;
91}
92
93DriverResult RingConProtocol::ConfigureRing() {
94 LOG_DEBUG(Input, "ConfigureRing");
95
96 static constexpr std::array<u8, 37> ring_config{
97 0x06, 0x03, 0x25, 0x06, 0x00, 0x00, 0x00, 0x00, 0x1C, 0x16, 0xED, 0x34, 0x36,
98 0x00, 0x00, 0x00, 0x0A, 0x64, 0x0B, 0xE6, 0xA9, 0x22, 0x00, 0x00, 0x04, 0x00,
99 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x90, 0xA8, 0xE1, 0x34, 0x36};
100
101 const DriverResult result = SendSubCommand(SubCommand::SET_EXTERNAL_FORMAT_CONFIG, ring_config);
102
103 if (result != DriverResult::Success) {
104 return result;
105 }
106
107 static constexpr std::array<u8, 4> ringcon_data{0x04, 0x01, 0x01, 0x02};
108 return SendSubCommand(SubCommand::ENABLE_EXTERNAL_POLLING, ringcon_data);
109}
110
111bool RingConProtocol::IsEnabled() const {
112 return is_enabled;
113}
114
115} // namespace InputCommon::Joycon
diff --git a/src/input_common/helpers/joycon_protocol/ringcon.h b/src/input_common/helpers/joycon_protocol/ringcon.h
new file mode 100644
index 000000000..6e858f3fc
--- /dev/null
+++ b/src/input_common/helpers/joycon_protocol/ringcon.h
@@ -0,0 +1,38 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4// Based on dkms-hid-nintendo implementation, CTCaer joycon toolkit and dekuNukem reverse
5// engineering https://github.com/nicman23/dkms-hid-nintendo/blob/master/src/hid-nintendo.c
6// https://github.com/CTCaer/jc_toolkit
7// https://github.com/dekuNukem/Nintendo_Switch_Reverse_Engineering
8
9#pragma once
10
11#include <vector>
12
13#include "input_common/helpers/joycon_protocol/common_protocol.h"
14#include "input_common/helpers/joycon_protocol/joycon_types.h"
15
16namespace InputCommon::Joycon {
17
18class RingConProtocol final : private JoyconCommonProtocol {
19public:
20 explicit RingConProtocol(std::shared_ptr<JoyconHandle> handle);
21
22 DriverResult EnableRingCon();
23
24 DriverResult DisableRingCon();
25
26 DriverResult StartRingconPolling();
27
28 bool IsEnabled() const;
29
30private:
31 DriverResult IsRingConnected(bool& is_connected);
32
33 DriverResult ConfigureRing();
34
35 bool is_enabled{};
36};
37
38} // namespace InputCommon::Joycon
diff --git a/src/input_common/helpers/joycon_protocol/rumble.cpp b/src/input_common/helpers/joycon_protocol/rumble.cpp
new file mode 100644
index 000000000..63b60c946
--- /dev/null
+++ b/src/input_common/helpers/joycon_protocol/rumble.cpp
@@ -0,0 +1,299 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include <algorithm>
5#include <cmath>
6
7#include "common/logging/log.h"
8#include "input_common/helpers/joycon_protocol/rumble.h"
9
10namespace InputCommon::Joycon {
11
12RumbleProtocol::RumbleProtocol(std::shared_ptr<JoyconHandle> handle)
13 : JoyconCommonProtocol(std::move(handle)) {}
14
15DriverResult RumbleProtocol::EnableRumble(bool is_enabled) {
16 LOG_DEBUG(Input, "Enable Rumble");
17 ScopedSetBlocking sb(this);
18 const std::array<u8, 1> buffer{static_cast<u8>(is_enabled ? 1 : 0)};
19 return SendSubCommand(SubCommand::ENABLE_VIBRATION, buffer);
20}
21
22DriverResult RumbleProtocol::SendVibration(const VibrationValue& vibration) {
23 std::array<u8, sizeof(DefaultVibrationBuffer)> buffer{};
24
25 if (vibration.high_amplitude <= 0.0f && vibration.low_amplitude <= 0.0f) {
26 return SendVibrationReport(DefaultVibrationBuffer);
27 }
28
29 // Protect joycons from damage from strong vibrations
30 const f32 clamp_amplitude =
31 1.0f / std::max(1.0f, vibration.high_amplitude + vibration.low_amplitude);
32
33 const u16 encoded_high_frequency = EncodeHighFrequency(vibration.high_frequency);
34 const u8 encoded_high_amplitude =
35 EncodeHighAmplitude(vibration.high_amplitude * clamp_amplitude);
36 const u8 encoded_low_frequency = EncodeLowFrequency(vibration.low_frequency);
37 const u16 encoded_low_amplitude = EncodeLowAmplitude(vibration.low_amplitude * clamp_amplitude);
38
39 buffer[0] = static_cast<u8>(encoded_high_frequency & 0xFF);
40 buffer[1] = static_cast<u8>(encoded_high_amplitude | ((encoded_high_frequency >> 8) & 0x01));
41 buffer[2] = static_cast<u8>(encoded_low_frequency | ((encoded_low_amplitude >> 8) & 0x80));
42 buffer[3] = static_cast<u8>(encoded_low_amplitude & 0xFF);
43
44 // Duplicate rumble for now
45 buffer[4] = buffer[0];
46 buffer[5] = buffer[1];
47 buffer[6] = buffer[2];
48 buffer[7] = buffer[3];
49
50 return SendVibrationReport(buffer);
51}
52
53u16 RumbleProtocol::EncodeHighFrequency(f32 frequency) const {
54 const u8 new_frequency =
55 static_cast<u8>(std::clamp(std::log2(frequency / 10.0f) * 32.0f, 0.0f, 255.0f));
56 return static_cast<u16>((new_frequency - 0x60) * 4);
57}
58
59u8 RumbleProtocol::EncodeLowFrequency(f32 frequency) const {
60 const u8 new_frequency =
61 static_cast<u8>(std::clamp(std::log2(frequency / 10.0f) * 32.0f, 0.0f, 255.0f));
62 return static_cast<u8>(new_frequency - 0x40);
63}
64
65u8 RumbleProtocol::EncodeHighAmplitude(f32 amplitude) const {
66 // More information about these values can be found here:
67 // https://github.com/dekuNukem/Nintendo_Switch_Reverse_Engineering/blob/master/rumble_data_table.md
68
69 static constexpr std::array<std::pair<f32, int>, 101> high_fequency_amplitude{
70 std::pair<f32, int>{0.0f, 0x0},
71 {0.01f, 0x2},
72 {0.012f, 0x4},
73 {0.014f, 0x6},
74 {0.017f, 0x8},
75 {0.02f, 0x0a},
76 {0.024f, 0x0c},
77 {0.028f, 0x0e},
78 {0.033f, 0x10},
79 {0.04f, 0x12},
80 {0.047f, 0x14},
81 {0.056f, 0x16},
82 {0.067f, 0x18},
83 {0.08f, 0x1a},
84 {0.095f, 0x1c},
85 {0.112f, 0x1e},
86 {0.117f, 0x20},
87 {0.123f, 0x22},
88 {0.128f, 0x24},
89 {0.134f, 0x26},
90 {0.14f, 0x28},
91 {0.146f, 0x2a},
92 {0.152f, 0x2c},
93 {0.159f, 0x2e},
94 {0.166f, 0x30},
95 {0.173f, 0x32},
96 {0.181f, 0x34},
97 {0.189f, 0x36},
98 {0.198f, 0x38},
99 {0.206f, 0x3a},
100 {0.215f, 0x3c},
101 {0.225f, 0x3e},
102 {0.23f, 0x40},
103 {0.235f, 0x42},
104 {0.24f, 0x44},
105 {0.245f, 0x46},
106 {0.251f, 0x48},
107 {0.256f, 0x4a},
108 {0.262f, 0x4c},
109 {0.268f, 0x4e},
110 {0.273f, 0x50},
111 {0.279f, 0x52},
112 {0.286f, 0x54},
113 {0.292f, 0x56},
114 {0.298f, 0x58},
115 {0.305f, 0x5a},
116 {0.311f, 0x5c},
117 {0.318f, 0x5e},
118 {0.325f, 0x60},
119 {0.332f, 0x62},
120 {0.34f, 0x64},
121 {0.347f, 0x66},
122 {0.355f, 0x68},
123 {0.362f, 0x6a},
124 {0.37f, 0x6c},
125 {0.378f, 0x6e},
126 {0.387f, 0x70},
127 {0.395f, 0x72},
128 {0.404f, 0x74},
129 {0.413f, 0x76},
130 {0.422f, 0x78},
131 {0.431f, 0x7a},
132 {0.44f, 0x7c},
133 {0.45f, 0x7e},
134 {0.46f, 0x80},
135 {0.47f, 0x82},
136 {0.48f, 0x84},
137 {0.491f, 0x86},
138 {0.501f, 0x88},
139 {0.512f, 0x8a},
140 {0.524f, 0x8c},
141 {0.535f, 0x8e},
142 {0.547f, 0x90},
143 {0.559f, 0x92},
144 {0.571f, 0x94},
145 {0.584f, 0x96},
146 {0.596f, 0x98},
147 {0.609f, 0x9a},
148 {0.623f, 0x9c},
149 {0.636f, 0x9e},
150 {0.65f, 0xa0},
151 {0.665f, 0xa2},
152 {0.679f, 0xa4},
153 {0.694f, 0xa6},
154 {0.709f, 0xa8},
155 {0.725f, 0xaa},
156 {0.741f, 0xac},
157 {0.757f, 0xae},
158 {0.773f, 0xb0},
159 {0.79f, 0xb2},
160 {0.808f, 0xb4},
161 {0.825f, 0xb6},
162 {0.843f, 0xb8},
163 {0.862f, 0xba},
164 {0.881f, 0xbc},
165 {0.9f, 0xbe},
166 {0.92f, 0xc0},
167 {0.94f, 0xc2},
168 {0.96f, 0xc4},
169 {0.981f, 0xc6},
170 {1.003f, 0xc8},
171 };
172
173 for (const auto& [amplitude_value, code] : high_fequency_amplitude) {
174 if (amplitude <= amplitude_value) {
175 return static_cast<u8>(code);
176 }
177 }
178
179 return static_cast<u8>(high_fequency_amplitude[high_fequency_amplitude.size() - 1].second);
180}
181
182u16 RumbleProtocol::EncodeLowAmplitude(f32 amplitude) const {
183 // More information about these values can be found here:
184 // https://github.com/dekuNukem/Nintendo_Switch_Reverse_Engineering/blob/master/rumble_data_table.md
185
186 static constexpr std::array<std::pair<f32, int>, 101> high_fequency_amplitude{
187 std::pair<f32, int>{0.0f, 0x0040},
188 {0.01f, 0x8040},
189 {0.012f, 0x0041},
190 {0.014f, 0x8041},
191 {0.017f, 0x0042},
192 {0.02f, 0x8042},
193 {0.024f, 0x0043},
194 {0.028f, 0x8043},
195 {0.033f, 0x0044},
196 {0.04f, 0x8044},
197 {0.047f, 0x0045},
198 {0.056f, 0x8045},
199 {0.067f, 0x0046},
200 {0.08f, 0x8046},
201 {0.095f, 0x0047},
202 {0.112f, 0x8047},
203 {0.117f, 0x0048},
204 {0.123f, 0x8048},
205 {0.128f, 0x0049},
206 {0.134f, 0x8049},
207 {0.14f, 0x004a},
208 {0.146f, 0x804a},
209 {0.152f, 0x004b},
210 {0.159f, 0x804b},
211 {0.166f, 0x004c},
212 {0.173f, 0x804c},
213 {0.181f, 0x004d},
214 {0.189f, 0x804d},
215 {0.198f, 0x004e},
216 {0.206f, 0x804e},
217 {0.215f, 0x004f},
218 {0.225f, 0x804f},
219 {0.23f, 0x0050},
220 {0.235f, 0x8050},
221 {0.24f, 0x0051},
222 {0.245f, 0x8051},
223 {0.251f, 0x0052},
224 {0.256f, 0x8052},
225 {0.262f, 0x0053},
226 {0.268f, 0x8053},
227 {0.273f, 0x0054},
228 {0.279f, 0x8054},
229 {0.286f, 0x0055},
230 {0.292f, 0x8055},
231 {0.298f, 0x0056},
232 {0.305f, 0x8056},
233 {0.311f, 0x0057},
234 {0.318f, 0x8057},
235 {0.325f, 0x0058},
236 {0.332f, 0x8058},
237 {0.34f, 0x0059},
238 {0.347f, 0x8059},
239 {0.355f, 0x005a},
240 {0.362f, 0x805a},
241 {0.37f, 0x005b},
242 {0.378f, 0x805b},
243 {0.387f, 0x005c},
244 {0.395f, 0x805c},
245 {0.404f, 0x005d},
246 {0.413f, 0x805d},
247 {0.422f, 0x005e},
248 {0.431f, 0x805e},
249 {0.44f, 0x005f},
250 {0.45f, 0x805f},
251 {0.46f, 0x0060},
252 {0.47f, 0x8060},
253 {0.48f, 0x0061},
254 {0.491f, 0x8061},
255 {0.501f, 0x0062},
256 {0.512f, 0x8062},
257 {0.524f, 0x0063},
258 {0.535f, 0x8063},
259 {0.547f, 0x0064},
260 {0.559f, 0x8064},
261 {0.571f, 0x0065},
262 {0.584f, 0x8065},
263 {0.596f, 0x0066},
264 {0.609f, 0x8066},
265 {0.623f, 0x0067},
266 {0.636f, 0x8067},
267 {0.65f, 0x0068},
268 {0.665f, 0x8068},
269 {0.679f, 0x0069},
270 {0.694f, 0x8069},
271 {0.709f, 0x006a},
272 {0.725f, 0x806a},
273 {0.741f, 0x006b},
274 {0.757f, 0x806b},
275 {0.773f, 0x006c},
276 {0.79f, 0x806c},
277 {0.808f, 0x006d},
278 {0.825f, 0x806d},
279 {0.843f, 0x006e},
280 {0.862f, 0x806e},
281 {0.881f, 0x006f},
282 {0.9f, 0x806f},
283 {0.92f, 0x0070},
284 {0.94f, 0x8070},
285 {0.96f, 0x0071},
286 {0.981f, 0x8071},
287 {1.003f, 0x0072},
288 };
289
290 for (const auto& [amplitude_value, code] : high_fequency_amplitude) {
291 if (amplitude <= amplitude_value) {
292 return static_cast<u16>(code);
293 }
294 }
295
296 return static_cast<u16>(high_fequency_amplitude[high_fequency_amplitude.size() - 1].second);
297}
298
299} // namespace InputCommon::Joycon
diff --git a/src/input_common/helpers/joycon_protocol/rumble.h b/src/input_common/helpers/joycon_protocol/rumble.h
new file mode 100644
index 000000000..6c12b7925
--- /dev/null
+++ b/src/input_common/helpers/joycon_protocol/rumble.h
@@ -0,0 +1,33 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4// Based on dkms-hid-nintendo implementation, CTCaer joycon toolkit and dekuNukem reverse
5// engineering https://github.com/nicman23/dkms-hid-nintendo/blob/master/src/hid-nintendo.c
6// https://github.com/CTCaer/jc_toolkit
7// https://github.com/dekuNukem/Nintendo_Switch_Reverse_Engineering
8
9#pragma once
10
11#include <vector>
12
13#include "input_common/helpers/joycon_protocol/common_protocol.h"
14#include "input_common/helpers/joycon_protocol/joycon_types.h"
15
16namespace InputCommon::Joycon {
17
18class RumbleProtocol final : private JoyconCommonProtocol {
19public:
20 explicit RumbleProtocol(std::shared_ptr<JoyconHandle> handle);
21
22 DriverResult EnableRumble(bool is_enabled);
23
24 DriverResult SendVibration(const VibrationValue& vibration);
25
26private:
27 u16 EncodeHighFrequency(f32 frequency) const;
28 u8 EncodeLowFrequency(f32 frequency) const;
29 u8 EncodeHighAmplitude(f32 amplitude) const;
30 u16 EncodeLowAmplitude(f32 amplitude) const;
31};
32
33} // namespace InputCommon::Joycon
diff --git a/src/input_common/helpers/stick_from_buttons.cpp b/src/input_common/helpers/stick_from_buttons.cpp
index f3a0b3419..a6be6dac1 100644
--- a/src/input_common/helpers/stick_from_buttons.cpp
+++ b/src/input_common/helpers/stick_from_buttons.cpp
@@ -11,6 +11,14 @@ namespace InputCommon {
11 11
12class Stick final : public Common::Input::InputDevice { 12class Stick final : public Common::Input::InputDevice {
13public: 13public:
14 // Some games such as EARTH DEFENSE FORCE: WORLD BROTHERS
15 // do not play nicely with the theoretical maximum range.
16 // Using a value one lower from the maximum emulates real stick behavior.
17 static constexpr float MAX_RANGE = 32766.0f / 32767.0f;
18 static constexpr float TAU = Common::PI * 2.0f;
19 // Use wider angle to ease the transition.
20 static constexpr float APERTURE = TAU * 0.15f;
21
14 using Button = std::unique_ptr<Common::Input::InputDevice>; 22 using Button = std::unique_ptr<Common::Input::InputDevice>;
15 23
16 Stick(Button up_, Button down_, Button left_, Button right_, Button modifier_, Button updater_, 24 Stick(Button up_, Button down_, Button left_, Button right_, Button modifier_, Button updater_,
@@ -56,30 +64,23 @@ public:
56 } 64 }
57 65
58 bool IsAngleGreater(float old_angle, float new_angle) const { 66 bool IsAngleGreater(float old_angle, float new_angle) const {
59 constexpr float TAU = Common::PI * 2.0f; 67 const float top_limit = new_angle + APERTURE;
60 // Use wider angle to ease the transition.
61 constexpr float aperture = TAU * 0.15f;
62 const float top_limit = new_angle + aperture;
63 return (old_angle > new_angle && old_angle <= top_limit) || 68 return (old_angle > new_angle && old_angle <= top_limit) ||
64 (old_angle + TAU > new_angle && old_angle + TAU <= top_limit); 69 (old_angle + TAU > new_angle && old_angle + TAU <= top_limit);
65 } 70 }
66 71
67 bool IsAngleSmaller(float old_angle, float new_angle) const { 72 bool IsAngleSmaller(float old_angle, float new_angle) const {
68 constexpr float TAU = Common::PI * 2.0f; 73 const float bottom_limit = new_angle - APERTURE;
69 // Use wider angle to ease the transition.
70 constexpr float aperture = TAU * 0.15f;
71 const float bottom_limit = new_angle - aperture;
72 return (old_angle >= bottom_limit && old_angle < new_angle) || 74 return (old_angle >= bottom_limit && old_angle < new_angle) ||
73 (old_angle - TAU >= bottom_limit && old_angle - TAU < new_angle); 75 (old_angle - TAU >= bottom_limit && old_angle - TAU < new_angle);
74 } 76 }
75 77
76 float GetAngle(std::chrono::time_point<std::chrono::steady_clock> now) const { 78 float GetAngle(std::chrono::time_point<std::chrono::steady_clock> now) const {
77 constexpr float TAU = Common::PI * 2.0f;
78 float new_angle = angle; 79 float new_angle = angle;
79 80
80 auto time_difference = static_cast<float>( 81 auto time_difference = static_cast<float>(
81 std::chrono::duration_cast<std::chrono::microseconds>(now - last_update).count()); 82 std::chrono::duration_cast<std::chrono::milliseconds>(now - last_update).count());
82 time_difference /= 1000.0f * 1000.0f; 83 time_difference /= 1000.0f;
83 if (time_difference > 0.5f) { 84 if (time_difference > 0.5f) {
84 time_difference = 0.5f; 85 time_difference = 0.5f;
85 } 86 }
@@ -196,8 +197,6 @@ public:
196 } 197 }
197 198
198 void UpdateStatus() { 199 void UpdateStatus() {
199 const float coef = modifier_status.value ? modifier_scale : 1.0f;
200
201 bool r = right_status; 200 bool r = right_status;
202 bool l = left_status; 201 bool l = left_status;
203 bool u = up_status; 202 bool u = up_status;
@@ -215,7 +214,7 @@ public:
215 214
216 // Move if a key is pressed 215 // Move if a key is pressed
217 if (r || l || u || d) { 216 if (r || l || u || d) {
218 amplitude = coef; 217 amplitude = modifier_status.value ? modifier_scale : MAX_RANGE;
219 } else { 218 } else {
220 amplitude = 0; 219 amplitude = 0;
221 } 220 }
@@ -269,30 +268,17 @@ public:
269 Common::Input::StickStatus status{}; 268 Common::Input::StickStatus status{};
270 status.x.properties = properties; 269 status.x.properties = properties;
271 status.y.properties = properties; 270 status.y.properties = properties;
271
272 if (Settings::values.emulate_analog_keyboard) { 272 if (Settings::values.emulate_analog_keyboard) {
273 const auto now = std::chrono::steady_clock::now(); 273 const auto now = std::chrono::steady_clock::now();
274 float angle_ = GetAngle(now); 274 const float angle_ = GetAngle(now);
275 status.x.raw_value = std::cos(angle_) * amplitude; 275 status.x.raw_value = std::cos(angle_) * amplitude;
276 status.y.raw_value = std::sin(angle_) * amplitude; 276 status.y.raw_value = std::sin(angle_) * amplitude;
277 return status; 277 return status;
278 } 278 }
279 constexpr float SQRT_HALF = 0.707106781f; 279
280 int x = 0, y = 0; 280 status.x.raw_value = std::cos(goal_angle) * amplitude;
281 if (right_status) { 281 status.y.raw_value = std::sin(goal_angle) * amplitude;
282 ++x;
283 }
284 if (left_status) {
285 --x;
286 }
287 if (up_status) {
288 ++y;
289 }
290 if (down_status) {
291 --y;
292 }
293 const float coef = modifier_status.value ? modifier_scale : 1.0f;
294 status.x.raw_value = static_cast<float>(x) * coef * (y == 0 ? 1.0f : SQRT_HALF);
295 status.y.raw_value = static_cast<float>(y) * coef * (x == 0 ? 1.0f : SQRT_HALF);
296 return status; 282 return status;
297 } 283 }
298 284
diff --git a/src/input_common/input_engine.cpp b/src/input_common/input_engine.cpp
index 61cfd0911..91aa96aa7 100644
--- a/src/input_common/input_engine.cpp
+++ b/src/input_common/input_engine.cpp
@@ -79,6 +79,17 @@ void InputEngine::SetBattery(const PadIdentifier& identifier, Common::Input::Bat
79 TriggerOnBatteryChange(identifier, value); 79 TriggerOnBatteryChange(identifier, value);
80} 80}
81 81
82void InputEngine::SetColor(const PadIdentifier& identifier, Common::Input::BodyColorStatus value) {
83 {
84 std::scoped_lock lock{mutex};
85 ControllerData& controller = controller_list.at(identifier);
86 if (!configuring) {
87 controller.color = value;
88 }
89 }
90 TriggerOnColorChange(identifier, value);
91}
92
82void InputEngine::SetMotion(const PadIdentifier& identifier, int motion, const BasicMotion& value) { 93void InputEngine::SetMotion(const PadIdentifier& identifier, int motion, const BasicMotion& value) {
83 { 94 {
84 std::scoped_lock lock{mutex}; 95 std::scoped_lock lock{mutex};
@@ -176,6 +187,18 @@ Common::Input::BatteryLevel InputEngine::GetBattery(const PadIdentifier& identif
176 return controller.battery; 187 return controller.battery;
177} 188}
178 189
190Common::Input::BodyColorStatus InputEngine::GetColor(const PadIdentifier& identifier) const {
191 std::scoped_lock lock{mutex};
192 const auto controller_iter = controller_list.find(identifier);
193 if (controller_iter == controller_list.cend()) {
194 LOG_ERROR(Input, "Invalid identifier guid={}, pad={}, port={}", identifier.guid.RawString(),
195 identifier.pad, identifier.port);
196 return {};
197 }
198 const ControllerData& controller = controller_iter->second;
199 return controller.color;
200}
201
179BasicMotion InputEngine::GetMotion(const PadIdentifier& identifier, int motion) const { 202BasicMotion InputEngine::GetMotion(const PadIdentifier& identifier, int motion) const {
180 std::scoped_lock lock{mutex}; 203 std::scoped_lock lock{mutex};
181 const auto controller_iter = controller_list.find(identifier); 204 const auto controller_iter = controller_list.find(identifier);
@@ -328,6 +351,20 @@ void InputEngine::TriggerOnBatteryChange(const PadIdentifier& identifier,
328 } 351 }
329} 352}
330 353
354void InputEngine::TriggerOnColorChange(const PadIdentifier& identifier,
355 [[maybe_unused]] Common::Input::BodyColorStatus value) {
356 std::scoped_lock lock{mutex_callback};
357 for (const auto& poller_pair : callback_list) {
358 const InputIdentifier& poller = poller_pair.second;
359 if (!IsInputIdentifierEqual(poller, identifier, EngineInputType::Color, 0)) {
360 continue;
361 }
362 if (poller.callback.on_change) {
363 poller.callback.on_change();
364 }
365 }
366}
367
331void InputEngine::TriggerOnMotionChange(const PadIdentifier& identifier, int motion, 368void InputEngine::TriggerOnMotionChange(const PadIdentifier& identifier, int motion,
332 const BasicMotion& value) { 369 const BasicMotion& value) {
333 std::scoped_lock lock{mutex_callback}; 370 std::scoped_lock lock{mutex_callback};
diff --git a/src/input_common/input_engine.h b/src/input_common/input_engine.h
index 6cbcf5207..50b5a3dc8 100644
--- a/src/input_common/input_engine.h
+++ b/src/input_common/input_engine.h
@@ -40,6 +40,7 @@ enum class EngineInputType {
40 Battery, 40 Battery,
41 Button, 41 Button,
42 Camera, 42 Camera,
43 Color,
43 HatButton, 44 HatButton,
44 Motion, 45 Motion,
45 Nfc, 46 Nfc,
@@ -104,14 +105,17 @@ public:
104 void EndConfiguration(); 105 void EndConfiguration();
105 106
106 // Sets a led pattern for a controller 107 // Sets a led pattern for a controller
107 virtual void SetLeds([[maybe_unused]] const PadIdentifier& identifier, 108 virtual Common::Input::DriverResult SetLeds(
108 [[maybe_unused]] const Common::Input::LedStatus& led_status) {} 109 [[maybe_unused]] const PadIdentifier& identifier,
110 [[maybe_unused]] const Common::Input::LedStatus& led_status) {
111 return Common::Input::DriverResult::NotSupported;
112 }
109 113
110 // Sets rumble to a controller 114 // Sets rumble to a controller
111 virtual Common::Input::VibrationError SetVibration( 115 virtual Common::Input::DriverResult SetVibration(
112 [[maybe_unused]] const PadIdentifier& identifier, 116 [[maybe_unused]] const PadIdentifier& identifier,
113 [[maybe_unused]] const Common::Input::VibrationStatus& vibration) { 117 [[maybe_unused]] const Common::Input::VibrationStatus& vibration) {
114 return Common::Input::VibrationError::NotSupported; 118 return Common::Input::DriverResult::NotSupported;
115 } 119 }
116 120
117 // Returns true if device supports vibrations 121 // Returns true if device supports vibrations
@@ -120,17 +124,17 @@ public:
120 } 124 }
121 125
122 // Sets polling mode to a controller 126 // Sets polling mode to a controller
123 virtual Common::Input::PollingError SetPollingMode( 127 virtual Common::Input::DriverResult SetPollingMode(
124 [[maybe_unused]] const PadIdentifier& identifier, 128 [[maybe_unused]] const PadIdentifier& identifier,
125 [[maybe_unused]] const Common::Input::PollingMode polling_mode) { 129 [[maybe_unused]] const Common::Input::PollingMode polling_mode) {
126 return Common::Input::PollingError::NotSupported; 130 return Common::Input::DriverResult::NotSupported;
127 } 131 }
128 132
129 // Sets camera format to a controller 133 // Sets camera format to a controller
130 virtual Common::Input::CameraError SetCameraFormat( 134 virtual Common::Input::DriverResult SetCameraFormat(
131 [[maybe_unused]] const PadIdentifier& identifier, 135 [[maybe_unused]] const PadIdentifier& identifier,
132 [[maybe_unused]] Common::Input::CameraFormat camera_format) { 136 [[maybe_unused]] Common::Input::CameraFormat camera_format) {
133 return Common::Input::CameraError::NotSupported; 137 return Common::Input::DriverResult::NotSupported;
134 } 138 }
135 139
136 // Returns success if nfc is supported 140 // Returns success if nfc is supported
@@ -199,6 +203,7 @@ public:
199 bool GetHatButton(const PadIdentifier& identifier, int button, u8 direction) const; 203 bool GetHatButton(const PadIdentifier& identifier, int button, u8 direction) const;
200 f32 GetAxis(const PadIdentifier& identifier, int axis) const; 204 f32 GetAxis(const PadIdentifier& identifier, int axis) const;
201 Common::Input::BatteryLevel GetBattery(const PadIdentifier& identifier) const; 205 Common::Input::BatteryLevel GetBattery(const PadIdentifier& identifier) const;
206 Common::Input::BodyColorStatus GetColor(const PadIdentifier& identifier) const;
202 BasicMotion GetMotion(const PadIdentifier& identifier, int motion) const; 207 BasicMotion GetMotion(const PadIdentifier& identifier, int motion) const;
203 Common::Input::CameraStatus GetCamera(const PadIdentifier& identifier) const; 208 Common::Input::CameraStatus GetCamera(const PadIdentifier& identifier) const;
204 Common::Input::NfcStatus GetNfc(const PadIdentifier& identifier) const; 209 Common::Input::NfcStatus GetNfc(const PadIdentifier& identifier) const;
@@ -212,6 +217,7 @@ protected:
212 void SetHatButton(const PadIdentifier& identifier, int button, u8 value); 217 void SetHatButton(const PadIdentifier& identifier, int button, u8 value);
213 void SetAxis(const PadIdentifier& identifier, int axis, f32 value); 218 void SetAxis(const PadIdentifier& identifier, int axis, f32 value);
214 void SetBattery(const PadIdentifier& identifier, Common::Input::BatteryLevel value); 219 void SetBattery(const PadIdentifier& identifier, Common::Input::BatteryLevel value);
220 void SetColor(const PadIdentifier& identifier, Common::Input::BodyColorStatus value);
215 void SetMotion(const PadIdentifier& identifier, int motion, const BasicMotion& value); 221 void SetMotion(const PadIdentifier& identifier, int motion, const BasicMotion& value);
216 void SetCamera(const PadIdentifier& identifier, const Common::Input::CameraStatus& value); 222 void SetCamera(const PadIdentifier& identifier, const Common::Input::CameraStatus& value);
217 void SetNfc(const PadIdentifier& identifier, const Common::Input::NfcStatus& value); 223 void SetNfc(const PadIdentifier& identifier, const Common::Input::NfcStatus& value);
@@ -227,6 +233,7 @@ private:
227 std::unordered_map<int, float> axes; 233 std::unordered_map<int, float> axes;
228 std::unordered_map<int, BasicMotion> motions; 234 std::unordered_map<int, BasicMotion> motions;
229 Common::Input::BatteryLevel battery{}; 235 Common::Input::BatteryLevel battery{};
236 Common::Input::BodyColorStatus color{};
230 Common::Input::CameraStatus camera{}; 237 Common::Input::CameraStatus camera{};
231 Common::Input::NfcStatus nfc{}; 238 Common::Input::NfcStatus nfc{};
232 }; 239 };
@@ -235,6 +242,8 @@ private:
235 void TriggerOnHatButtonChange(const PadIdentifier& identifier, int button, u8 value); 242 void TriggerOnHatButtonChange(const PadIdentifier& identifier, int button, u8 value);
236 void TriggerOnAxisChange(const PadIdentifier& identifier, int axis, f32 value); 243 void TriggerOnAxisChange(const PadIdentifier& identifier, int axis, f32 value);
237 void TriggerOnBatteryChange(const PadIdentifier& identifier, Common::Input::BatteryLevel value); 244 void TriggerOnBatteryChange(const PadIdentifier& identifier, Common::Input::BatteryLevel value);
245 void TriggerOnColorChange(const PadIdentifier& identifier,
246 Common::Input::BodyColorStatus value);
238 void TriggerOnMotionChange(const PadIdentifier& identifier, int motion, 247 void TriggerOnMotionChange(const PadIdentifier& identifier, int motion,
239 const BasicMotion& value); 248 const BasicMotion& value);
240 void TriggerOnCameraChange(const PadIdentifier& identifier, 249 void TriggerOnCameraChange(const PadIdentifier& identifier,
diff --git a/src/input_common/input_poller.cpp b/src/input_common/input_poller.cpp
index fb8be42e2..8c6a6521a 100644
--- a/src/input_common/input_poller.cpp
+++ b/src/input_common/input_poller.cpp
@@ -16,10 +16,10 @@ public:
16 16
17class InputFromButton final : public Common::Input::InputDevice { 17class InputFromButton final : public Common::Input::InputDevice {
18public: 18public:
19 explicit InputFromButton(PadIdentifier identifier_, int button_, bool toggle_, bool inverted_, 19 explicit InputFromButton(PadIdentifier identifier_, int button_, bool turbo_, bool toggle_,
20 InputEngine* input_engine_) 20 bool inverted_, InputEngine* input_engine_)
21 : identifier(identifier_), button(button_), toggle(toggle_), inverted(inverted_), 21 : identifier(identifier_), button(button_), turbo(turbo_), toggle(toggle_),
22 input_engine(input_engine_) { 22 inverted(inverted_), input_engine(input_engine_) {
23 UpdateCallback engine_callback{[this]() { OnChange(); }}; 23 UpdateCallback engine_callback{[this]() { OnChange(); }};
24 const InputIdentifier input_identifier{ 24 const InputIdentifier input_identifier{
25 .identifier = identifier, 25 .identifier = identifier,
@@ -40,6 +40,7 @@ public:
40 .value = input_engine->GetButton(identifier, button), 40 .value = input_engine->GetButton(identifier, button),
41 .inverted = inverted, 41 .inverted = inverted,
42 .toggle = toggle, 42 .toggle = toggle,
43 .turbo = turbo,
43 }; 44 };
44 } 45 }
45 46
@@ -68,6 +69,7 @@ public:
68private: 69private:
69 const PadIdentifier identifier; 70 const PadIdentifier identifier;
70 const int button; 71 const int button;
72 const bool turbo;
71 const bool toggle; 73 const bool toggle;
72 const bool inverted; 74 const bool inverted;
73 int callback_key; 75 int callback_key;
@@ -77,10 +79,10 @@ private:
77 79
78class InputFromHatButton final : public Common::Input::InputDevice { 80class InputFromHatButton final : public Common::Input::InputDevice {
79public: 81public:
80 explicit InputFromHatButton(PadIdentifier identifier_, int button_, u8 direction_, bool toggle_, 82 explicit InputFromHatButton(PadIdentifier identifier_, int button_, u8 direction_, bool turbo_,
81 bool inverted_, InputEngine* input_engine_) 83 bool toggle_, bool inverted_, InputEngine* input_engine_)
82 : identifier(identifier_), button(button_), direction(direction_), toggle(toggle_), 84 : identifier(identifier_), button(button_), direction(direction_), turbo(turbo_),
83 inverted(inverted_), input_engine(input_engine_) { 85 toggle(toggle_), inverted(inverted_), input_engine(input_engine_) {
84 UpdateCallback engine_callback{[this]() { OnChange(); }}; 86 UpdateCallback engine_callback{[this]() { OnChange(); }};
85 const InputIdentifier input_identifier{ 87 const InputIdentifier input_identifier{
86 .identifier = identifier, 88 .identifier = identifier,
@@ -101,6 +103,7 @@ public:
101 .value = input_engine->GetHatButton(identifier, button, direction), 103 .value = input_engine->GetHatButton(identifier, button, direction),
102 .inverted = inverted, 104 .inverted = inverted,
103 .toggle = toggle, 105 .toggle = toggle,
106 .turbo = turbo,
104 }; 107 };
105 } 108 }
106 109
@@ -130,6 +133,7 @@ private:
130 const PadIdentifier identifier; 133 const PadIdentifier identifier;
131 const int button; 134 const int button;
132 const u8 direction; 135 const u8 direction;
136 const bool turbo;
133 const bool toggle; 137 const bool toggle;
134 const bool inverted; 138 const bool inverted;
135 int callback_key; 139 int callback_key;
@@ -498,6 +502,58 @@ private:
498 InputEngine* input_engine; 502 InputEngine* input_engine;
499}; 503};
500 504
505class InputFromColor final : public Common::Input::InputDevice {
506public:
507 explicit InputFromColor(PadIdentifier identifier_, InputEngine* input_engine_)
508 : identifier(identifier_), input_engine(input_engine_) {
509 UpdateCallback engine_callback{[this]() { OnChange(); }};
510 const InputIdentifier input_identifier{
511 .identifier = identifier,
512 .type = EngineInputType::Color,
513 .index = 0,
514 .callback = engine_callback,
515 };
516 last_color_value = {};
517 callback_key = input_engine->SetCallback(input_identifier);
518 }
519
520 ~InputFromColor() override {
521 input_engine->DeleteCallback(callback_key);
522 }
523
524 Common::Input::BodyColorStatus GetStatus() const {
525 return input_engine->GetColor(identifier);
526 }
527
528 void ForceUpdate() override {
529 const Common::Input::CallbackStatus status{
530 .type = Common::Input::InputType::Color,
531 .color_status = GetStatus(),
532 };
533
534 last_color_value = status.color_status;
535 TriggerOnChange(status);
536 }
537
538 void OnChange() {
539 const Common::Input::CallbackStatus status{
540 .type = Common::Input::InputType::Color,
541 .color_status = GetStatus(),
542 };
543
544 if (status.color_status.body != last_color_value.body) {
545 last_color_value = status.color_status;
546 TriggerOnChange(status);
547 }
548 }
549
550private:
551 const PadIdentifier identifier;
552 int callback_key;
553 Common::Input::BodyColorStatus last_color_value;
554 InputEngine* input_engine;
555};
556
501class InputFromMotion final : public Common::Input::InputDevice { 557class InputFromMotion final : public Common::Input::InputDevice {
502public: 558public:
503 explicit InputFromMotion(PadIdentifier identifier_, int motion_sensor_, float gyro_threshold_, 559 explicit InputFromMotion(PadIdentifier identifier_, int motion_sensor_, float gyro_threshold_,
@@ -754,11 +810,11 @@ public:
754 explicit OutputFromIdentifier(PadIdentifier identifier_, InputEngine* input_engine_) 810 explicit OutputFromIdentifier(PadIdentifier identifier_, InputEngine* input_engine_)
755 : identifier(identifier_), input_engine(input_engine_) {} 811 : identifier(identifier_), input_engine(input_engine_) {}
756 812
757 void SetLED(const Common::Input::LedStatus& led_status) override { 813 Common::Input::DriverResult SetLED(const Common::Input::LedStatus& led_status) override {
758 input_engine->SetLeds(identifier, led_status); 814 return input_engine->SetLeds(identifier, led_status);
759 } 815 }
760 816
761 Common::Input::VibrationError SetVibration( 817 Common::Input::DriverResult SetVibration(
762 const Common::Input::VibrationStatus& vibration_status) override { 818 const Common::Input::VibrationStatus& vibration_status) override {
763 return input_engine->SetVibration(identifier, vibration_status); 819 return input_engine->SetVibration(identifier, vibration_status);
764 } 820 }
@@ -767,11 +823,12 @@ public:
767 return input_engine->IsVibrationEnabled(identifier); 823 return input_engine->IsVibrationEnabled(identifier);
768 } 824 }
769 825
770 Common::Input::PollingError SetPollingMode(Common::Input::PollingMode polling_mode) override { 826 Common::Input::DriverResult SetPollingMode(Common::Input::PollingMode polling_mode) override {
771 return input_engine->SetPollingMode(identifier, polling_mode); 827 return input_engine->SetPollingMode(identifier, polling_mode);
772 } 828 }
773 829
774 Common::Input::CameraError SetCameraFormat(Common::Input::CameraFormat camera_format) override { 830 Common::Input::DriverResult SetCameraFormat(
831 Common::Input::CameraFormat camera_format) override {
775 return input_engine->SetCameraFormat(identifier, camera_format); 832 return input_engine->SetCameraFormat(identifier, camera_format);
776 } 833 }
777 834
@@ -800,14 +857,15 @@ std::unique_ptr<Common::Input::InputDevice> InputFactory::CreateButtonDevice(
800 const auto keyboard_key = params.Get("code", 0); 857 const auto keyboard_key = params.Get("code", 0);
801 const auto toggle = params.Get("toggle", false) != 0; 858 const auto toggle = params.Get("toggle", false) != 0;
802 const auto inverted = params.Get("inverted", false) != 0; 859 const auto inverted = params.Get("inverted", false) != 0;
860 const auto turbo = params.Get("turbo", false) != 0;
803 input_engine->PreSetController(identifier); 861 input_engine->PreSetController(identifier);
804 input_engine->PreSetButton(identifier, button_id); 862 input_engine->PreSetButton(identifier, button_id);
805 input_engine->PreSetButton(identifier, keyboard_key); 863 input_engine->PreSetButton(identifier, keyboard_key);
806 if (keyboard_key != 0) { 864 if (keyboard_key != 0) {
807 return std::make_unique<InputFromButton>(identifier, keyboard_key, toggle, inverted, 865 return std::make_unique<InputFromButton>(identifier, keyboard_key, turbo, toggle, inverted,
808 input_engine.get()); 866 input_engine.get());
809 } 867 }
810 return std::make_unique<InputFromButton>(identifier, button_id, toggle, inverted, 868 return std::make_unique<InputFromButton>(identifier, button_id, turbo, toggle, inverted,
811 input_engine.get()); 869 input_engine.get());
812} 870}
813 871
@@ -823,11 +881,12 @@ std::unique_ptr<Common::Input::InputDevice> InputFactory::CreateHatButtonDevice(
823 const auto direction = input_engine->GetHatButtonId(params.Get("direction", "")); 881 const auto direction = input_engine->GetHatButtonId(params.Get("direction", ""));
824 const auto toggle = params.Get("toggle", false) != 0; 882 const auto toggle = params.Get("toggle", false) != 0;
825 const auto inverted = params.Get("inverted", false) != 0; 883 const auto inverted = params.Get("inverted", false) != 0;
884 const auto turbo = params.Get("turbo", false) != 0;
826 885
827 input_engine->PreSetController(identifier); 886 input_engine->PreSetController(identifier);
828 input_engine->PreSetHatButton(identifier, button_id); 887 input_engine->PreSetHatButton(identifier, button_id);
829 return std::make_unique<InputFromHatButton>(identifier, button_id, direction, toggle, inverted, 888 return std::make_unique<InputFromHatButton>(identifier, button_id, direction, turbo, toggle,
830 input_engine.get()); 889 inverted, input_engine.get());
831} 890}
832 891
833std::unique_ptr<Common::Input::InputDevice> InputFactory::CreateStickDevice( 892std::unique_ptr<Common::Input::InputDevice> InputFactory::CreateStickDevice(
@@ -966,6 +1025,18 @@ std::unique_ptr<Common::Input::InputDevice> InputFactory::CreateBatteryDevice(
966 return std::make_unique<InputFromBattery>(identifier, input_engine.get()); 1025 return std::make_unique<InputFromBattery>(identifier, input_engine.get());
967} 1026}
968 1027
1028std::unique_ptr<Common::Input::InputDevice> InputFactory::CreateColorDevice(
1029 const Common::ParamPackage& params) {
1030 const PadIdentifier identifier = {
1031 .guid = Common::UUID{params.Get("guid", "")},
1032 .port = static_cast<std::size_t>(params.Get("port", 0)),
1033 .pad = static_cast<std::size_t>(params.Get("pad", 0)),
1034 };
1035
1036 input_engine->PreSetController(identifier);
1037 return std::make_unique<InputFromColor>(identifier, input_engine.get());
1038}
1039
969std::unique_ptr<Common::Input::InputDevice> InputFactory::CreateMotionDevice( 1040std::unique_ptr<Common::Input::InputDevice> InputFactory::CreateMotionDevice(
970 Common::ParamPackage params) { 1041 Common::ParamPackage params) {
971 const PadIdentifier identifier = { 1042 const PadIdentifier identifier = {
@@ -1053,6 +1124,9 @@ std::unique_ptr<Common::Input::InputDevice> InputFactory::Create(
1053 if (params.Has("battery")) { 1124 if (params.Has("battery")) {
1054 return CreateBatteryDevice(params); 1125 return CreateBatteryDevice(params);
1055 } 1126 }
1127 if (params.Has("color")) {
1128 return CreateColorDevice(params);
1129 }
1056 if (params.Has("camera")) { 1130 if (params.Has("camera")) {
1057 return CreateCameraDevice(params); 1131 return CreateCameraDevice(params);
1058 } 1132 }
diff --git a/src/input_common/input_poller.h b/src/input_common/input_poller.h
index d7db13ce4..e097e254c 100644
--- a/src/input_common/input_poller.h
+++ b/src/input_common/input_poller.h
@@ -191,6 +191,17 @@ private:
191 const Common::ParamPackage& params); 191 const Common::ParamPackage& params);
192 192
193 /** 193 /**
194 * Creates a color device from the parameters given.
195 * @param params contains parameters for creating the device:
196 * - "guid": text string for identifying controllers
197 * - "port": port of the connected device
198 * - "pad": slot of the connected controller
199 * @returns a unique input device with the parameters specified
200 */
201 std::unique_ptr<Common::Input::InputDevice> CreateColorDevice(
202 const Common::ParamPackage& params);
203
204 /**
194 * Creates a motion device from the parameters given. 205 * Creates a motion device from the parameters given.
195 * @param params contains parameters for creating the device: 206 * @param params contains parameters for creating the device:
196 * - "axis_x": the controller horizontal axis id to bind with the input 207 * - "axis_x": the controller horizontal axis id to bind with the input
diff --git a/src/input_common/main.cpp b/src/input_common/main.cpp
index e0b2131ed..c77fc04ee 100644
--- a/src/input_common/main.cpp
+++ b/src/input_common/main.cpp
@@ -23,6 +23,7 @@
23#include "input_common/drivers/gc_adapter.h" 23#include "input_common/drivers/gc_adapter.h"
24#endif 24#endif
25#ifdef HAVE_SDL2 25#ifdef HAVE_SDL2
26#include "input_common/drivers/joycon.h"
26#include "input_common/drivers/sdl_driver.h" 27#include "input_common/drivers/sdl_driver.h"
27#endif 28#endif
28 29
@@ -81,6 +82,7 @@ struct InputSubsystem::Impl {
81 RegisterEngine("virtual_gamepad", virtual_gamepad); 82 RegisterEngine("virtual_gamepad", virtual_gamepad);
82#ifdef HAVE_SDL2 83#ifdef HAVE_SDL2
83 RegisterEngine("sdl", sdl); 84 RegisterEngine("sdl", sdl);
85 RegisterEngine("joycon", joycon);
84#endif 86#endif
85 87
86 Common::Input::RegisterInputFactory("touch_from_button", 88 Common::Input::RegisterInputFactory("touch_from_button",
@@ -111,6 +113,7 @@ struct InputSubsystem::Impl {
111 UnregisterEngine(virtual_gamepad); 113 UnregisterEngine(virtual_gamepad);
112#ifdef HAVE_SDL2 114#ifdef HAVE_SDL2
113 UnregisterEngine(sdl); 115 UnregisterEngine(sdl);
116 UnregisterEngine(joycon);
114#endif 117#endif
115 118
116 Common::Input::UnregisterInputFactory("touch_from_button"); 119 Common::Input::UnregisterInputFactory("touch_from_button");
@@ -133,6 +136,8 @@ struct InputSubsystem::Impl {
133 auto udp_devices = udp_client->GetInputDevices(); 136 auto udp_devices = udp_client->GetInputDevices();
134 devices.insert(devices.end(), udp_devices.begin(), udp_devices.end()); 137 devices.insert(devices.end(), udp_devices.begin(), udp_devices.end());
135#ifdef HAVE_SDL2 138#ifdef HAVE_SDL2
139 auto joycon_devices = joycon->GetInputDevices();
140 devices.insert(devices.end(), joycon_devices.begin(), joycon_devices.end());
136 auto sdl_devices = sdl->GetInputDevices(); 141 auto sdl_devices = sdl->GetInputDevices();
137 devices.insert(devices.end(), sdl_devices.begin(), sdl_devices.end()); 142 devices.insert(devices.end(), sdl_devices.begin(), sdl_devices.end());
138#endif 143#endif
@@ -164,6 +169,9 @@ struct InputSubsystem::Impl {
164 if (engine == sdl->GetEngineName()) { 169 if (engine == sdl->GetEngineName()) {
165 return sdl; 170 return sdl;
166 } 171 }
172 if (engine == joycon->GetEngineName()) {
173 return joycon;
174 }
167#endif 175#endif
168 return nullptr; 176 return nullptr;
169 } 177 }
@@ -247,6 +255,9 @@ struct InputSubsystem::Impl {
247 if (engine == sdl->GetEngineName()) { 255 if (engine == sdl->GetEngineName()) {
248 return true; 256 return true;
249 } 257 }
258 if (engine == joycon->GetEngineName()) {
259 return true;
260 }
250#endif 261#endif
251 return false; 262 return false;
252 } 263 }
@@ -260,6 +271,7 @@ struct InputSubsystem::Impl {
260 udp_client->BeginConfiguration(); 271 udp_client->BeginConfiguration();
261#ifdef HAVE_SDL2 272#ifdef HAVE_SDL2
262 sdl->BeginConfiguration(); 273 sdl->BeginConfiguration();
274 joycon->BeginConfiguration();
263#endif 275#endif
264 } 276 }
265 277
@@ -272,6 +284,7 @@ struct InputSubsystem::Impl {
272 udp_client->EndConfiguration(); 284 udp_client->EndConfiguration();
273#ifdef HAVE_SDL2 285#ifdef HAVE_SDL2
274 sdl->EndConfiguration(); 286 sdl->EndConfiguration();
287 joycon->EndConfiguration();
275#endif 288#endif
276 } 289 }
277 290
@@ -304,6 +317,7 @@ struct InputSubsystem::Impl {
304 317
305#ifdef HAVE_SDL2 318#ifdef HAVE_SDL2
306 std::shared_ptr<SDLDriver> sdl; 319 std::shared_ptr<SDLDriver> sdl;
320 std::shared_ptr<Joycons> joycon;
307#endif 321#endif
308}; 322};
309 323
diff --git a/src/shader_recompiler/backend/glasm/emit_glasm.cpp b/src/shader_recompiler/backend/glasm/emit_glasm.cpp
index 0cb1e193e..fd4a61a4d 100644
--- a/src/shader_recompiler/backend/glasm/emit_glasm.cpp
+++ b/src/shader_recompiler/backend/glasm/emit_glasm.cpp
@@ -279,6 +279,8 @@ void SetupOptions(const IR::Program& program, const Profile& profile,
279 header += "OPTION NV_internal;" 279 header += "OPTION NV_internal;"
280 "OPTION NV_shader_storage_buffer;" 280 "OPTION NV_shader_storage_buffer;"
281 "OPTION NV_gpu_program_fp64;"; 281 "OPTION NV_gpu_program_fp64;";
282 // TODO: Enable only when MS is used
283 header += "OPTION NV_texture_multisample;";
282 if (info.uses_int64_bit_atomics) { 284 if (info.uses_int64_bit_atomics) {
283 header += "OPTION NV_shader_atomic_int64;"; 285 header += "OPTION NV_shader_atomic_int64;";
284 } 286 }
diff --git a/src/shader_recompiler/backend/glasm/emit_glasm_bitwise_conversion.cpp b/src/shader_recompiler/backend/glasm/emit_glasm_bitwise_conversion.cpp
index 5bfdecc09..2fc2a0ac6 100644
--- a/src/shader_recompiler/backend/glasm/emit_glasm_bitwise_conversion.cpp
+++ b/src/shader_recompiler/backend/glasm/emit_glasm_bitwise_conversion.cpp
@@ -43,10 +43,6 @@ void EmitBitCastU64F64(EmitContext&, IR::Inst& inst, const IR::Value& value) {
43 Alias(inst, value); 43 Alias(inst, value);
44} 44}
45 45
46void EmitBitCastS32F32(EmitContext&, IR::Inst& inst, const IR::Value& value) {
47 Alias(inst, value);
48}
49
50void EmitBitCastF16U16(EmitContext&, IR::Inst& inst, const IR::Value& value) { 46void EmitBitCastF16U16(EmitContext&, IR::Inst& inst, const IR::Value& value) {
51 Alias(inst, value); 47 Alias(inst, value);
52} 48}
diff --git a/src/shader_recompiler/backend/glasm/emit_glasm_image.cpp b/src/shader_recompiler/backend/glasm/emit_glasm_image.cpp
index e67e80fac..85ee27333 100644
--- a/src/shader_recompiler/backend/glasm/emit_glasm_image.cpp
+++ b/src/shader_recompiler/backend/glasm/emit_glasm_image.cpp
@@ -59,7 +59,14 @@ std::string Image(EmitContext& ctx, IR::TextureInstInfo info,
59 } 59 }
60} 60}
61 61
62std::string_view TextureType(IR::TextureInstInfo info) { 62bool IsTextureMsaa(EmitContext& ctx, const IR::TextureInstInfo& info) {
63 if (info.type == TextureType::Buffer) {
64 return false;
65 }
66 return ctx.info.texture_descriptors.at(info.descriptor_index).is_multisample;
67}
68
69std::string_view TextureType(IR::TextureInstInfo info, bool is_ms = false) {
63 if (info.is_depth) { 70 if (info.is_depth) {
64 switch (info.type) { 71 switch (info.type) {
65 case TextureType::Color1D: 72 case TextureType::Color1D:
@@ -88,9 +95,9 @@ std::string_view TextureType(IR::TextureInstInfo info) {
88 return "ARRAY1D"; 95 return "ARRAY1D";
89 case TextureType::Color2D: 96 case TextureType::Color2D:
90 case TextureType::Color2DRect: 97 case TextureType::Color2DRect:
91 return "2D"; 98 return is_ms ? "2DMS" : "2D";
92 case TextureType::ColorArray2D: 99 case TextureType::ColorArray2D:
93 return "ARRAY2D"; 100 return is_ms ? "ARRAY2DMS" : "ARRAY2D";
94 case TextureType::Color3D: 101 case TextureType::Color3D:
95 return "3D"; 102 return "3D";
96 case TextureType::ColorCube: 103 case TextureType::ColorCube:
@@ -510,15 +517,16 @@ void EmitImageFetch(EmitContext& ctx, IR::Inst& inst, const IR::Value& index,
510 const IR::Value& coord, const IR::Value& offset, ScalarS32 lod, ScalarS32 ms) { 517 const IR::Value& coord, const IR::Value& offset, ScalarS32 lod, ScalarS32 ms) {
511 const auto info{inst.Flags<IR::TextureInstInfo>()}; 518 const auto info{inst.Flags<IR::TextureInstInfo>()};
512 const auto sparse_inst{PrepareSparse(inst)}; 519 const auto sparse_inst{PrepareSparse(inst)};
520 const bool is_multisample{ms.type != Type::Void};
513 const std::string_view sparse_mod{sparse_inst ? ".SPARSE" : ""}; 521 const std::string_view sparse_mod{sparse_inst ? ".SPARSE" : ""};
514 const std::string_view type{TextureType(info)}; 522 const std::string_view type{TextureType(info, is_multisample)};
515 const std::string texture{Texture(ctx, info, index)}; 523 const std::string texture{Texture(ctx, info, index)};
516 const std::string offset_vec{Offset(ctx, offset)}; 524 const std::string offset_vec{Offset(ctx, offset)};
517 const auto [coord_vec, coord_alloc]{Coord(ctx, coord)}; 525 const auto [coord_vec, coord_alloc]{Coord(ctx, coord)};
518 const Register ret{ctx.reg_alloc.Define(inst)}; 526 const Register ret{ctx.reg_alloc.Define(inst)};
519 if (info.type == TextureType::Buffer) { 527 if (info.type == TextureType::Buffer) {
520 ctx.Add("TXF.F{} {},{},{},{}{};", sparse_mod, ret, coord_vec, texture, type, offset_vec); 528 ctx.Add("TXF.F{} {},{},{},{}{};", sparse_mod, ret, coord_vec, texture, type, offset_vec);
521 } else if (ms.type != Type::Void) { 529 } else if (is_multisample) {
522 ctx.Add("MOV.S {}.w,{};" 530 ctx.Add("MOV.S {}.w,{};"
523 "TXFMS.F{} {},{},{},{}{};", 531 "TXFMS.F{} {},{},{},{}{};",
524 coord_vec, ms, sparse_mod, ret, coord_vec, texture, type, offset_vec); 532 coord_vec, ms, sparse_mod, ret, coord_vec, texture, type, offset_vec);
@@ -531,10 +539,11 @@ void EmitImageFetch(EmitContext& ctx, IR::Inst& inst, const IR::Value& index,
531} 539}
532 540
533void EmitImageQueryDimensions(EmitContext& ctx, IR::Inst& inst, const IR::Value& index, 541void EmitImageQueryDimensions(EmitContext& ctx, IR::Inst& inst, const IR::Value& index,
534 ScalarS32 lod) { 542 ScalarS32 lod, [[maybe_unused]] const IR::Value& skip_mips) {
535 const auto info{inst.Flags<IR::TextureInstInfo>()}; 543 const auto info{inst.Flags<IR::TextureInstInfo>()};
536 const std::string texture{Texture(ctx, info, index)}; 544 const std::string texture{Texture(ctx, info, index)};
537 const std::string_view type{TextureType(info)}; 545 const bool is_msaa{IsTextureMsaa(ctx, info)};
546 const std::string_view type{TextureType(info, is_msaa)};
538 ctx.Add("TXQ {},{},{},{};", inst, lod, texture, type); 547 ctx.Add("TXQ {},{},{},{};", inst, lod, texture, type);
539} 548}
540 549
diff --git a/src/shader_recompiler/backend/glasm/emit_glasm_instructions.h b/src/shader_recompiler/backend/glasm/emit_glasm_instructions.h
index eaaf9ba39..1a1ea61d5 100644
--- a/src/shader_recompiler/backend/glasm/emit_glasm_instructions.h
+++ b/src/shader_recompiler/backend/glasm/emit_glasm_instructions.h
@@ -197,7 +197,6 @@ void EmitSelectF64(EmitContext& ctx, ScalarS32 cond, Register true_value, Regist
197void EmitBitCastU16F16(EmitContext& ctx, IR::Inst& inst, const IR::Value& value); 197void EmitBitCastU16F16(EmitContext& ctx, IR::Inst& inst, const IR::Value& value);
198void EmitBitCastU32F32(EmitContext& ctx, IR::Inst& inst, const IR::Value& value); 198void EmitBitCastU32F32(EmitContext& ctx, IR::Inst& inst, const IR::Value& value);
199void EmitBitCastU64F64(EmitContext& ctx, IR::Inst& inst, const IR::Value& value); 199void EmitBitCastU64F64(EmitContext& ctx, IR::Inst& inst, const IR::Value& value);
200void EmitBitCastS32F32(EmitContext& ctx, IR::Inst& inst, const IR::Value& value);
201void EmitBitCastF16U16(EmitContext& ctx, IR::Inst& inst, const IR::Value& value); 200void EmitBitCastF16U16(EmitContext& ctx, IR::Inst& inst, const IR::Value& value);
202void EmitBitCastF32U32(EmitContext& ctx, IR::Inst& inst, const IR::Value& value); 201void EmitBitCastF32U32(EmitContext& ctx, IR::Inst& inst, const IR::Value& value);
203void EmitBitCastF64U64(EmitContext& ctx, IR::Inst& inst, const IR::Value& value); 202void EmitBitCastF64U64(EmitContext& ctx, IR::Inst& inst, const IR::Value& value);
@@ -582,7 +581,7 @@ void EmitImageGatherDref(EmitContext& ctx, IR::Inst& inst, const IR::Value& inde
582void EmitImageFetch(EmitContext& ctx, IR::Inst& inst, const IR::Value& index, 581void EmitImageFetch(EmitContext& ctx, IR::Inst& inst, const IR::Value& index,
583 const IR::Value& coord, const IR::Value& offset, ScalarS32 lod, ScalarS32 ms); 582 const IR::Value& coord, const IR::Value& offset, ScalarS32 lod, ScalarS32 ms);
584void EmitImageQueryDimensions(EmitContext& ctx, IR::Inst& inst, const IR::Value& index, 583void EmitImageQueryDimensions(EmitContext& ctx, IR::Inst& inst, const IR::Value& index,
585 ScalarS32 lod); 584 ScalarS32 lod, const IR::Value& skip_mips);
586void EmitImageQueryLod(EmitContext& ctx, IR::Inst& inst, const IR::Value& index, Register coord); 585void EmitImageQueryLod(EmitContext& ctx, IR::Inst& inst, const IR::Value& index, Register coord);
587void EmitImageGradient(EmitContext& ctx, IR::Inst& inst, const IR::Value& index, 586void EmitImageGradient(EmitContext& ctx, IR::Inst& inst, const IR::Value& index,
588 const IR::Value& coord, const IR::Value& derivatives, 587 const IR::Value& coord, const IR::Value& derivatives,
diff --git a/src/shader_recompiler/backend/glsl/emit_glsl_bitwise_conversion.cpp b/src/shader_recompiler/backend/glsl/emit_glsl_bitwise_conversion.cpp
index 8e5e6cf1f..1be4a0f59 100644
--- a/src/shader_recompiler/backend/glsl/emit_glsl_bitwise_conversion.cpp
+++ b/src/shader_recompiler/backend/glsl/emit_glsl_bitwise_conversion.cpp
@@ -48,10 +48,6 @@ void EmitBitCastU64F64(EmitContext& ctx, IR::Inst& inst, std::string_view value)
48 ctx.AddU64("{}=doubleBitsToUint64({});", inst, value); 48 ctx.AddU64("{}=doubleBitsToUint64({});", inst, value);
49} 49}
50 50
51void EmitBitCastS32F32(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
52 ctx.AddF32("{}=ftoi({});", inst, value);
53}
54
55void EmitBitCastF16U16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst) { 51void EmitBitCastF16U16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst) {
56 NotImplemented(); 52 NotImplemented();
57} 53}
diff --git a/src/shader_recompiler/backend/glsl/emit_glsl_image.cpp b/src/shader_recompiler/backend/glsl/emit_glsl_image.cpp
index cecdbb9d6..f335c8af0 100644
--- a/src/shader_recompiler/backend/glsl/emit_glsl_image.cpp
+++ b/src/shader_recompiler/backend/glsl/emit_glsl_image.cpp
@@ -25,6 +25,13 @@ std::string Image(EmitContext& ctx, const IR::TextureInstInfo& info, const IR::V
25 return fmt::format("img{}{}", def.binding, index_offset); 25 return fmt::format("img{}{}", def.binding, index_offset);
26} 26}
27 27
28bool IsTextureMsaa(EmitContext& ctx, const IR::TextureInstInfo& info) {
29 if (info.type == TextureType::Buffer) {
30 return false;
31 }
32 return ctx.info.texture_descriptors.at(info.descriptor_index).is_multisample;
33}
34
28std::string CastToIntVec(std::string_view value, const IR::TextureInstInfo& info) { 35std::string CastToIntVec(std::string_view value, const IR::TextureInstInfo& info) {
29 switch (info.type) { 36 switch (info.type) {
30 case TextureType::Color1D: 37 case TextureType::Color1D:
@@ -414,7 +421,7 @@ void EmitImageGatherDref(EmitContext& ctx, IR::Inst& inst, const IR::Value& inde
414 421
415void EmitImageFetch(EmitContext& ctx, IR::Inst& inst, const IR::Value& index, 422void EmitImageFetch(EmitContext& ctx, IR::Inst& inst, const IR::Value& index,
416 std::string_view coords, std::string_view offset, std::string_view lod, 423 std::string_view coords, std::string_view offset, std::string_view lod,
417 [[maybe_unused]] std::string_view ms) { 424 std::string_view ms) {
418 const auto info{inst.Flags<IR::TextureInstInfo>()}; 425 const auto info{inst.Flags<IR::TextureInstInfo>()};
419 if (info.has_bias) { 426 if (info.has_bias) {
420 throw NotImplementedException("EmitImageFetch Bias texture samples"); 427 throw NotImplementedException("EmitImageFetch Bias texture samples");
@@ -431,19 +438,24 @@ void EmitImageFetch(EmitContext& ctx, IR::Inst& inst, const IR::Value& index,
431 ctx.AddU1("{}=true;", *sparse_inst); 438 ctx.AddU1("{}=true;", *sparse_inst);
432 } 439 }
433 if (!sparse_inst || !supports_sparse) { 440 if (!sparse_inst || !supports_sparse) {
434 if (!offset.empty()) { 441 const auto int_coords{CoordsCastToInt(coords, info)};
435 ctx.Add("{}=texelFetchOffset({},{},int({}),{});", texel, texture, 442 if (!ms.empty()) {
436 CoordsCastToInt(coords, info), lod, CoordsCastToInt(offset, info)); 443 ctx.Add("{}=texelFetch({},{},int({}));", texel, texture, int_coords, ms);
444 } else if (!offset.empty()) {
445 ctx.Add("{}=texelFetchOffset({},{},int({}),{});", texel, texture, int_coords, lod,
446 CoordsCastToInt(offset, info));
437 } else { 447 } else {
438 if (info.type == TextureType::Buffer) { 448 if (info.type == TextureType::Buffer) {
439 ctx.Add("{}=texelFetch({},int({}));", texel, texture, coords); 449 ctx.Add("{}=texelFetch({},int({}));", texel, texture, coords);
440 } else { 450 } else {
441 ctx.Add("{}=texelFetch({},{},int({}));", texel, texture, 451 ctx.Add("{}=texelFetch({},{},int({}));", texel, texture, int_coords, lod);
442 CoordsCastToInt(coords, info), lod);
443 } 452 }
444 } 453 }
445 return; 454 return;
446 } 455 }
456 if (!ms.empty()) {
457 throw NotImplementedException("EmitImageFetch Sparse MSAA samples");
458 }
447 if (!offset.empty()) { 459 if (!offset.empty()) {
448 ctx.AddU1("{}=sparseTexelsResidentARB(sparseTexelFetchOffsetARB({},{},int({}),{},{}));", 460 ctx.AddU1("{}=sparseTexelsResidentARB(sparseTexelFetchOffsetARB({},{},int({}),{},{}));",
449 *sparse_inst, texture, CastToIntVec(coords, info), lod, 461 *sparse_inst, texture, CastToIntVec(coords, info), lod,
@@ -455,29 +467,36 @@ void EmitImageFetch(EmitContext& ctx, IR::Inst& inst, const IR::Value& index,
455} 467}
456 468
457void EmitImageQueryDimensions(EmitContext& ctx, IR::Inst& inst, const IR::Value& index, 469void EmitImageQueryDimensions(EmitContext& ctx, IR::Inst& inst, const IR::Value& index,
458 std::string_view lod) { 470 std::string_view lod, const IR::Value& skip_mips_val) {
459 const auto info{inst.Flags<IR::TextureInstInfo>()}; 471 const auto info{inst.Flags<IR::TextureInstInfo>()};
460 const auto texture{Texture(ctx, info, index)}; 472 const auto texture{Texture(ctx, info, index)};
473 const bool is_msaa{IsTextureMsaa(ctx, info)};
474 const bool skip_mips{skip_mips_val.U1()};
475 const auto mips{skip_mips ? "0u" : fmt::format("uint(textureQueryLevels({}))", texture)};
476 if (is_msaa && !skip_mips) {
477 throw NotImplementedException("EmitImageQueryDimensions MSAA QueryLevels");
478 }
479 if (info.type == TextureType::Buffer && !skip_mips) {
480 throw NotImplementedException("EmitImageQueryDimensions TextureType::Buffer QueryLevels");
481 }
482 const bool uses_lod{!is_msaa && info.type != TextureType::Buffer};
483 const auto lod_str{uses_lod ? fmt::format(",int({})", lod) : ""};
461 switch (info.type) { 484 switch (info.type) {
462 case TextureType::Color1D: 485 case TextureType::Color1D:
463 return ctx.AddU32x4( 486 return ctx.AddU32x4("{}=uvec4(uint(textureSize({}{})),0u,0u,{});", inst, texture, lod_str,
464 "{}=uvec4(uint(textureSize({},int({}))),0u,0u,uint(textureQueryLevels({})));", inst, 487 mips);
465 texture, lod, texture);
466 case TextureType::ColorArray1D: 488 case TextureType::ColorArray1D:
467 case TextureType::Color2D: 489 case TextureType::Color2D:
468 case TextureType::ColorCube: 490 case TextureType::ColorCube:
469 case TextureType::Color2DRect: 491 case TextureType::Color2DRect:
470 return ctx.AddU32x4( 492 return ctx.AddU32x4("{}=uvec4(uvec2(textureSize({}{})),0u,{});", inst, texture, lod_str,
471 "{}=uvec4(uvec2(textureSize({},int({}))),0u,uint(textureQueryLevels({})));", inst, 493 mips);
472 texture, lod, texture);
473 case TextureType::ColorArray2D: 494 case TextureType::ColorArray2D:
474 case TextureType::Color3D: 495 case TextureType::Color3D:
475 case TextureType::ColorArrayCube: 496 case TextureType::ColorArrayCube:
476 return ctx.AddU32x4( 497 return ctx.AddU32x4("{}=uvec4(uvec3(textureSize({}{})),{});", inst, texture, lod_str, mips);
477 "{}=uvec4(uvec3(textureSize({},int({}))),uint(textureQueryLevels({})));", inst, texture,
478 lod, texture);
479 case TextureType::Buffer: 498 case TextureType::Buffer:
480 throw NotImplementedException("EmitImageQueryDimensions Texture buffers"); 499 return ctx.AddU32x4("{}=uvec4(uint(textureSize({})),0u,0u,{});", inst, texture, mips);
481 } 500 }
482 throw LogicError("Unspecified image type {}", info.type.Value()); 501 throw LogicError("Unspecified image type {}", info.type.Value());
483} 502}
diff --git a/src/shader_recompiler/backend/glsl/emit_glsl_instructions.h b/src/shader_recompiler/backend/glsl/emit_glsl_instructions.h
index 4151c89de..8d0a65047 100644
--- a/src/shader_recompiler/backend/glsl/emit_glsl_instructions.h
+++ b/src/shader_recompiler/backend/glsl/emit_glsl_instructions.h
@@ -231,7 +231,6 @@ void EmitSelectF64(EmitContext& ctx, IR::Inst& inst, std::string_view cond,
231void EmitBitCastU16F16(EmitContext& ctx, IR::Inst& inst); 231void EmitBitCastU16F16(EmitContext& ctx, IR::Inst& inst);
232void EmitBitCastU32F32(EmitContext& ctx, IR::Inst& inst, std::string_view value); 232void EmitBitCastU32F32(EmitContext& ctx, IR::Inst& inst, std::string_view value);
233void EmitBitCastU64F64(EmitContext& ctx, IR::Inst& inst, std::string_view value); 233void EmitBitCastU64F64(EmitContext& ctx, IR::Inst& inst, std::string_view value);
234void EmitBitCastS32F32(EmitContext& ctx, IR::Inst& inst, std::string_view value);
235void EmitBitCastF16U16(EmitContext& ctx, IR::Inst& inst); 234void EmitBitCastF16U16(EmitContext& ctx, IR::Inst& inst);
236void EmitBitCastF32U32(EmitContext& ctx, IR::Inst& inst, std::string_view value); 235void EmitBitCastF32U32(EmitContext& ctx, IR::Inst& inst, std::string_view value);
237void EmitBitCastF64U64(EmitContext& ctx, IR::Inst& inst, std::string_view value); 236void EmitBitCastF64U64(EmitContext& ctx, IR::Inst& inst, std::string_view value);
@@ -655,7 +654,7 @@ void EmitImageFetch(EmitContext& ctx, IR::Inst& inst, const IR::Value& index,
655 std::string_view coords, std::string_view offset, std::string_view lod, 654 std::string_view coords, std::string_view offset, std::string_view lod,
656 std::string_view ms); 655 std::string_view ms);
657void EmitImageQueryDimensions(EmitContext& ctx, IR::Inst& inst, const IR::Value& index, 656void EmitImageQueryDimensions(EmitContext& ctx, IR::Inst& inst, const IR::Value& index,
658 std::string_view lod); 657 std::string_view lod, const IR::Value& skip_mips);
659void EmitImageQueryLod(EmitContext& ctx, IR::Inst& inst, const IR::Value& index, 658void EmitImageQueryLod(EmitContext& ctx, IR::Inst& inst, const IR::Value& index,
660 std::string_view coords); 659 std::string_view coords);
661void EmitImageGradient(EmitContext& ctx, IR::Inst& inst, const IR::Value& index, 660void EmitImageGradient(EmitContext& ctx, IR::Inst& inst, const IR::Value& index,
diff --git a/src/shader_recompiler/backend/glsl/glsl_emit_context.cpp b/src/shader_recompiler/backend/glsl/glsl_emit_context.cpp
index 5d01ec0cd..1b006e811 100644
--- a/src/shader_recompiler/backend/glsl/glsl_emit_context.cpp
+++ b/src/shader_recompiler/backend/glsl/glsl_emit_context.cpp
@@ -61,24 +61,28 @@ std::string OutputDecorator(Stage stage, u32 size) {
61 } 61 }
62} 62}
63 63
64std::string_view SamplerType(TextureType type, bool is_depth) { 64std::string_view DepthSamplerType(TextureType type) {
65 if (is_depth) { 65 switch (type) {
66 switch (type) { 66 case TextureType::Color1D:
67 case TextureType::Color1D: 67 return "sampler1DShadow";
68 return "sampler1DShadow"; 68 case TextureType::ColorArray1D:
69 case TextureType::ColorArray1D: 69 return "sampler1DArrayShadow";
70 return "sampler1DArrayShadow"; 70 case TextureType::Color2D:
71 case TextureType::Color2D: 71 return "sampler2DShadow";
72 return "sampler2DShadow"; 72 case TextureType::ColorArray2D:
73 case TextureType::ColorArray2D: 73 return "sampler2DArrayShadow";
74 return "sampler2DArrayShadow"; 74 case TextureType::ColorCube:
75 case TextureType::ColorCube: 75 return "samplerCubeShadow";
76 return "samplerCubeShadow"; 76 case TextureType::ColorArrayCube:
77 case TextureType::ColorArrayCube: 77 return "samplerCubeArrayShadow";
78 return "samplerCubeArrayShadow"; 78 default:
79 default: 79 throw NotImplementedException("Texture type: {}", type);
80 throw NotImplementedException("Texture type: {}", type); 80 }
81 } 81}
82
83std::string_view ColorSamplerType(TextureType type, bool is_multisample = false) {
84 if (is_multisample) {
85 ASSERT(type == TextureType::Color2D || type == TextureType::ColorArray2D);
82 } 86 }
83 switch (type) { 87 switch (type) {
84 case TextureType::Color1D: 88 case TextureType::Color1D:
@@ -87,9 +91,9 @@ std::string_view SamplerType(TextureType type, bool is_depth) {
87 return "sampler1DArray"; 91 return "sampler1DArray";
88 case TextureType::Color2D: 92 case TextureType::Color2D:
89 case TextureType::Color2DRect: 93 case TextureType::Color2DRect:
90 return "sampler2D"; 94 return is_multisample ? "sampler2DMS" : "sampler2D";
91 case TextureType::ColorArray2D: 95 case TextureType::ColorArray2D:
92 return "sampler2DArray"; 96 return is_multisample ? "sampler2DMSArray" : "sampler2DArray";
93 case TextureType::Color3D: 97 case TextureType::Color3D:
94 return "sampler3D"; 98 return "sampler3D";
95 case TextureType::ColorCube: 99 case TextureType::ColorCube:
@@ -677,7 +681,7 @@ void EmitContext::SetupTextures(Bindings& bindings) {
677 texture_buffers.reserve(info.texture_buffer_descriptors.size()); 681 texture_buffers.reserve(info.texture_buffer_descriptors.size());
678 for (const auto& desc : info.texture_buffer_descriptors) { 682 for (const auto& desc : info.texture_buffer_descriptors) {
679 texture_buffers.push_back({bindings.texture, desc.count}); 683 texture_buffers.push_back({bindings.texture, desc.count});
680 const auto sampler_type{SamplerType(TextureType::Buffer, false)}; 684 const auto sampler_type{ColorSamplerType(TextureType::Buffer)};
681 const auto array_decorator{desc.count > 1 ? fmt::format("[{}]", desc.count) : ""}; 685 const auto array_decorator{desc.count > 1 ? fmt::format("[{}]", desc.count) : ""};
682 header += fmt::format("layout(binding={}) uniform {} tex{}{};", bindings.texture, 686 header += fmt::format("layout(binding={}) uniform {} tex{}{};", bindings.texture,
683 sampler_type, bindings.texture, array_decorator); 687 sampler_type, bindings.texture, array_decorator);
@@ -686,7 +690,8 @@ void EmitContext::SetupTextures(Bindings& bindings) {
686 textures.reserve(info.texture_descriptors.size()); 690 textures.reserve(info.texture_descriptors.size());
687 for (const auto& desc : info.texture_descriptors) { 691 for (const auto& desc : info.texture_descriptors) {
688 textures.push_back({bindings.texture, desc.count}); 692 textures.push_back({bindings.texture, desc.count});
689 const auto sampler_type{SamplerType(desc.type, desc.is_depth)}; 693 const auto sampler_type{desc.is_depth ? DepthSamplerType(desc.type)
694 : ColorSamplerType(desc.type, desc.is_multisample)};
690 const auto array_decorator{desc.count > 1 ? fmt::format("[{}]", desc.count) : ""}; 695 const auto array_decorator{desc.count > 1 ? fmt::format("[{}]", desc.count) : ""};
691 header += fmt::format("layout(binding={}) uniform {} tex{}{};", bindings.texture, 696 header += fmt::format("layout(binding={}) uniform {} tex{}{};", bindings.texture,
692 sampler_type, bindings.texture, array_decorator); 697 sampler_type, bindings.texture, array_decorator);
diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_bitwise_conversion.cpp b/src/shader_recompiler/backend/spirv/emit_spirv_bitwise_conversion.cpp
index 50daacd95..c4ca28d11 100644
--- a/src/shader_recompiler/backend/spirv/emit_spirv_bitwise_conversion.cpp
+++ b/src/shader_recompiler/backend/spirv/emit_spirv_bitwise_conversion.cpp
@@ -18,10 +18,6 @@ void EmitBitCastU64F64(EmitContext&) {
18 throw NotImplementedException("SPIR-V Instruction"); 18 throw NotImplementedException("SPIR-V Instruction");
19} 19}
20 20
21void EmitBitCastS32F32(EmitContext&) {
22 throw NotImplementedException("SPIR-V Instruction");
23}
24
25void EmitBitCastF16U16(EmitContext&) { 21void EmitBitCastF16U16(EmitContext&) {
26 throw NotImplementedException("SPIR-V Instruction"); 22 throw NotImplementedException("SPIR-V Instruction");
27} 23}
diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_image.cpp b/src/shader_recompiler/backend/spirv/emit_spirv_image.cpp
index fb5799c42..02073c420 100644
--- a/src/shader_recompiler/backend/spirv/emit_spirv_image.cpp
+++ b/src/shader_recompiler/backend/spirv/emit_spirv_image.cpp
@@ -201,6 +201,13 @@ Id Image(EmitContext& ctx, const IR::Value& index, IR::TextureInstInfo info) {
201 } 201 }
202} 202}
203 203
204bool IsTextureMsaa(EmitContext& ctx, const IR::TextureInstInfo& info) {
205 if (info.type == TextureType::Buffer) {
206 return false;
207 }
208 return ctx.textures.at(info.descriptor_index).is_multisample;
209}
210
204Id Decorate(EmitContext& ctx, IR::Inst* inst, Id sample) { 211Id Decorate(EmitContext& ctx, IR::Inst* inst, Id sample) {
205 const auto info{inst->Flags<IR::TextureInstInfo>()}; 212 const auto info{inst->Flags<IR::TextureInstInfo>()};
206 if (info.relaxed_precision != 0) { 213 if (info.relaxed_precision != 0) {
@@ -436,34 +443,42 @@ Id EmitImageFetch(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id c
436 if (info.type == TextureType::Buffer) { 443 if (info.type == TextureType::Buffer) {
437 lod = Id{}; 444 lod = Id{};
438 } 445 }
446 if (Sirit::ValidId(ms)) {
447 // This image is multisampled, lod must be implicit
448 lod = Id{};
449 }
439 const ImageOperands operands(offset, lod, ms); 450 const ImageOperands operands(offset, lod, ms);
440 return Emit(&EmitContext::OpImageSparseFetch, &EmitContext::OpImageFetch, ctx, inst, ctx.F32[4], 451 return Emit(&EmitContext::OpImageSparseFetch, &EmitContext::OpImageFetch, ctx, inst, ctx.F32[4],
441 TextureImage(ctx, info, index), coords, operands.MaskOptional(), operands.Span()); 452 TextureImage(ctx, info, index), coords, operands.MaskOptional(), operands.Span());
442} 453}
443 454
444Id EmitImageQueryDimensions(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id lod) { 455Id EmitImageQueryDimensions(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id lod,
456 const IR::Value& skip_mips_val) {
445 const auto info{inst->Flags<IR::TextureInstInfo>()}; 457 const auto info{inst->Flags<IR::TextureInstInfo>()};
446 const Id image{TextureImage(ctx, info, index)}; 458 const Id image{TextureImage(ctx, info, index)};
447 const Id zero{ctx.u32_zero_value}; 459 const Id zero{ctx.u32_zero_value};
448 const auto mips{[&] { return ctx.OpImageQueryLevels(ctx.U32[1], image); }}; 460 const bool skip_mips{skip_mips_val.U1()};
461 const auto mips{[&] { return skip_mips ? zero : ctx.OpImageQueryLevels(ctx.U32[1], image); }};
462 const bool is_msaa{IsTextureMsaa(ctx, info)};
463 const bool uses_lod{!is_msaa && info.type != TextureType::Buffer};
464 const auto query{[&](Id type) {
465 return uses_lod ? ctx.OpImageQuerySizeLod(type, image, lod)
466 : ctx.OpImageQuerySize(type, image);
467 }};
449 switch (info.type) { 468 switch (info.type) {
450 case TextureType::Color1D: 469 case TextureType::Color1D:
451 return ctx.OpCompositeConstruct(ctx.U32[4], ctx.OpImageQuerySizeLod(ctx.U32[1], image, lod), 470 return ctx.OpCompositeConstruct(ctx.U32[4], query(ctx.U32[1]), zero, zero, mips());
452 zero, zero, mips());
453 case TextureType::ColorArray1D: 471 case TextureType::ColorArray1D:
454 case TextureType::Color2D: 472 case TextureType::Color2D:
455 case TextureType::ColorCube: 473 case TextureType::ColorCube:
456 case TextureType::Color2DRect: 474 case TextureType::Color2DRect:
457 return ctx.OpCompositeConstruct(ctx.U32[4], ctx.OpImageQuerySizeLod(ctx.U32[2], image, lod), 475 return ctx.OpCompositeConstruct(ctx.U32[4], query(ctx.U32[2]), zero, mips());
458 zero, mips());
459 case TextureType::ColorArray2D: 476 case TextureType::ColorArray2D:
460 case TextureType::Color3D: 477 case TextureType::Color3D:
461 case TextureType::ColorArrayCube: 478 case TextureType::ColorArrayCube:
462 return ctx.OpCompositeConstruct(ctx.U32[4], ctx.OpImageQuerySizeLod(ctx.U32[3], image, lod), 479 return ctx.OpCompositeConstruct(ctx.U32[4], query(ctx.U32[3]), mips());
463 mips());
464 case TextureType::Buffer: 480 case TextureType::Buffer:
465 return ctx.OpCompositeConstruct(ctx.U32[4], ctx.OpImageQuerySize(ctx.U32[1], image), zero, 481 return ctx.OpCompositeConstruct(ctx.U32[4], query(ctx.U32[1]), zero, zero, mips());
466 zero, mips());
467 } 482 }
468 throw LogicError("Unspecified image type {}", info.type.Value()); 483 throw LogicError("Unspecified image type {}", info.type.Value());
469} 484}
diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_instructions.h b/src/shader_recompiler/backend/spirv/emit_spirv_instructions.h
index e31cdc5e8..a440b557d 100644
--- a/src/shader_recompiler/backend/spirv/emit_spirv_instructions.h
+++ b/src/shader_recompiler/backend/spirv/emit_spirv_instructions.h
@@ -179,7 +179,6 @@ Id EmitSelectF64(EmitContext& ctx, Id cond, Id true_value, Id false_value);
179void EmitBitCastU16F16(EmitContext& ctx); 179void EmitBitCastU16F16(EmitContext& ctx);
180Id EmitBitCastU32F32(EmitContext& ctx, Id value); 180Id EmitBitCastU32F32(EmitContext& ctx, Id value);
181void EmitBitCastU64F64(EmitContext& ctx); 181void EmitBitCastU64F64(EmitContext& ctx);
182void EmitBitCastS32F32(EmitContext& ctx);
183void EmitBitCastF16U16(EmitContext&); 182void EmitBitCastF16U16(EmitContext&);
184Id EmitBitCastF32U32(EmitContext& ctx, Id value); 183Id EmitBitCastF32U32(EmitContext& ctx, Id value);
185void EmitBitCastF64U64(EmitContext& ctx); 184void EmitBitCastF64U64(EmitContext& ctx);
@@ -540,7 +539,8 @@ Id EmitImageGatherDref(EmitContext& ctx, IR::Inst* inst, const IR::Value& index,
540 const IR::Value& offset, const IR::Value& offset2, Id dref); 539 const IR::Value& offset, const IR::Value& offset2, Id dref);
541Id EmitImageFetch(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords, Id offset, 540Id EmitImageFetch(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords, Id offset,
542 Id lod, Id ms); 541 Id lod, Id ms);
543Id EmitImageQueryDimensions(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id lod); 542Id EmitImageQueryDimensions(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id lod,
543 const IR::Value& skip_mips);
544Id EmitImageQueryLod(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords); 544Id EmitImageQueryLod(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords);
545Id EmitImageGradient(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords, 545Id EmitImageGradient(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords,
546 Id derivates, Id offset, Id lod_clamp); 546 Id derivates, Id offset, Id lod_clamp);
diff --git a/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp b/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp
index a0c155fdb..d48d4860e 100644
--- a/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp
+++ b/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp
@@ -35,6 +35,7 @@ Id ImageType(EmitContext& ctx, const TextureDescriptor& desc) {
35 const spv::ImageFormat format{spv::ImageFormat::Unknown}; 35 const spv::ImageFormat format{spv::ImageFormat::Unknown};
36 const Id type{ctx.F32[1]}; 36 const Id type{ctx.F32[1]};
37 const bool depth{desc.is_depth}; 37 const bool depth{desc.is_depth};
38 const bool ms{desc.is_multisample};
38 switch (desc.type) { 39 switch (desc.type) {
39 case TextureType::Color1D: 40 case TextureType::Color1D:
40 return ctx.TypeImage(type, spv::Dim::Dim1D, depth, false, false, 1, format); 41 return ctx.TypeImage(type, spv::Dim::Dim1D, depth, false, false, 1, format);
@@ -42,9 +43,9 @@ Id ImageType(EmitContext& ctx, const TextureDescriptor& desc) {
42 return ctx.TypeImage(type, spv::Dim::Dim1D, depth, true, false, 1, format); 43 return ctx.TypeImage(type, spv::Dim::Dim1D, depth, true, false, 1, format);
43 case TextureType::Color2D: 44 case TextureType::Color2D:
44 case TextureType::Color2DRect: 45 case TextureType::Color2DRect:
45 return ctx.TypeImage(type, spv::Dim::Dim2D, depth, false, false, 1, format); 46 return ctx.TypeImage(type, spv::Dim::Dim2D, depth, false, ms, 1, format);
46 case TextureType::ColorArray2D: 47 case TextureType::ColorArray2D:
47 return ctx.TypeImage(type, spv::Dim::Dim2D, depth, true, false, 1, format); 48 return ctx.TypeImage(type, spv::Dim::Dim2D, depth, true, ms, 1, format);
48 case TextureType::Color3D: 49 case TextureType::Color3D:
49 return ctx.TypeImage(type, spv::Dim::Dim3D, depth, false, false, 1, format); 50 return ctx.TypeImage(type, spv::Dim::Dim3D, depth, false, false, 1, format);
50 case TextureType::ColorCube: 51 case TextureType::ColorCube:
@@ -1287,6 +1288,7 @@ void EmitContext::DefineTextures(const Info& info, u32& binding, u32& scaling_in
1287 .pointer_type = pointer_type, 1288 .pointer_type = pointer_type,
1288 .image_type = image_type, 1289 .image_type = image_type,
1289 .count = desc.count, 1290 .count = desc.count,
1291 .is_multisample = desc.is_multisample,
1290 }); 1292 });
1291 if (profile.supported_spirv >= 0x00010400) { 1293 if (profile.supported_spirv >= 0x00010400) {
1292 interfaces.push_back(id); 1294 interfaces.push_back(id);
diff --git a/src/shader_recompiler/backend/spirv/spirv_emit_context.h b/src/shader_recompiler/backend/spirv/spirv_emit_context.h
index dbc5c55b9..768a4fbb5 100644
--- a/src/shader_recompiler/backend/spirv/spirv_emit_context.h
+++ b/src/shader_recompiler/backend/spirv/spirv_emit_context.h
@@ -35,6 +35,7 @@ struct TextureDefinition {
35 Id pointer_type; 35 Id pointer_type;
36 Id image_type; 36 Id image_type;
37 u32 count; 37 u32 count;
38 bool is_multisample;
38}; 39};
39 40
40struct TextureBufferDefinition { 41struct TextureBufferDefinition {
diff --git a/src/shader_recompiler/frontend/ir/ir_emitter.cpp b/src/shader_recompiler/frontend/ir/ir_emitter.cpp
index eb2e49a68..b7caa4246 100644
--- a/src/shader_recompiler/frontend/ir/ir_emitter.cpp
+++ b/src/shader_recompiler/frontend/ir/ir_emitter.cpp
@@ -704,11 +704,6 @@ IR::U32 IREmitter::BitCast<IR::U32, IR::F32>(const IR::F32& value) {
704} 704}
705 705
706template <> 706template <>
707IR::S32 IREmitter::BitCast<IR::S32, IR::F32>(const IR::F32& value) {
708 return Inst<IR::S32>(Opcode::BitCastS32F32, value);
709}
710
711template <>
712IR::F32 IREmitter::BitCast<IR::F32, IR::U32>(const IR::U32& value) { 707IR::F32 IREmitter::BitCast<IR::F32, IR::U32>(const IR::U32& value) {
713 return Inst<IR::F32>(Opcode::BitCastF32U32, value); 708 return Inst<IR::F32>(Opcode::BitCastF32U32, value);
714} 709}
@@ -1851,15 +1846,16 @@ Value IREmitter::ImageFetch(const Value& handle, const Value& coords, const Valu
1851 return Inst(op, Flags{info}, handle, coords, offset, lod, multisampling); 1846 return Inst(op, Flags{info}, handle, coords, offset, lod, multisampling);
1852} 1847}
1853 1848
1854Value IREmitter::ImageQueryDimension(const Value& handle, const IR::U32& lod) { 1849Value IREmitter::ImageQueryDimension(const Value& handle, const IR::U32& lod,
1850 const IR::U1& skip_mips) {
1855 const Opcode op{handle.IsImmediate() ? Opcode::BoundImageQueryDimensions 1851 const Opcode op{handle.IsImmediate() ? Opcode::BoundImageQueryDimensions
1856 : Opcode::BindlessImageQueryDimensions}; 1852 : Opcode::BindlessImageQueryDimensions};
1857 return Inst(op, handle, lod); 1853 return Inst(op, handle, lod, skip_mips);
1858} 1854}
1859 1855
1860Value IREmitter::ImageQueryDimension(const Value& handle, const IR::U32& lod, 1856Value IREmitter::ImageQueryDimension(const Value& handle, const IR::U32& lod,
1861 TextureInstInfo info) { 1857 const IR::U1& skip_mips, TextureInstInfo info) {
1862 return Inst(Opcode::ImageQueryDimensions, Flags{info}, handle, lod); 1858 return Inst(Opcode::ImageQueryDimensions, Flags{info}, handle, lod, skip_mips);
1863} 1859}
1864 1860
1865Value IREmitter::ImageQueryLod(const Value& handle, const Value& coords, TextureInstInfo info) { 1861Value IREmitter::ImageQueryLod(const Value& handle, const Value& coords, TextureInstInfo info) {
diff --git a/src/shader_recompiler/frontend/ir/ir_emitter.h b/src/shader_recompiler/frontend/ir/ir_emitter.h
index 7aaaa4ab0..f3c81dbe1 100644
--- a/src/shader_recompiler/frontend/ir/ir_emitter.h
+++ b/src/shader_recompiler/frontend/ir/ir_emitter.h
@@ -320,9 +320,10 @@ public:
320 [[nodiscard]] F32 ImageSampleDrefExplicitLod(const Value& handle, const Value& coords, 320 [[nodiscard]] F32 ImageSampleDrefExplicitLod(const Value& handle, const Value& coords,
321 const F32& dref, const F32& lod, 321 const F32& dref, const F32& lod,
322 const Value& offset, TextureInstInfo info); 322 const Value& offset, TextureInstInfo info);
323 [[nodiscard]] Value ImageQueryDimension(const Value& handle, const IR::U32& lod);
324 [[nodiscard]] Value ImageQueryDimension(const Value& handle, const IR::U32& lod, 323 [[nodiscard]] Value ImageQueryDimension(const Value& handle, const IR::U32& lod,
325 TextureInstInfo info); 324 const IR::U1& skip_mips);
325 [[nodiscard]] Value ImageQueryDimension(const Value& handle, const IR::U32& lod,
326 const IR::U1& skip_mips, TextureInstInfo info);
326 327
327 [[nodiscard]] Value ImageQueryLod(const Value& handle, const Value& coords, 328 [[nodiscard]] Value ImageQueryLod(const Value& handle, const Value& coords,
328 TextureInstInfo info); 329 TextureInstInfo info);
@@ -408,7 +409,8 @@ private:
408 } 409 }
409 410
410 template <typename T> 411 template <typename T>
411 requires(sizeof(T) <= sizeof(u32) && std::is_trivially_copyable_v<T>) struct Flags { 412 requires(sizeof(T) <= sizeof(u32) && std::is_trivially_copyable_v<T>)
413 struct Flags {
412 Flags() = default; 414 Flags() = default;
413 Flags(T proxy_) : proxy{proxy_} {} 415 Flags(T proxy_) : proxy{proxy_} {}
414 416
diff --git a/src/shader_recompiler/frontend/ir/opcodes.h b/src/shader_recompiler/frontend/ir/opcodes.h
index d155afd0f..e300714f3 100644
--- a/src/shader_recompiler/frontend/ir/opcodes.h
+++ b/src/shader_recompiler/frontend/ir/opcodes.h
@@ -38,7 +38,6 @@ constexpr Type U8{Type::U8};
38constexpr Type U16{Type::U16}; 38constexpr Type U16{Type::U16};
39constexpr Type U32{Type::U32}; 39constexpr Type U32{Type::U32};
40constexpr Type U64{Type::U64}; 40constexpr Type U64{Type::U64};
41constexpr Type S32{Type::S32};
42constexpr Type F16{Type::F16}; 41constexpr Type F16{Type::F16};
43constexpr Type F32{Type::F32}; 42constexpr Type F32{Type::F32};
44constexpr Type F64{Type::F64}; 43constexpr Type F64{Type::F64};
diff --git a/src/shader_recompiler/frontend/ir/opcodes.inc b/src/shader_recompiler/frontend/ir/opcodes.inc
index 1fe3749cc..4447d67b0 100644
--- a/src/shader_recompiler/frontend/ir/opcodes.inc
+++ b/src/shader_recompiler/frontend/ir/opcodes.inc
@@ -175,7 +175,6 @@ OPCODE(SelectF64, F64, U1,
175OPCODE(BitCastU16F16, U16, F16, ) 175OPCODE(BitCastU16F16, U16, F16, )
176OPCODE(BitCastU32F32, U32, F32, ) 176OPCODE(BitCastU32F32, U32, F32, )
177OPCODE(BitCastU64F64, U64, F64, ) 177OPCODE(BitCastU64F64, U64, F64, )
178OPCODE(BitCastS32F32, S32, F32, )
179OPCODE(BitCastF16U16, F16, U16, ) 178OPCODE(BitCastF16U16, F16, U16, )
180OPCODE(BitCastF32U32, F32, U32, ) 179OPCODE(BitCastF32U32, F32, U32, )
181OPCODE(BitCastF64U64, F64, U64, ) 180OPCODE(BitCastF64U64, F64, U64, )
@@ -483,7 +482,7 @@ OPCODE(BindlessImageSampleDrefExplicitLod, F32, U32,
483OPCODE(BindlessImageGather, F32x4, U32, Opaque, Opaque, Opaque, ) 482OPCODE(BindlessImageGather, F32x4, U32, Opaque, Opaque, Opaque, )
484OPCODE(BindlessImageGatherDref, F32x4, U32, Opaque, Opaque, Opaque, F32, ) 483OPCODE(BindlessImageGatherDref, F32x4, U32, Opaque, Opaque, Opaque, F32, )
485OPCODE(BindlessImageFetch, F32x4, U32, Opaque, Opaque, U32, Opaque, ) 484OPCODE(BindlessImageFetch, F32x4, U32, Opaque, Opaque, U32, Opaque, )
486OPCODE(BindlessImageQueryDimensions, U32x4, U32, U32, ) 485OPCODE(BindlessImageQueryDimensions, U32x4, U32, U32, U1, )
487OPCODE(BindlessImageQueryLod, F32x4, U32, Opaque, ) 486OPCODE(BindlessImageQueryLod, F32x4, U32, Opaque, )
488OPCODE(BindlessImageGradient, F32x4, U32, Opaque, Opaque, Opaque, Opaque, ) 487OPCODE(BindlessImageGradient, F32x4, U32, Opaque, Opaque, Opaque, Opaque, )
489OPCODE(BindlessImageRead, U32x4, U32, Opaque, ) 488OPCODE(BindlessImageRead, U32x4, U32, Opaque, )
@@ -496,7 +495,7 @@ OPCODE(BoundImageSampleDrefExplicitLod, F32, U32,
496OPCODE(BoundImageGather, F32x4, U32, Opaque, Opaque, Opaque, ) 495OPCODE(BoundImageGather, F32x4, U32, Opaque, Opaque, Opaque, )
497OPCODE(BoundImageGatherDref, F32x4, U32, Opaque, Opaque, Opaque, F32, ) 496OPCODE(BoundImageGatherDref, F32x4, U32, Opaque, Opaque, Opaque, F32, )
498OPCODE(BoundImageFetch, F32x4, U32, Opaque, Opaque, U32, Opaque, ) 497OPCODE(BoundImageFetch, F32x4, U32, Opaque, Opaque, U32, Opaque, )
499OPCODE(BoundImageQueryDimensions, U32x4, U32, U32, ) 498OPCODE(BoundImageQueryDimensions, U32x4, U32, U32, U1, )
500OPCODE(BoundImageQueryLod, F32x4, U32, Opaque, ) 499OPCODE(BoundImageQueryLod, F32x4, U32, Opaque, )
501OPCODE(BoundImageGradient, F32x4, U32, Opaque, Opaque, Opaque, Opaque, ) 500OPCODE(BoundImageGradient, F32x4, U32, Opaque, Opaque, Opaque, Opaque, )
502OPCODE(BoundImageRead, U32x4, U32, Opaque, ) 501OPCODE(BoundImageRead, U32x4, U32, Opaque, )
@@ -509,7 +508,7 @@ OPCODE(ImageSampleDrefExplicitLod, F32, Opaq
509OPCODE(ImageGather, F32x4, Opaque, Opaque, Opaque, Opaque, ) 508OPCODE(ImageGather, F32x4, Opaque, Opaque, Opaque, Opaque, )
510OPCODE(ImageGatherDref, F32x4, Opaque, Opaque, Opaque, Opaque, F32, ) 509OPCODE(ImageGatherDref, F32x4, Opaque, Opaque, Opaque, Opaque, F32, )
511OPCODE(ImageFetch, F32x4, Opaque, Opaque, Opaque, U32, Opaque, ) 510OPCODE(ImageFetch, F32x4, Opaque, Opaque, Opaque, U32, Opaque, )
512OPCODE(ImageQueryDimensions, U32x4, Opaque, U32, ) 511OPCODE(ImageQueryDimensions, U32x4, Opaque, U32, U1, )
513OPCODE(ImageQueryLod, F32x4, Opaque, Opaque, ) 512OPCODE(ImageQueryLod, F32x4, Opaque, Opaque, )
514OPCODE(ImageGradient, F32x4, Opaque, Opaque, Opaque, Opaque, Opaque, ) 513OPCODE(ImageGradient, F32x4, Opaque, Opaque, Opaque, Opaque, Opaque, )
515OPCODE(ImageRead, U32x4, Opaque, Opaque, ) 514OPCODE(ImageRead, U32x4, Opaque, Opaque, )
diff --git a/src/shader_recompiler/frontend/ir/type.h b/src/shader_recompiler/frontend/ir/type.h
index 5a7c706ad..04c8c4ddb 100644
--- a/src/shader_recompiler/frontend/ir/type.h
+++ b/src/shader_recompiler/frontend/ir/type.h
@@ -24,22 +24,21 @@ enum class Type {
24 U16 = 1 << 7, 24 U16 = 1 << 7,
25 U32 = 1 << 8, 25 U32 = 1 << 8,
26 U64 = 1 << 9, 26 U64 = 1 << 9,
27 S32 = 1 << 10, 27 F16 = 1 << 10,
28 F16 = 1 << 11, 28 F32 = 1 << 11,
29 F32 = 1 << 12, 29 F64 = 1 << 12,
30 F64 = 1 << 13, 30 U32x2 = 1 << 13,
31 U32x2 = 1 << 14, 31 U32x3 = 1 << 14,
32 U32x3 = 1 << 15, 32 U32x4 = 1 << 15,
33 U32x4 = 1 << 16, 33 F16x2 = 1 << 16,
34 F16x2 = 1 << 17, 34 F16x3 = 1 << 17,
35 F16x3 = 1 << 18, 35 F16x4 = 1 << 18,
36 F16x4 = 1 << 19, 36 F32x2 = 1 << 19,
37 F32x2 = 1 << 20, 37 F32x3 = 1 << 20,
38 F32x3 = 1 << 21, 38 F32x4 = 1 << 21,
39 F32x4 = 1 << 22, 39 F64x2 = 1 << 22,
40 F64x2 = 1 << 23, 40 F64x3 = 1 << 23,
41 F64x3 = 1 << 24, 41 F64x4 = 1 << 24,
42 F64x4 = 1 << 25,
43}; 42};
44DECLARE_ENUM_FLAG_OPERATORS(Type) 43DECLARE_ENUM_FLAG_OPERATORS(Type)
45 44
diff --git a/src/shader_recompiler/frontend/ir/value.cpp b/src/shader_recompiler/frontend/ir/value.cpp
index 30ba12316..346169328 100644
--- a/src/shader_recompiler/frontend/ir/value.cpp
+++ b/src/shader_recompiler/frontend/ir/value.cpp
@@ -23,8 +23,6 @@ Value::Value(u16 value) noexcept : type{Type::U16}, imm_u16{value} {}
23 23
24Value::Value(u32 value) noexcept : type{Type::U32}, imm_u32{value} {} 24Value::Value(u32 value) noexcept : type{Type::U32}, imm_u32{value} {}
25 25
26Value::Value(s32 value) noexcept : type{Type::S32}, imm_s32{value} {}
27
28Value::Value(f32 value) noexcept : type{Type::F32}, imm_f32{value} {} 26Value::Value(f32 value) noexcept : type{Type::F32}, imm_f32{value} {}
29 27
30Value::Value(u64 value) noexcept : type{Type::U64}, imm_u64{value} {} 28Value::Value(u64 value) noexcept : type{Type::U64}, imm_u64{value} {}
@@ -71,7 +69,6 @@ bool Value::operator==(const Value& other) const {
71 return imm_u16 == other.imm_u16; 69 return imm_u16 == other.imm_u16;
72 case Type::U32: 70 case Type::U32:
73 case Type::F32: 71 case Type::F32:
74 case Type::S32:
75 return imm_u32 == other.imm_u32; 72 return imm_u32 == other.imm_u32;
76 case Type::U64: 73 case Type::U64:
77 case Type::F64: 74 case Type::F64:
diff --git a/src/shader_recompiler/frontend/ir/value.h b/src/shader_recompiler/frontend/ir/value.h
index 8b34356fd..c27546b0e 100644
--- a/src/shader_recompiler/frontend/ir/value.h
+++ b/src/shader_recompiler/frontend/ir/value.h
@@ -43,7 +43,6 @@ public:
43 explicit Value(u8 value) noexcept; 43 explicit Value(u8 value) noexcept;
44 explicit Value(u16 value) noexcept; 44 explicit Value(u16 value) noexcept;
45 explicit Value(u32 value) noexcept; 45 explicit Value(u32 value) noexcept;
46 explicit Value(s32 value) noexcept;
47 explicit Value(f32 value) noexcept; 46 explicit Value(f32 value) noexcept;
48 explicit Value(u64 value) noexcept; 47 explicit Value(u64 value) noexcept;
49 explicit Value(f64 value) noexcept; 48 explicit Value(f64 value) noexcept;
@@ -66,7 +65,6 @@ public:
66 [[nodiscard]] u8 U8() const; 65 [[nodiscard]] u8 U8() const;
67 [[nodiscard]] u16 U16() const; 66 [[nodiscard]] u16 U16() const;
68 [[nodiscard]] u32 U32() const; 67 [[nodiscard]] u32 U32() const;
69 [[nodiscard]] s32 S32() const;
70 [[nodiscard]] f32 F32() const; 68 [[nodiscard]] f32 F32() const;
71 [[nodiscard]] u64 U64() const; 69 [[nodiscard]] u64 U64() const;
72 [[nodiscard]] f64 F64() const; 70 [[nodiscard]] f64 F64() const;
@@ -86,7 +84,6 @@ private:
86 u8 imm_u8; 84 u8 imm_u8;
87 u16 imm_u16; 85 u16 imm_u16;
88 u32 imm_u32; 86 u32 imm_u32;
89 s32 imm_s32;
90 f32 imm_f32; 87 f32 imm_f32;
91 u64 imm_u64; 88 u64 imm_u64;
92 f64 imm_f64; 89 f64 imm_f64;
@@ -101,9 +98,8 @@ public:
101 TypedValue() = default; 98 TypedValue() = default;
102 99
103 template <IR::Type other_type> 100 template <IR::Type other_type>
104 requires((other_type & type_) != IR::Type::Void) explicit(false) 101 requires((other_type & type_) != IR::Type::Void)
105 TypedValue(const TypedValue<other_type>& value) 102 explicit(false) TypedValue(const TypedValue<other_type>& value) : Value(value) {}
106 : Value(value) {}
107 103
108 explicit TypedValue(const Value& value) : Value(value) { 104 explicit TypedValue(const Value& value) : Value(value) {
109 if ((value.Type() & type_) == IR::Type::Void) { 105 if ((value.Type() & type_) == IR::Type::Void) {
@@ -194,16 +190,16 @@ public:
194 void ReplaceOpcode(IR::Opcode opcode); 190 void ReplaceOpcode(IR::Opcode opcode);
195 191
196 template <typename FlagsType> 192 template <typename FlagsType>
197 requires(sizeof(FlagsType) <= sizeof(u32) && std::is_trivially_copyable_v<FlagsType>) 193 requires(sizeof(FlagsType) <= sizeof(u32) && std::is_trivially_copyable_v<FlagsType>)
198 [[nodiscard]] FlagsType Flags() const noexcept { 194 [[nodiscard]] FlagsType Flags() const noexcept {
199 FlagsType ret; 195 FlagsType ret;
200 std::memcpy(reinterpret_cast<char*>(&ret), &flags, sizeof(ret)); 196 std::memcpy(reinterpret_cast<char*>(&ret), &flags, sizeof(ret));
201 return ret; 197 return ret;
202 } 198 }
203 199
204 template <typename FlagsType> 200 template <typename FlagsType>
205 requires(sizeof(FlagsType) <= sizeof(u32) && 201 requires(sizeof(FlagsType) <= sizeof(u32) && std::is_trivially_copyable_v<FlagsType>)
206 std::is_trivially_copyable_v<FlagsType>) void SetFlags(FlagsType value) noexcept { 202 void SetFlags(FlagsType value) noexcept {
207 std::memcpy(&flags, &value, sizeof(value)); 203 std::memcpy(&flags, &value, sizeof(value));
208 } 204 }
209 205
@@ -268,7 +264,6 @@ using U8 = TypedValue<Type::U8>;
268using U16 = TypedValue<Type::U16>; 264using U16 = TypedValue<Type::U16>;
269using U32 = TypedValue<Type::U32>; 265using U32 = TypedValue<Type::U32>;
270using U64 = TypedValue<Type::U64>; 266using U64 = TypedValue<Type::U64>;
271using S32 = TypedValue<Type::S32>;
272using F16 = TypedValue<Type::F16>; 267using F16 = TypedValue<Type::F16>;
273using F32 = TypedValue<Type::F32>; 268using F32 = TypedValue<Type::F32>;
274using F64 = TypedValue<Type::F64>; 269using F64 = TypedValue<Type::F64>;
@@ -380,14 +375,6 @@ inline u32 Value::U32() const {
380 return imm_u32; 375 return imm_u32;
381} 376}
382 377
383inline s32 Value::S32() const {
384 if (IsIdentity()) {
385 return inst->Arg(0).S32();
386 }
387 DEBUG_ASSERT(type == Type::S32);
388 return imm_s32;
389}
390
391inline f32 Value::F32() const { 378inline f32 Value::F32() const {
392 if (IsIdentity()) { 379 if (IsIdentity()) {
393 return inst->Arg(0).F32(); 380 return inst->Arg(0).F32();
diff --git a/src/shader_recompiler/frontend/maxwell/translate/impl/texture_query.cpp b/src/shader_recompiler/frontend/maxwell/translate/impl/texture_query.cpp
index f8cfd4ab6..39af62559 100644
--- a/src/shader_recompiler/frontend/maxwell/translate/impl/texture_query.cpp
+++ b/src/shader_recompiler/frontend/maxwell/translate/impl/texture_query.cpp
@@ -15,11 +15,13 @@ enum class Mode : u64 {
15 SamplePos = 5, 15 SamplePos = 5,
16}; 16};
17 17
18IR::Value Query(TranslatorVisitor& v, const IR::U32& handle, Mode mode, IR::Reg src_reg) { 18IR::Value Query(TranslatorVisitor& v, const IR::U32& handle, Mode mode, IR::Reg src_reg, u64 mask) {
19 switch (mode) { 19 switch (mode) {
20 case Mode::Dimension: { 20 case Mode::Dimension: {
21 const bool needs_num_mips{((mask >> 3) & 1) != 0};
22 const IR::U1 skip_mips{v.ir.Imm1(!needs_num_mips)};
21 const IR::U32 lod{v.X(src_reg)}; 23 const IR::U32 lod{v.X(src_reg)};
22 return v.ir.ImageQueryDimension(handle, lod); 24 return v.ir.ImageQueryDimension(handle, lod, skip_mips);
23 } 25 }
24 case Mode::TextureType: 26 case Mode::TextureType:
25 case Mode::SamplePos: 27 case Mode::SamplePos:
@@ -46,7 +48,7 @@ void Impl(TranslatorVisitor& v, u64 insn, std::optional<u32> cbuf_offset) {
46 handle = v.X(src_reg); 48 handle = v.X(src_reg);
47 ++src_reg; 49 ++src_reg;
48 } 50 }
49 const IR::Value query{Query(v, handle, txq.mode, src_reg)}; 51 const IR::Value query{Query(v, handle, txq.mode, src_reg, txq.mask)};
50 IR::Reg dest_reg{txq.dest_reg}; 52 IR::Reg dest_reg{txq.dest_reg};
51 for (int element = 0; element < 4; ++element) { 53 for (int element = 0; element < 4; ++element) {
52 if (((txq.mask >> element) & 1) == 0) { 54 if (((txq.mask >> element) & 1) == 0) {
diff --git a/src/shader_recompiler/ir_opt/texture_pass.cpp b/src/shader_recompiler/ir_opt/texture_pass.cpp
index f5c86fcb1..d374c976a 100644
--- a/src/shader_recompiler/ir_opt/texture_pass.cpp
+++ b/src/shader_recompiler/ir_opt/texture_pass.cpp
@@ -355,21 +355,21 @@ TextureInst MakeInst(Environment& env, IR::Block* block, IR::Inst& inst) {
355 }; 355 };
356} 356}
357 357
358TextureType ReadTextureType(Environment& env, const ConstBufferAddr& cbuf) { 358u32 GetTextureHandle(Environment& env, const ConstBufferAddr& cbuf) {
359 const u32 secondary_index{cbuf.has_secondary ? cbuf.secondary_index : cbuf.index}; 359 const u32 secondary_index{cbuf.has_secondary ? cbuf.secondary_index : cbuf.index};
360 const u32 secondary_offset{cbuf.has_secondary ? cbuf.secondary_offset : cbuf.offset}; 360 const u32 secondary_offset{cbuf.has_secondary ? cbuf.secondary_offset : cbuf.offset};
361 const u32 lhs_raw{env.ReadCbufValue(cbuf.index, cbuf.offset) << cbuf.shift_left}; 361 const u32 lhs_raw{env.ReadCbufValue(cbuf.index, cbuf.offset) << cbuf.shift_left};
362 const u32 rhs_raw{env.ReadCbufValue(secondary_index, secondary_offset) 362 const u32 rhs_raw{env.ReadCbufValue(secondary_index, secondary_offset)
363 << cbuf.secondary_shift_left}; 363 << cbuf.secondary_shift_left};
364 return env.ReadTextureType(lhs_raw | rhs_raw); 364 return lhs_raw | rhs_raw;
365}
366
367TextureType ReadTextureType(Environment& env, const ConstBufferAddr& cbuf) {
368 return env.ReadTextureType(GetTextureHandle(env, cbuf));
365} 369}
366 370
367TexturePixelFormat ReadTexturePixelFormat(Environment& env, const ConstBufferAddr& cbuf) { 371TexturePixelFormat ReadTexturePixelFormat(Environment& env, const ConstBufferAddr& cbuf) {
368 const u32 secondary_index{cbuf.has_secondary ? cbuf.secondary_index : cbuf.index}; 372 return env.ReadTexturePixelFormat(GetTextureHandle(env, cbuf));
369 const u32 secondary_offset{cbuf.has_secondary ? cbuf.secondary_offset : cbuf.offset};
370 const u32 lhs_raw{env.ReadCbufValue(cbuf.index, cbuf.offset)};
371 const u32 rhs_raw{env.ReadCbufValue(secondary_index, secondary_offset)};
372 return env.ReadTexturePixelFormat(lhs_raw | rhs_raw);
373} 373}
374 374
375class Descriptors { 375class Descriptors {
@@ -386,8 +386,10 @@ public:
386 return Add(texture_buffer_descriptors, desc, [&desc](const auto& existing) { 386 return Add(texture_buffer_descriptors, desc, [&desc](const auto& existing) {
387 return desc.cbuf_index == existing.cbuf_index && 387 return desc.cbuf_index == existing.cbuf_index &&
388 desc.cbuf_offset == existing.cbuf_offset && 388 desc.cbuf_offset == existing.cbuf_offset &&
389 desc.shift_left == existing.shift_left &&
389 desc.secondary_cbuf_index == existing.secondary_cbuf_index && 390 desc.secondary_cbuf_index == existing.secondary_cbuf_index &&
390 desc.secondary_cbuf_offset == existing.secondary_cbuf_offset && 391 desc.secondary_cbuf_offset == existing.secondary_cbuf_offset &&
392 desc.secondary_shift_left == existing.secondary_shift_left &&
391 desc.count == existing.count && desc.size_shift == existing.size_shift && 393 desc.count == existing.count && desc.size_shift == existing.size_shift &&
392 desc.has_secondary == existing.has_secondary; 394 desc.has_secondary == existing.has_secondary;
393 }); 395 });
@@ -405,15 +407,20 @@ public:
405 } 407 }
406 408
407 u32 Add(const TextureDescriptor& desc) { 409 u32 Add(const TextureDescriptor& desc) {
408 return Add(texture_descriptors, desc, [&desc](const auto& existing) { 410 const u32 index{Add(texture_descriptors, desc, [&desc](const auto& existing) {
409 return desc.type == existing.type && desc.is_depth == existing.is_depth && 411 return desc.type == existing.type && desc.is_depth == existing.is_depth &&
410 desc.has_secondary == existing.has_secondary && 412 desc.has_secondary == existing.has_secondary &&
411 desc.cbuf_index == existing.cbuf_index && 413 desc.cbuf_index == existing.cbuf_index &&
412 desc.cbuf_offset == existing.cbuf_offset && 414 desc.cbuf_offset == existing.cbuf_offset &&
415 desc.shift_left == existing.shift_left &&
413 desc.secondary_cbuf_index == existing.secondary_cbuf_index && 416 desc.secondary_cbuf_index == existing.secondary_cbuf_index &&
414 desc.secondary_cbuf_offset == existing.secondary_cbuf_offset && 417 desc.secondary_cbuf_offset == existing.secondary_cbuf_offset &&
418 desc.secondary_shift_left == existing.secondary_shift_left &&
415 desc.count == existing.count && desc.size_shift == existing.size_shift; 419 desc.count == existing.count && desc.size_shift == existing.size_shift;
416 }); 420 })};
421 // TODO: Read this from TIC
422 texture_descriptors[index].is_multisample |= desc.is_multisample;
423 return index;
417 } 424 }
418 425
419 u32 Add(const ImageDescriptor& desc) { 426 u32 Add(const ImageDescriptor& desc) {
@@ -452,7 +459,8 @@ void PatchImageSampleImplicitLod(IR::Block& block, IR::Inst& inst) {
452 const IR::Value coord(inst.Arg(1)); 459 const IR::Value coord(inst.Arg(1));
453 const IR::Value handle(ir.Imm32(0)); 460 const IR::Value handle(ir.Imm32(0));
454 const IR::U32 lod{ir.Imm32(0)}; 461 const IR::U32 lod{ir.Imm32(0)};
455 const IR::Value texture_size = ir.ImageQueryDimension(handle, lod, info); 462 const IR::U1 skip_mips{ir.Imm1(true)};
463 const IR::Value texture_size = ir.ImageQueryDimension(handle, lod, skip_mips, info);
456 inst.SetArg( 464 inst.SetArg(
457 1, ir.CompositeConstruct( 465 1, ir.CompositeConstruct(
458 ir.FPMul(IR::F32(ir.CompositeExtract(coord, 0)), 466 ir.FPMul(IR::F32(ir.CompositeExtract(coord, 0)),
@@ -486,10 +494,10 @@ void PatchTexelFetch(IR::Block& block, IR::Inst& inst, TexturePixelFormat pixel_
486 const IR::F32 w(ir.CompositeExtract(new_inst, 3)); 494 const IR::F32 w(ir.CompositeExtract(new_inst, 3));
487 const IR::F16F32F64 max_value(ir.Imm32(get_max_value())); 495 const IR::F16F32F64 max_value(ir.Imm32(get_max_value()));
488 const IR::Value converted = 496 const IR::Value converted =
489 ir.CompositeConstruct(ir.FPMul(ir.ConvertSToF(32, 32, ir.BitCast<IR::S32>(x)), max_value), 497 ir.CompositeConstruct(ir.FPMul(ir.ConvertSToF(32, 32, ir.BitCast<IR::U32>(x)), max_value),
490 ir.FPMul(ir.ConvertSToF(32, 32, ir.BitCast<IR::S32>(y)), max_value), 498 ir.FPMul(ir.ConvertSToF(32, 32, ir.BitCast<IR::U32>(y)), max_value),
491 ir.FPMul(ir.ConvertSToF(32, 32, ir.BitCast<IR::S32>(z)), max_value), 499 ir.FPMul(ir.ConvertSToF(32, 32, ir.BitCast<IR::U32>(z)), max_value),
492 ir.FPMul(ir.ConvertSToF(32, 32, ir.BitCast<IR::S32>(w)), max_value)); 500 ir.FPMul(ir.ConvertSToF(32, 32, ir.BitCast<IR::U32>(w)), max_value));
493 inst.ReplaceUsesWith(converted); 501 inst.ReplaceUsesWith(converted);
494} 502}
495} // Anonymous namespace 503} // Anonymous namespace
@@ -524,6 +532,7 @@ void TexturePass(Environment& env, IR::Program& program, const HostTranslateInfo
524 532
525 const auto& cbuf{texture_inst.cbuf}; 533 const auto& cbuf{texture_inst.cbuf};
526 auto flags{inst->Flags<IR::TextureInstInfo>()}; 534 auto flags{inst->Flags<IR::TextureInstInfo>()};
535 bool is_multisample{false};
527 switch (inst->GetOpcode()) { 536 switch (inst->GetOpcode()) {
528 case IR::Opcode::ImageQueryDimensions: 537 case IR::Opcode::ImageQueryDimensions:
529 flags.type.Assign(ReadTextureType(env, cbuf)); 538 flags.type.Assign(ReadTextureType(env, cbuf));
@@ -538,6 +547,12 @@ void TexturePass(Environment& env, IR::Program& program, const HostTranslateInfo
538 } 547 }
539 break; 548 break;
540 case IR::Opcode::ImageFetch: 549 case IR::Opcode::ImageFetch:
550 if (flags.type == TextureType::Color2D || flags.type == TextureType::Color2DRect ||
551 flags.type == TextureType::ColorArray2D) {
552 is_multisample = !inst->Arg(4).IsEmpty();
553 } else {
554 inst->SetArg(4, IR::U32{});
555 }
541 if (flags.type != TextureType::Color1D) { 556 if (flags.type != TextureType::Color1D) {
542 break; 557 break;
543 } 558 }
@@ -613,6 +628,7 @@ void TexturePass(Environment& env, IR::Program& program, const HostTranslateInfo
613 index = descriptors.Add(TextureDescriptor{ 628 index = descriptors.Add(TextureDescriptor{
614 .type = flags.type, 629 .type = flags.type,
615 .is_depth = flags.is_depth != 0, 630 .is_depth = flags.is_depth != 0,
631 .is_multisample = is_multisample,
616 .has_secondary = cbuf.has_secondary, 632 .has_secondary = cbuf.has_secondary,
617 .cbuf_index = cbuf.index, 633 .cbuf_index = cbuf.index,
618 .cbuf_offset = cbuf.offset, 634 .cbuf_offset = cbuf.offset,
diff --git a/src/shader_recompiler/object_pool.h b/src/shader_recompiler/object_pool.h
index 2b42c4ba2..5d648b159 100644
--- a/src/shader_recompiler/object_pool.h
+++ b/src/shader_recompiler/object_pool.h
@@ -10,7 +10,7 @@
10namespace Shader { 10namespace Shader {
11 11
12template <typename T> 12template <typename T>
13requires std::is_destructible_v<T> 13 requires std::is_destructible_v<T>
14class ObjectPool { 14class ObjectPool {
15public: 15public:
16 explicit ObjectPool(size_t chunk_size = 8192) : new_chunk_size{chunk_size} { 16 explicit ObjectPool(size_t chunk_size = 8192) : new_chunk_size{chunk_size} {
@@ -18,7 +18,7 @@ public:
18 } 18 }
19 19
20 template <typename... Args> 20 template <typename... Args>
21 requires std::is_constructible_v<T, Args...> 21 requires std::is_constructible_v<T, Args...>
22 [[nodiscard]] T* Create(Args&&... args) { 22 [[nodiscard]] T* Create(Args&&... args) {
23 return std::construct_at(Memory(), std::forward<Args>(args)...); 23 return std::construct_at(Memory(), std::forward<Args>(args)...);
24 } 24 }
diff --git a/src/shader_recompiler/shader_info.h b/src/shader_recompiler/shader_info.h
index f93181e1e..d308db942 100644
--- a/src/shader_recompiler/shader_info.h
+++ b/src/shader_recompiler/shader_info.h
@@ -109,6 +109,7 @@ using ImageBufferDescriptors = boost::container::small_vector<ImageBufferDescrip
109struct TextureDescriptor { 109struct TextureDescriptor {
110 TextureType type; 110 TextureType type;
111 bool is_depth; 111 bool is_depth;
112 bool is_multisample;
112 bool has_secondary; 113 bool has_secondary;
113 u32 cbuf_index; 114 u32 cbuf_index;
114 u32 cbuf_offset; 115 u32 cbuf_offset;
diff --git a/src/tests/CMakeLists.txt b/src/tests/CMakeLists.txt
index 9b65e79cb..ae84408bc 100644
--- a/src/tests/CMakeLists.txt
+++ b/src/tests/CMakeLists.txt
@@ -14,7 +14,6 @@ add_executable(tests
14 core/core_timing.cpp 14 core/core_timing.cpp
15 core/internal_network/network.cpp 15 core/internal_network/network.cpp
16 precompiled_headers.h 16 precompiled_headers.h
17 tests.cpp
18 video_core/buffer_base.cpp 17 video_core/buffer_base.cpp
19 input_common/calibration_configuration_job.cpp 18 input_common/calibration_configuration_job.cpp
20) 19)
@@ -22,7 +21,7 @@ add_executable(tests
22create_target_directory_groups(tests) 21create_target_directory_groups(tests)
23 22
24target_link_libraries(tests PRIVATE common core input_common) 23target_link_libraries(tests PRIVATE common core input_common)
25target_link_libraries(tests PRIVATE ${PLATFORM_LIBRARIES} Catch2::Catch2 Threads::Threads) 24target_link_libraries(tests PRIVATE ${PLATFORM_LIBRARIES} Catch2::Catch2WithMain Threads::Threads)
26 25
27add_test(NAME tests COMMAND tests) 26add_test(NAME tests COMMAND tests)
28 27
diff --git a/src/tests/common/bit_field.cpp b/src/tests/common/bit_field.cpp
index 0071ae52e..75e990ecd 100644
--- a/src/tests/common/bit_field.cpp
+++ b/src/tests/common/bit_field.cpp
@@ -4,7 +4,7 @@
4#include <array> 4#include <array>
5#include <cstring> 5#include <cstring>
6#include <type_traits> 6#include <type_traits>
7#include <catch2/catch.hpp> 7#include <catch2/catch_test_macros.hpp>
8#include "common/bit_field.h" 8#include "common/bit_field.h"
9 9
10TEST_CASE("BitField", "[common]") { 10TEST_CASE("BitField", "[common]") {
diff --git a/src/tests/common/cityhash.cpp b/src/tests/common/cityhash.cpp
index 05942eadb..2a391dff1 100644
--- a/src/tests/common/cityhash.cpp
+++ b/src/tests/common/cityhash.cpp
@@ -1,7 +1,7 @@
1// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project 1// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later 2// SPDX-License-Identifier: GPL-2.0-or-later
3 3
4#include <catch2/catch.hpp> 4#include <catch2/catch_test_macros.hpp>
5 5
6#include "common/cityhash.h" 6#include "common/cityhash.h"
7 7
diff --git a/src/tests/common/fibers.cpp b/src/tests/common/fibers.cpp
index 4e29f9199..ecad7583f 100644
--- a/src/tests/common/fibers.cpp
+++ b/src/tests/common/fibers.cpp
@@ -11,7 +11,7 @@
11#include <unordered_map> 11#include <unordered_map>
12#include <vector> 12#include <vector>
13 13
14#include <catch2/catch.hpp> 14#include <catch2/catch_test_macros.hpp>
15 15
16#include "common/common_types.h" 16#include "common/common_types.h"
17#include "common/fiber.h" 17#include "common/fiber.h"
diff --git a/src/tests/common/host_memory.cpp b/src/tests/common/host_memory.cpp
index e49d0a09f..1b014b632 100644
--- a/src/tests/common/host_memory.cpp
+++ b/src/tests/common/host_memory.cpp
@@ -1,7 +1,7 @@
1// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project 1// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later 2// SPDX-License-Identifier: GPL-2.0-or-later
3 3
4#include <catch2/catch.hpp> 4#include <catch2/catch_test_macros.hpp>
5 5
6#include "common/host_memory.h" 6#include "common/host_memory.h"
7#include "common/literals.h" 7#include "common/literals.h"
diff --git a/src/tests/common/param_package.cpp b/src/tests/common/param_package.cpp
index d036cc83a..41575def4 100644
--- a/src/tests/common/param_package.cpp
+++ b/src/tests/common/param_package.cpp
@@ -1,7 +1,7 @@
1// SPDX-FileCopyrightText: 2017 Citra Emulator Project 1// SPDX-FileCopyrightText: 2017 Citra Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later 2// SPDX-License-Identifier: GPL-2.0-or-later
3 3
4#include <catch2/catch.hpp> 4#include <catch2/catch_test_macros.hpp>
5#include <math.h> 5#include <math.h>
6#include "common/logging/backend.h" 6#include "common/logging/backend.h"
7#include "common/param_package.h" 7#include "common/param_package.h"
diff --git a/src/tests/common/range_map.cpp b/src/tests/common/range_map.cpp
index 5a4630a38..d301ac5f6 100644
--- a/src/tests/common/range_map.cpp
+++ b/src/tests/common/range_map.cpp
@@ -3,7 +3,7 @@
3 3
4#include <stdexcept> 4#include <stdexcept>
5 5
6#include <catch2/catch.hpp> 6#include <catch2/catch_test_macros.hpp>
7 7
8#include "common/range_map.h" 8#include "common/range_map.h"
9 9
diff --git a/src/tests/common/ring_buffer.cpp b/src/tests/common/ring_buffer.cpp
index 4f81b6e5e..7dee988c8 100644
--- a/src/tests/common/ring_buffer.cpp
+++ b/src/tests/common/ring_buffer.cpp
@@ -7,7 +7,7 @@
7#include <numeric> 7#include <numeric>
8#include <thread> 8#include <thread>
9#include <vector> 9#include <vector>
10#include <catch2/catch.hpp> 10#include <catch2/catch_test_macros.hpp>
11#include "common/ring_buffer.h" 11#include "common/ring_buffer.h"
12 12
13namespace Common { 13namespace Common {
diff --git a/src/tests/common/scratch_buffer.cpp b/src/tests/common/scratch_buffer.cpp
index f6e50da4a..132f139fa 100644
--- a/src/tests/common/scratch_buffer.cpp
+++ b/src/tests/common/scratch_buffer.cpp
@@ -5,7 +5,7 @@
5#include <array> 5#include <array>
6#include <cstring> 6#include <cstring>
7#include <span> 7#include <span>
8#include <catch2/catch.hpp> 8#include <catch2/catch_test_macros.hpp>
9#include "common/common_types.h" 9#include "common/common_types.h"
10#include "common/scratch_buffer.h" 10#include "common/scratch_buffer.h"
11 11
diff --git a/src/tests/common/unique_function.cpp b/src/tests/common/unique_function.cpp
index 311272506..f7a23e876 100644
--- a/src/tests/common/unique_function.cpp
+++ b/src/tests/common/unique_function.cpp
@@ -3,7 +3,7 @@
3 3
4#include <string> 4#include <string>
5 5
6#include <catch2/catch.hpp> 6#include <catch2/catch_test_macros.hpp>
7 7
8#include "common/unique_function.h" 8#include "common/unique_function.h"
9 9
diff --git a/src/tests/core/core_timing.cpp b/src/tests/core/core_timing.cpp
index 284b2ae66..f08afbf9a 100644
--- a/src/tests/core/core_timing.cpp
+++ b/src/tests/core/core_timing.cpp
@@ -1,7 +1,7 @@
1// SPDX-FileCopyrightText: 2016 Dolphin Emulator Project 1// SPDX-FileCopyrightText: 2016 Dolphin Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later 2// SPDX-License-Identifier: GPL-2.0-or-later
3 3
4#include <catch2/catch.hpp> 4#include <catch2/catch_test_macros.hpp>
5 5
6#include <array> 6#include <array>
7#include <bitset> 7#include <bitset>
diff --git a/src/tests/core/internal_network/network.cpp b/src/tests/core/internal_network/network.cpp
index 164b0ff24..10ddd8b42 100644
--- a/src/tests/core/internal_network/network.cpp
+++ b/src/tests/core/internal_network/network.cpp
@@ -1,7 +1,7 @@
1// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project 1// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later 2// SPDX-License-Identifier: GPL-2.0-or-later
3 3
4#include <catch2/catch.hpp> 4#include <catch2/catch_test_macros.hpp>
5 5
6#include "core/internal_network/network.h" 6#include "core/internal_network/network.h"
7#include "core/internal_network/sockets.h" 7#include "core/internal_network/sockets.h"
diff --git a/src/tests/input_common/calibration_configuration_job.cpp b/src/tests/input_common/calibration_configuration_job.cpp
index e5f698886..516ff1b30 100644
--- a/src/tests/input_common/calibration_configuration_job.cpp
+++ b/src/tests/input_common/calibration_configuration_job.cpp
@@ -6,7 +6,7 @@
6#include <thread> 6#include <thread>
7#include <boost/asio.hpp> 7#include <boost/asio.hpp>
8#include <boost/crc.hpp> 8#include <boost/crc.hpp>
9#include <catch2/catch.hpp> 9#include <catch2/catch_test_macros.hpp>
10 10
11#include "input_common/drivers/udp_client.h" 11#include "input_common/drivers/udp_client.h"
12#include "input_common/helpers/udp_protocol.h" 12#include "input_common/helpers/udp_protocol.h"
diff --git a/src/tests/tests.cpp b/src/tests/tests.cpp
deleted file mode 100644
index 3f905c05c..000000000
--- a/src/tests/tests.cpp
+++ /dev/null
@@ -1,8 +0,0 @@
1// SPDX-FileCopyrightText: 2016 Citra Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#define CATCH_CONFIG_MAIN
5#include <catch2/catch.hpp>
6
7// Catch provides the main function since we've given it the
8// CATCH_CONFIG_MAIN preprocessor directive.
diff --git a/src/tests/video_core/buffer_base.cpp b/src/tests/video_core/buffer_base.cpp
index 5cd0628f2..1275cca24 100644
--- a/src/tests/video_core/buffer_base.cpp
+++ b/src/tests/video_core/buffer_base.cpp
@@ -4,7 +4,7 @@
4#include <stdexcept> 4#include <stdexcept>
5#include <unordered_map> 5#include <unordered_map>
6 6
7#include <catch2/catch.hpp> 7#include <catch2/catch_test_macros.hpp>
8 8
9#include "common/alignment.h" 9#include "common/alignment.h"
10#include "common/common_types.h" 10#include "common/common_types.h"
diff --git a/src/video_core/CMakeLists.txt b/src/video_core/CMakeLists.txt
index f617665de..4742bcbe9 100644
--- a/src/video_core/CMakeLists.txt
+++ b/src/video_core/CMakeLists.txt
@@ -52,6 +52,8 @@ add_library(video_core STATIC
52 engines/puller.cpp 52 engines/puller.cpp
53 engines/puller.h 53 engines/puller.h
54 framebuffer_config.h 54 framebuffer_config.h
55 fsr.cpp
56 fsr.h
55 host1x/codecs/codec.cpp 57 host1x/codecs/codec.cpp
56 host1x/codecs/codec.h 58 host1x/codecs/codec.h
57 host1x/codecs/h264.cpp 59 host1x/codecs/h264.cpp
@@ -100,6 +102,8 @@ add_library(video_core STATIC
100 renderer_null/null_rasterizer.h 102 renderer_null/null_rasterizer.h
101 renderer_null/renderer_null.cpp 103 renderer_null/renderer_null.cpp
102 renderer_null/renderer_null.h 104 renderer_null/renderer_null.h
105 renderer_opengl/blit_image.cpp
106 renderer_opengl/blit_image.h
103 renderer_opengl/gl_buffer_cache.cpp 107 renderer_opengl/gl_buffer_cache.cpp
104 renderer_opengl/gl_buffer_cache.h 108 renderer_opengl/gl_buffer_cache.h
105 renderer_opengl/gl_compute_pipeline.cpp 109 renderer_opengl/gl_compute_pipeline.cpp
@@ -108,6 +112,8 @@ add_library(video_core STATIC
108 renderer_opengl/gl_device.h 112 renderer_opengl/gl_device.h
109 renderer_opengl/gl_fence_manager.cpp 113 renderer_opengl/gl_fence_manager.cpp
110 renderer_opengl/gl_fence_manager.h 114 renderer_opengl/gl_fence_manager.h
115 renderer_opengl/gl_fsr.cpp
116 renderer_opengl/gl_fsr.h
111 renderer_opengl/gl_graphics_pipeline.cpp 117 renderer_opengl/gl_graphics_pipeline.cpp
112 renderer_opengl/gl_graphics_pipeline.h 118 renderer_opengl/gl_graphics_pipeline.h
113 renderer_opengl/gl_rasterizer.cpp 119 renderer_opengl/gl_rasterizer.cpp
diff --git a/src/video_core/engines/draw_manager.cpp b/src/video_core/engines/draw_manager.cpp
index 2437121ce..1d22d25f1 100644
--- a/src/video_core/engines/draw_manager.cpp
+++ b/src/video_core/engines/draw_manager.cpp
@@ -51,6 +51,10 @@ void DrawManager::ProcessMethodCall(u32 method, u32 argument) {
51 LOG_WARNING(HW_GPU, "(STUBBED) called"); 51 LOG_WARNING(HW_GPU, "(STUBBED) called");
52 break; 52 break;
53 } 53 }
54 case MAXWELL3D_REG_INDEX(draw_texture.src_y0): {
55 DrawTexture();
56 break;
57 }
54 default: 58 default:
55 break; 59 break;
56 } 60 }
@@ -179,6 +183,33 @@ void DrawManager::DrawIndexSmall(u32 argument) {
179 ProcessDraw(true, 1); 183 ProcessDraw(true, 1);
180} 184}
181 185
186void DrawManager::DrawTexture() {
187 const auto& regs{maxwell3d->regs};
188 draw_texture_state.dst_x0 = static_cast<float>(regs.draw_texture.dst_x0) / 4096.f;
189 draw_texture_state.dst_y0 = static_cast<float>(regs.draw_texture.dst_y0) / 4096.f;
190 const auto dst_width = static_cast<float>(regs.draw_texture.dst_width) / 4096.f;
191 const auto dst_height = static_cast<float>(regs.draw_texture.dst_height) / 4096.f;
192 const bool lower_left{regs.window_origin.mode !=
193 Maxwell3D::Regs::WindowOrigin::Mode::UpperLeft};
194 if (lower_left) {
195 draw_texture_state.dst_y0 -= dst_height;
196 }
197 draw_texture_state.dst_x1 = draw_texture_state.dst_x0 + dst_width;
198 draw_texture_state.dst_y1 = draw_texture_state.dst_y0 + dst_height;
199 draw_texture_state.src_x0 = static_cast<float>(regs.draw_texture.src_x0) / 4096.f;
200 draw_texture_state.src_y0 = static_cast<float>(regs.draw_texture.src_y0) / 4096.f;
201 draw_texture_state.src_x1 =
202 (static_cast<float>(regs.draw_texture.dx_du) / 4294967296.f) * dst_width +
203 draw_texture_state.src_x0;
204 draw_texture_state.src_y1 =
205 (static_cast<float>(regs.draw_texture.dy_dv) / 4294967296.f) * dst_height +
206 draw_texture_state.src_y0;
207 draw_texture_state.src_sampler = regs.draw_texture.src_sampler;
208 draw_texture_state.src_texture = regs.draw_texture.src_texture;
209
210 maxwell3d->rasterizer->DrawTexture();
211}
212
182void DrawManager::UpdateTopology() { 213void DrawManager::UpdateTopology() {
183 const auto& regs{maxwell3d->regs}; 214 const auto& regs{maxwell3d->regs};
184 switch (regs.primitive_topology_control) { 215 switch (regs.primitive_topology_control) {
diff --git a/src/video_core/engines/draw_manager.h b/src/video_core/engines/draw_manager.h
index 58d1b2d59..7c22c49f1 100644
--- a/src/video_core/engines/draw_manager.h
+++ b/src/video_core/engines/draw_manager.h
@@ -32,6 +32,19 @@ public:
32 std::vector<u8> inline_index_draw_indexes; 32 std::vector<u8> inline_index_draw_indexes;
33 }; 33 };
34 34
35 struct DrawTextureState {
36 f32 dst_x0;
37 f32 dst_y0;
38 f32 dst_x1;
39 f32 dst_y1;
40 f32 src_x0;
41 f32 src_y0;
42 f32 src_x1;
43 f32 src_y1;
44 u32 src_sampler;
45 u32 src_texture;
46 };
47
35 struct IndirectParams { 48 struct IndirectParams {
36 bool is_indexed; 49 bool is_indexed;
37 bool include_count; 50 bool include_count;
@@ -64,6 +77,10 @@ public:
64 return draw_state; 77 return draw_state;
65 } 78 }
66 79
80 const DrawTextureState& GetDrawTextureState() const {
81 return draw_texture_state;
82 }
83
67 IndirectParams& GetIndirectParams() { 84 IndirectParams& GetIndirectParams() {
68 return indirect_state; 85 return indirect_state;
69 } 86 }
@@ -81,6 +98,8 @@ private:
81 98
82 void DrawIndexSmall(u32 argument); 99 void DrawIndexSmall(u32 argument);
83 100
101 void DrawTexture();
102
84 void UpdateTopology(); 103 void UpdateTopology();
85 104
86 void ProcessDraw(bool draw_indexed, u32 instance_count); 105 void ProcessDraw(bool draw_indexed, u32 instance_count);
@@ -89,6 +108,7 @@ private:
89 108
90 Maxwell3D* maxwell3d{}; 109 Maxwell3D* maxwell3d{};
91 State draw_state{}; 110 State draw_state{};
111 DrawTextureState draw_texture_state{};
92 IndirectParams indirect_state{}; 112 IndirectParams indirect_state{};
93}; 113};
94} // namespace Tegra::Engines 114} // namespace Tegra::Engines
diff --git a/src/video_core/engines/maxwell_3d.cpp b/src/video_core/engines/maxwell_3d.cpp
index 68ceda519..ae9da6290 100644
--- a/src/video_core/engines/maxwell_3d.cpp
+++ b/src/video_core/engines/maxwell_3d.cpp
@@ -149,6 +149,7 @@ bool Maxwell3D::IsMethodExecutable(u32 method) {
149 case MAXWELL3D_REG_INDEX(inline_index_4x8.index0): 149 case MAXWELL3D_REG_INDEX(inline_index_4x8.index0):
150 case MAXWELL3D_REG_INDEX(vertex_array_instance_first): 150 case MAXWELL3D_REG_INDEX(vertex_array_instance_first):
151 case MAXWELL3D_REG_INDEX(vertex_array_instance_subsequent): 151 case MAXWELL3D_REG_INDEX(vertex_array_instance_subsequent):
152 case MAXWELL3D_REG_INDEX(draw_texture.src_y0):
152 case MAXWELL3D_REG_INDEX(wait_for_idle): 153 case MAXWELL3D_REG_INDEX(wait_for_idle):
153 case MAXWELL3D_REG_INDEX(shadow_ram_control): 154 case MAXWELL3D_REG_INDEX(shadow_ram_control):
154 case MAXWELL3D_REG_INDEX(load_mme.instruction_ptr): 155 case MAXWELL3D_REG_INDEX(load_mme.instruction_ptr):
diff --git a/src/video_core/engines/maxwell_3d.h b/src/video_core/engines/maxwell_3d.h
index 0b2fd2928..c89969bb4 100644
--- a/src/video_core/engines/maxwell_3d.h
+++ b/src/video_core/engines/maxwell_3d.h
@@ -1599,6 +1599,20 @@ public:
1599 }; 1599 };
1600 static_assert(sizeof(TIRModulationCoeff) == 0x4); 1600 static_assert(sizeof(TIRModulationCoeff) == 0x4);
1601 1601
1602 struct DrawTexture {
1603 s32 dst_x0;
1604 s32 dst_y0;
1605 s32 dst_width;
1606 s32 dst_height;
1607 s64 dx_du;
1608 s64 dy_dv;
1609 u32 src_sampler;
1610 u32 src_texture;
1611 s32 src_x0;
1612 s32 src_y0;
1613 };
1614 static_assert(sizeof(DrawTexture) == 0x30);
1615
1602 struct ReduceColorThreshold { 1616 struct ReduceColorThreshold {
1603 union { 1617 union {
1604 BitField<0, 8, u32> all_hit_once; 1618 BitField<0, 8, u32> all_hit_once;
@@ -2751,7 +2765,7 @@ public:
2751 u32 reserved_sw_method2; ///< 0x102C 2765 u32 reserved_sw_method2; ///< 0x102C
2752 std::array<TIRModulationCoeff, 5> tir_modulation_coeff; ///< 0x1030 2766 std::array<TIRModulationCoeff, 5> tir_modulation_coeff; ///< 0x1030
2753 std::array<u32, 15> spare_nop; ///< 0x1044 2767 std::array<u32, 15> spare_nop; ///< 0x1044
2754 INSERT_PADDING_BYTES_NOINIT(0x30); 2768 DrawTexture draw_texture; ///< 0x1080
2755 std::array<u32, 7> reserved_sw_method3_to_7; ///< 0x10B0 2769 std::array<u32, 7> reserved_sw_method3_to_7; ///< 0x10B0
2756 ReduceColorThreshold reduce_color_thresholds_unorm8; ///< 0x10CC 2770 ReduceColorThreshold reduce_color_thresholds_unorm8; ///< 0x10CC
2757 std::array<u32, 4> reserved_sw_method10_to_13; ///< 0x10D0 2771 std::array<u32, 4> reserved_sw_method10_to_13; ///< 0x10D0
diff --git a/src/video_core/fsr.cpp b/src/video_core/fsr.cpp
new file mode 100644
index 000000000..5653c64fc
--- /dev/null
+++ b/src/video_core/fsr.cpp
@@ -0,0 +1,148 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include <cmath>
5#include "video_core/fsr.h"
6
7namespace FSR {
8namespace {
9// Reimplementations of the constant generating functions in ffx_fsr1.h
10// GCC generated a lot of warnings when using the official header.
11u32 AU1_AH1_AF1(f32 f) {
12 static constexpr u32 base[512]{
13 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
14 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
15 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
16 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
17 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
18 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
19 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
20 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
21 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
22 0x0000, 0x0000, 0x0000, 0x0000, 0x0001, 0x0002, 0x0004, 0x0008, 0x0010, 0x0020, 0x0040,
23 0x0080, 0x0100, 0x0200, 0x0400, 0x0800, 0x0c00, 0x1000, 0x1400, 0x1800, 0x1c00, 0x2000,
24 0x2400, 0x2800, 0x2c00, 0x3000, 0x3400, 0x3800, 0x3c00, 0x4000, 0x4400, 0x4800, 0x4c00,
25 0x5000, 0x5400, 0x5800, 0x5c00, 0x6000, 0x6400, 0x6800, 0x6c00, 0x7000, 0x7400, 0x7800,
26 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff,
27 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff,
28 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff,
29 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff,
30 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff,
31 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff,
32 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff,
33 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff,
34 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff,
35 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff,
36 0x7bff, 0x7bff, 0x7bff, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000,
37 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000,
38 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000,
39 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000,
40 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000,
41 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000,
42 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000,
43 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000,
44 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000,
45 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8001, 0x8002, 0x8004, 0x8008,
46 0x8010, 0x8020, 0x8040, 0x8080, 0x8100, 0x8200, 0x8400, 0x8800, 0x8c00, 0x9000, 0x9400,
47 0x9800, 0x9c00, 0xa000, 0xa400, 0xa800, 0xac00, 0xb000, 0xb400, 0xb800, 0xbc00, 0xc000,
48 0xc400, 0xc800, 0xcc00, 0xd000, 0xd400, 0xd800, 0xdc00, 0xe000, 0xe400, 0xe800, 0xec00,
49 0xf000, 0xf400, 0xf800, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff,
50 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff,
51 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff,
52 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff,
53 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff,
54 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff,
55 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff,
56 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff,
57 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff,
58 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff,
59 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff,
60 };
61 static constexpr s8 shift[512]{
62 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
63 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
64 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
65 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
66 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
67 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
68 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x17, 0x16,
69 0x15, 0x14, 0x13, 0x12, 0x11, 0x10, 0x0f, 0x0e, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d,
70 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d,
71 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
72 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
73 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
74 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
75 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
76 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
77 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
78 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
79 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
80 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
81 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
82 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
83 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
84 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
85 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x17,
86 0x16, 0x15, 0x14, 0x13, 0x12, 0x11, 0x10, 0x0f, 0x0e, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d,
87 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d,
88 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
89 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
90 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
91 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
92 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
93 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
94 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
95 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
96 0x18, 0x18,
97 };
98 const u32 u = Common::BitCast<u32>(f);
99 const u32 i = u >> 23;
100 return base[i] + ((u & 0x7fffff) >> shift[i]);
101}
102
103u32 AU1_AH2_AF2(f32 a[2]) {
104 return AU1_AH1_AF1(a[0]) + (AU1_AH1_AF1(a[1]) << 16);
105}
106
107void FsrEasuCon(u32 con0[4], u32 con1[4], u32 con2[4], u32 con3[4], f32 inputViewportInPixelsX,
108 f32 inputViewportInPixelsY, f32 inputSizeInPixelsX, f32 inputSizeInPixelsY,
109 f32 outputSizeInPixelsX, f32 outputSizeInPixelsY) {
110 con0[0] = Common::BitCast<u32>(inputViewportInPixelsX / outputSizeInPixelsX);
111 con0[1] = Common::BitCast<u32>(inputViewportInPixelsY / outputSizeInPixelsY);
112 con0[2] = Common::BitCast<u32>(0.5f * inputViewportInPixelsX / outputSizeInPixelsX - 0.5f);
113 con0[3] = Common::BitCast<u32>(0.5f * inputViewportInPixelsY / outputSizeInPixelsY - 0.5f);
114 con1[0] = Common::BitCast<u32>(1.0f / inputSizeInPixelsX);
115 con1[1] = Common::BitCast<u32>(1.0f / inputSizeInPixelsY);
116 con1[2] = Common::BitCast<u32>(1.0f / inputSizeInPixelsX);
117 con1[3] = Common::BitCast<u32>(-1.0f / inputSizeInPixelsY);
118 con2[0] = Common::BitCast<u32>(-1.0f / inputSizeInPixelsX);
119 con2[1] = Common::BitCast<u32>(2.0f / inputSizeInPixelsY);
120 con2[2] = Common::BitCast<u32>(1.0f / inputSizeInPixelsX);
121 con2[3] = Common::BitCast<u32>(2.0f / inputSizeInPixelsY);
122 con3[0] = Common::BitCast<u32>(0.0f / inputSizeInPixelsX);
123 con3[1] = Common::BitCast<u32>(4.0f / inputSizeInPixelsY);
124 con3[2] = con3[3] = 0;
125}
126} // Anonymous namespace
127
128void FsrEasuConOffset(u32 con0[4], u32 con1[4], u32 con2[4], u32 con3[4],
129 f32 inputViewportInPixelsX, f32 inputViewportInPixelsY,
130 f32 inputSizeInPixelsX, f32 inputSizeInPixelsY, f32 outputSizeInPixelsX,
131 f32 outputSizeInPixelsY, f32 inputOffsetInPixelsX, f32 inputOffsetInPixelsY) {
132 FsrEasuCon(con0, con1, con2, con3, inputViewportInPixelsX, inputViewportInPixelsY,
133 inputSizeInPixelsX, inputSizeInPixelsY, outputSizeInPixelsX, outputSizeInPixelsY);
134 con0[2] = Common::BitCast<u32>(0.5f * inputViewportInPixelsX / outputSizeInPixelsX - 0.5f +
135 inputOffsetInPixelsX);
136 con0[3] = Common::BitCast<u32>(0.5f * inputViewportInPixelsY / outputSizeInPixelsY - 0.5f +
137 inputOffsetInPixelsY);
138}
139
140void FsrRcasCon(u32* con, f32 sharpness) {
141 sharpness = std::exp2f(-sharpness);
142 f32 hSharp[2]{sharpness, sharpness};
143 con[0] = Common::BitCast<u32>(sharpness);
144 con[1] = AU1_AH2_AF2(hSharp);
145 con[2] = 0;
146 con[3] = 0;
147}
148} // namespace FSR
diff --git a/src/video_core/fsr.h b/src/video_core/fsr.h
new file mode 100644
index 000000000..db0d4ec6f
--- /dev/null
+++ b/src/video_core/fsr.h
@@ -0,0 +1,19 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include "common/bit_cast.h"
7#include "common/common_types.h"
8
9namespace FSR {
10// Reimplementations of the constant generating functions in ffx_fsr1.h
11// GCC generated a lot of warnings when using the official header.
12void FsrEasuConOffset(u32 con0[4], u32 con1[4], u32 con2[4], u32 con3[4],
13 f32 inputViewportInPixelsX, f32 inputViewportInPixelsY,
14 f32 inputSizeInPixelsX, f32 inputSizeInPixelsY, f32 outputSizeInPixelsX,
15 f32 outputSizeInPixelsY, f32 inputOffsetInPixelsX, f32 inputOffsetInPixelsY);
16
17void FsrRcasCon(u32* con, f32 sharpness);
18
19} // namespace FSR
diff --git a/src/video_core/host_shaders/CMakeLists.txt b/src/video_core/host_shaders/CMakeLists.txt
index f275b2aa9..52cd5bb81 100644
--- a/src/video_core/host_shaders/CMakeLists.txt
+++ b/src/video_core/host_shaders/CMakeLists.txt
@@ -3,14 +3,19 @@
3 3
4set(FIDELITYFX_INCLUDE_DIR ${CMAKE_SOURCE_DIR}/externals/FidelityFX-FSR/ffx-fsr) 4set(FIDELITYFX_INCLUDE_DIR ${CMAKE_SOURCE_DIR}/externals/FidelityFX-FSR/ffx-fsr)
5 5
6set(GLSL_INCLUDES 6set(FIDELITYFX_FILES
7 fidelityfx_fsr.comp
8 ${FIDELITYFX_INCLUDE_DIR}/ffx_a.h 7 ${FIDELITYFX_INCLUDE_DIR}/ffx_a.h
9 ${FIDELITYFX_INCLUDE_DIR}/ffx_fsr1.h 8 ${FIDELITYFX_INCLUDE_DIR}/ffx_fsr1.h
10) 9)
11 10
11set(GLSL_INCLUDES
12 fidelityfx_fsr.comp
13 ${FIDELITYFX_FILES}
14)
15
12set(SHADER_FILES 16set(SHADER_FILES
13 astc_decoder.comp 17 astc_decoder.comp
18 blit_color_float.frag
14 block_linear_unswizzle_2d.comp 19 block_linear_unswizzle_2d.comp
15 block_linear_unswizzle_3d.comp 20 block_linear_unswizzle_3d.comp
16 convert_abgr8_to_d24s8.frag 21 convert_abgr8_to_d24s8.frag
@@ -23,6 +28,9 @@ set(SHADER_FILES
23 fxaa.vert 28 fxaa.vert
24 opengl_convert_s8d24.comp 29 opengl_convert_s8d24.comp
25 opengl_copy_bc4.comp 30 opengl_copy_bc4.comp
31 opengl_fidelityfx_fsr.frag
32 opengl_fidelityfx_fsr_easu.frag
33 opengl_fidelityfx_fsr_rcas.frag
26 opengl_present.frag 34 opengl_present.frag
27 opengl_present.vert 35 opengl_present.vert
28 opengl_present_scaleforce.frag 36 opengl_present_scaleforce.frag
@@ -36,8 +44,9 @@ set(SHADER_FILES
36 smaa_blending_weight_calculation.frag 44 smaa_blending_weight_calculation.frag
37 smaa_neighborhood_blending.vert 45 smaa_neighborhood_blending.vert
38 smaa_neighborhood_blending.frag 46 smaa_neighborhood_blending.frag
39 vulkan_blit_color_float.frag
40 vulkan_blit_depth_stencil.frag 47 vulkan_blit_depth_stencil.frag
48 vulkan_color_clear.frag
49 vulkan_color_clear.vert
41 vulkan_fidelityfx_fsr_easu_fp16.comp 50 vulkan_fidelityfx_fsr_easu_fp16.comp
42 vulkan_fidelityfx_fsr_easu_fp32.comp 51 vulkan_fidelityfx_fsr_easu_fp32.comp
43 vulkan_fidelityfx_fsr_rcas_fp16.comp 52 vulkan_fidelityfx_fsr_rcas_fp16.comp
@@ -118,6 +127,25 @@ foreach(FILENAME IN ITEMS ${SHADER_FILES})
118 endif() 127 endif()
119endforeach() 128endforeach()
120 129
130foreach(FILEPATH IN ITEMS ${FIDELITYFX_FILES})
131 get_filename_component(FILENAME ${FILEPATH} NAME)
132 string(REPLACE "." "_" HEADER_NAME ${FILENAME})
133 set(SOURCE_FILE ${FILEPATH})
134 set(SOURCE_HEADER_FILE ${SHADER_DIR}/${HEADER_NAME}.h)
135 add_custom_command(
136 OUTPUT
137 ${SOURCE_HEADER_FILE}
138 COMMAND
139 ${CMAKE_COMMAND} -P ${HEADER_GENERATOR} ${SOURCE_FILE} ${SOURCE_HEADER_FILE} ${INPUT_FILE}
140 MAIN_DEPENDENCY
141 ${SOURCE_FILE}
142 DEPENDS
143 ${INPUT_FILE}
144 # HEADER_GENERATOR should be included here but msbuild seems to assume it's always modified
145 )
146 set(SHADER_HEADERS ${SHADER_HEADERS} ${SOURCE_HEADER_FILE})
147endforeach()
148
121set(SHADER_SOURCES ${SHADER_FILES}) 149set(SHADER_SOURCES ${SHADER_FILES})
122list(APPEND SHADER_SOURCES ${GLSL_INCLUDES}) 150list(APPEND SHADER_SOURCES ${GLSL_INCLUDES})
123 151
diff --git a/src/video_core/host_shaders/vulkan_blit_color_float.frag b/src/video_core/host_shaders/blit_color_float.frag
index c0c832296..c0c832296 100644
--- a/src/video_core/host_shaders/vulkan_blit_color_float.frag
+++ b/src/video_core/host_shaders/blit_color_float.frag
diff --git a/src/video_core/host_shaders/full_screen_triangle.vert b/src/video_core/host_shaders/full_screen_triangle.vert
index 2c976b19f..d16d98995 100644
--- a/src/video_core/host_shaders/full_screen_triangle.vert
+++ b/src/video_core/host_shaders/full_screen_triangle.vert
@@ -4,13 +4,20 @@
4#version 450 4#version 450
5 5
6#ifdef VULKAN 6#ifdef VULKAN
7#define VERTEX_ID gl_VertexIndex
7#define BEGIN_PUSH_CONSTANTS layout(push_constant) uniform PushConstants { 8#define BEGIN_PUSH_CONSTANTS layout(push_constant) uniform PushConstants {
8#define END_PUSH_CONSTANTS }; 9#define END_PUSH_CONSTANTS };
9#define UNIFORM(n) 10#define UNIFORM(n)
11#define FLIPY 1
10#else // ^^^ Vulkan ^^^ // vvv OpenGL vvv 12#else // ^^^ Vulkan ^^^ // vvv OpenGL vvv
13#define VERTEX_ID gl_VertexID
11#define BEGIN_PUSH_CONSTANTS 14#define BEGIN_PUSH_CONSTANTS
12#define END_PUSH_CONSTANTS 15#define END_PUSH_CONSTANTS
16#define FLIPY -1
13#define UNIFORM(n) layout (location = n) uniform 17#define UNIFORM(n) layout (location = n) uniform
18out gl_PerVertex {
19 vec4 gl_Position;
20};
14#endif 21#endif
15 22
16BEGIN_PUSH_CONSTANTS 23BEGIN_PUSH_CONSTANTS
@@ -21,8 +28,8 @@ END_PUSH_CONSTANTS
21layout(location = 0) out vec2 texcoord; 28layout(location = 0) out vec2 texcoord;
22 29
23void main() { 30void main() {
24 float x = float((gl_VertexIndex & 1) << 2); 31 float x = float((VERTEX_ID & 1) << 2);
25 float y = float((gl_VertexIndex & 2) << 1); 32 float y = float((VERTEX_ID & 2) << 1);
26 gl_Position = vec4(x - 1.0, y - 1.0, 0.0, 1.0); 33 gl_Position = vec4(x - 1.0, FLIPY * (y - 1.0), 0.0, 1.0);
27 texcoord = fma(vec2(x, y) / 2.0, tex_scale, tex_offset); 34 texcoord = fma(vec2(x, y) / 2.0, tex_scale, tex_offset);
28} 35}
diff --git a/src/video_core/host_shaders/opengl_fidelityfx_fsr.frag b/src/video_core/host_shaders/opengl_fidelityfx_fsr.frag
new file mode 100644
index 000000000..16d22f58e
--- /dev/null
+++ b/src/video_core/host_shaders/opengl_fidelityfx_fsr.frag
@@ -0,0 +1,108 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4//!#version 460 core
5#extension GL_ARB_separate_shader_objects : enable
6#extension GL_ARB_shading_language_420pack : enable
7
8#extension GL_AMD_gpu_shader_half_float : enable
9#extension GL_NV_gpu_shader5 : enable
10
11// FidelityFX Super Resolution Sample
12//
13// Copyright (c) 2021 Advanced Micro Devices, Inc. All rights reserved.
14// Permission is hereby granted, free of charge, to any person obtaining a copy
15// of this software and associated documentation files(the "Software"), to deal
16// in the Software without restriction, including without limitation the rights
17// to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
18// copies of the Software, and to permit persons to whom the Software is
19// furnished to do so, subject to the following conditions :
20// The above copyright notice and this permission notice shall be included in
21// all copies or substantial portions of the Software.
22// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
23// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
24// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE
25// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
26// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
27// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
28// THE SOFTWARE.
29
30layout (location = 0) uniform uvec4 constants[4];
31
32#define A_GPU 1
33#define A_GLSL 1
34
35#ifdef YUZU_USE_FP16
36 #define A_HALF
37#endif
38#include "ffx_a.h"
39
40#ifndef YUZU_USE_FP16
41 layout (binding=0) uniform sampler2D InputTexture;
42 #if USE_EASU
43 #define FSR_EASU_F 1
44 AF4 FsrEasuRF(AF2 p) { AF4 res = textureGather(InputTexture, p, 0); return res; }
45 AF4 FsrEasuGF(AF2 p) { AF4 res = textureGather(InputTexture, p, 1); return res; }
46 AF4 FsrEasuBF(AF2 p) { AF4 res = textureGather(InputTexture, p, 2); return res; }
47 #endif
48 #if USE_RCAS
49 #define FSR_RCAS_F
50 AF4 FsrRcasLoadF(ASU2 p) { return texelFetch(InputTexture, ASU2(p), 0); }
51 void FsrRcasInputF(inout AF1 r, inout AF1 g, inout AF1 b) {}
52 #endif
53#else
54 layout (binding=0) uniform sampler2D InputTexture;
55 #if USE_EASU
56 #define FSR_EASU_H 1
57 AH4 FsrEasuRH(AF2 p) { AH4 res = AH4(textureGather(InputTexture, p, 0)); return res; }
58 AH4 FsrEasuGH(AF2 p) { AH4 res = AH4(textureGather(InputTexture, p, 1)); return res; }
59 AH4 FsrEasuBH(AF2 p) { AH4 res = AH4(textureGather(InputTexture, p, 2)); return res; }
60 #endif
61 #if USE_RCAS
62 #define FSR_RCAS_H
63 AH4 FsrRcasLoadH(ASW2 p) { return AH4(texelFetch(InputTexture, ASU2(p), 0)); }
64 void FsrRcasInputH(inout AH1 r,inout AH1 g,inout AH1 b){}
65 #endif
66#endif
67
68#include "ffx_fsr1.h"
69
70#if USE_RCAS
71 layout(location = 0) in vec2 frag_texcoord;
72#endif
73layout (location = 0) out vec4 frag_color;
74
75void CurrFilter(AU2 pos)
76{
77#if USE_EASU
78 #ifndef YUZU_USE_FP16
79 AF3 c;
80 FsrEasuF(c, pos, constants[0], constants[1], constants[2], constants[3]);
81 frag_color = AF4(c, 1.0);
82 #else
83 AH3 c;
84 FsrEasuH(c, pos, constants[0], constants[1], constants[2], constants[3]);
85 frag_color = AH4(c, 1.0);
86 #endif
87#endif
88#if USE_RCAS
89 #ifndef YUZU_USE_FP16
90 AF3 c;
91 FsrRcasF(c.r, c.g, c.b, pos, constants[0]);
92 frag_color = AF4(c, 1.0);
93 #else
94 AH3 c;
95 FsrRcasH(c.r, c.g, c.b, pos, constants[0]);
96 frag_color = AH4(c, 1.0);
97 #endif
98#endif
99}
100
101void main()
102{
103#if USE_RCAS
104 CurrFilter(AU2(frag_texcoord * vec2(textureSize(InputTexture, 0))));
105#else
106 CurrFilter(AU2(gl_FragCoord.xy));
107#endif
108}
diff --git a/src/video_core/host_shaders/opengl_fidelityfx_fsr_easu.frag b/src/video_core/host_shaders/opengl_fidelityfx_fsr_easu.frag
new file mode 100644
index 000000000..d39f80ac1
--- /dev/null
+++ b/src/video_core/host_shaders/opengl_fidelityfx_fsr_easu.frag
@@ -0,0 +1,9 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#version 460 core
5#extension GL_GOOGLE_include_directive : enable
6
7#define USE_EASU 1
8
9#include "opengl_fidelityfx_fsr.frag"
diff --git a/src/video_core/host_shaders/opengl_fidelityfx_fsr_rcas.frag b/src/video_core/host_shaders/opengl_fidelityfx_fsr_rcas.frag
new file mode 100644
index 000000000..cfa78ddc7
--- /dev/null
+++ b/src/video_core/host_shaders/opengl_fidelityfx_fsr_rcas.frag
@@ -0,0 +1,9 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#version 460 core
5#extension GL_GOOGLE_include_directive : enable
6
7#define USE_RCAS 1
8
9#include "opengl_fidelityfx_fsr.frag"
diff --git a/src/video_core/host_shaders/vulkan_color_clear.frag b/src/video_core/host_shaders/vulkan_color_clear.frag
new file mode 100644
index 000000000..617bf01e1
--- /dev/null
+++ b/src/video_core/host_shaders/vulkan_color_clear.frag
@@ -0,0 +1,14 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#version 460 core
5
6layout (push_constant) uniform PushConstants {
7 vec4 clear_color;
8};
9
10layout(location = 0) out vec4 color;
11
12void main() {
13 color = clear_color;
14}
diff --git a/src/video_core/host_shaders/vulkan_color_clear.vert b/src/video_core/host_shaders/vulkan_color_clear.vert
new file mode 100644
index 000000000..d85883141
--- /dev/null
+++ b/src/video_core/host_shaders/vulkan_color_clear.vert
@@ -0,0 +1,10 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#version 460 core
5
6void main() {
7 float x = float((gl_VertexIndex & 1) << 2);
8 float y = float((gl_VertexIndex & 2) << 1);
9 gl_Position = vec4(x - 1.0, y - 1.0, 0.0, 1.0);
10}
diff --git a/src/video_core/memory_manager.cpp b/src/video_core/memory_manager.cpp
index 3bcae3503..83924475b 100644
--- a/src/video_core/memory_manager.cpp
+++ b/src/video_core/memory_manager.cpp
@@ -6,7 +6,6 @@
6#include "common/alignment.h" 6#include "common/alignment.h"
7#include "common/assert.h" 7#include "common/assert.h"
8#include "common/logging/log.h" 8#include "common/logging/log.h"
9#include "common/settings.h"
10#include "core/core.h" 9#include "core/core.h"
11#include "core/device_memory.h" 10#include "core/device_memory.h"
12#include "core/hle/kernel/k_page_table.h" 11#include "core/hle/kernel/k_page_table.h"
@@ -46,11 +45,6 @@ MemoryManager::MemoryManager(Core::System& system_, u64 address_space_bits_, u64
46 big_page_table_cpu.resize(big_page_table_size); 45 big_page_table_cpu.resize(big_page_table_size);
47 big_page_continous.resize(big_page_table_size / continous_bits, 0); 46 big_page_continous.resize(big_page_table_size / continous_bits, 0);
48 entries.resize(page_table_size / 32, 0); 47 entries.resize(page_table_size / 32, 0);
49 if (!Settings::IsGPULevelExtreme() && Settings::IsFastmemEnabled()) {
50 fastmem_arena = system.DeviceMemory().buffer.VirtualBasePointer();
51 } else {
52 fastmem_arena = nullptr;
53 }
54} 48}
55 49
56MemoryManager::~MemoryManager() = default; 50MemoryManager::~MemoryManager() = default;
@@ -360,7 +354,7 @@ inline void MemoryManager::MemoryOperation(GPUVAddr gpu_src_addr, std::size_t si
360 } 354 }
361} 355}
362 356
363template <bool is_safe, bool use_fastmem> 357template <bool is_safe>
364void MemoryManager::ReadBlockImpl(GPUVAddr gpu_src_addr, void* dest_buffer, std::size_t size, 358void MemoryManager::ReadBlockImpl(GPUVAddr gpu_src_addr, void* dest_buffer, std::size_t size,
365 [[maybe_unused]] VideoCommon::CacheType which) const { 359 [[maybe_unused]] VideoCommon::CacheType which) const {
366 auto set_to_zero = [&]([[maybe_unused]] std::size_t page_index, 360 auto set_to_zero = [&]([[maybe_unused]] std::size_t page_index,
@@ -374,12 +368,8 @@ void MemoryManager::ReadBlockImpl(GPUVAddr gpu_src_addr, void* dest_buffer, std:
374 if constexpr (is_safe) { 368 if constexpr (is_safe) {
375 rasterizer->FlushRegion(cpu_addr_base, copy_amount, which); 369 rasterizer->FlushRegion(cpu_addr_base, copy_amount, which);
376 } 370 }
377 if constexpr (use_fastmem) { 371 u8* physical = memory.GetPointer(cpu_addr_base);
378 std::memcpy(dest_buffer, &fastmem_arena[cpu_addr_base], copy_amount); 372 std::memcpy(dest_buffer, physical, copy_amount);
379 } else {
380 u8* physical = memory.GetPointer(cpu_addr_base);
381 std::memcpy(dest_buffer, physical, copy_amount);
382 }
383 dest_buffer = static_cast<u8*>(dest_buffer) + copy_amount; 373 dest_buffer = static_cast<u8*>(dest_buffer) + copy_amount;
384 }; 374 };
385 auto mapped_big = [&](std::size_t page_index, std::size_t offset, std::size_t copy_amount) { 375 auto mapped_big = [&](std::size_t page_index, std::size_t offset, std::size_t copy_amount) {
@@ -388,15 +378,11 @@ void MemoryManager::ReadBlockImpl(GPUVAddr gpu_src_addr, void* dest_buffer, std:
388 if constexpr (is_safe) { 378 if constexpr (is_safe) {
389 rasterizer->FlushRegion(cpu_addr_base, copy_amount, which); 379 rasterizer->FlushRegion(cpu_addr_base, copy_amount, which);
390 } 380 }
391 if constexpr (use_fastmem) { 381 if (!IsBigPageContinous(page_index)) [[unlikely]] {
392 std::memcpy(dest_buffer, &fastmem_arena[cpu_addr_base], copy_amount); 382 memory.ReadBlockUnsafe(cpu_addr_base, dest_buffer, copy_amount);
393 } else { 383 } else {
394 if (!IsBigPageContinous(page_index)) [[unlikely]] { 384 u8* physical = memory.GetPointer(cpu_addr_base);
395 memory.ReadBlockUnsafe(cpu_addr_base, dest_buffer, copy_amount); 385 std::memcpy(dest_buffer, physical, copy_amount);
396 } else {
397 u8* physical = memory.GetPointer(cpu_addr_base);
398 std::memcpy(dest_buffer, physical, copy_amount);
399 }
400 } 386 }
401 dest_buffer = static_cast<u8*>(dest_buffer) + copy_amount; 387 dest_buffer = static_cast<u8*>(dest_buffer) + copy_amount;
402 }; 388 };
@@ -410,20 +396,12 @@ void MemoryManager::ReadBlockImpl(GPUVAddr gpu_src_addr, void* dest_buffer, std:
410 396
411void MemoryManager::ReadBlock(GPUVAddr gpu_src_addr, void* dest_buffer, std::size_t size, 397void MemoryManager::ReadBlock(GPUVAddr gpu_src_addr, void* dest_buffer, std::size_t size,
412 VideoCommon::CacheType which) const { 398 VideoCommon::CacheType which) const {
413 if (fastmem_arena) [[likely]] { 399 ReadBlockImpl<true>(gpu_src_addr, dest_buffer, size, which);
414 ReadBlockImpl<true, true>(gpu_src_addr, dest_buffer, size, which);
415 return;
416 }
417 ReadBlockImpl<true, false>(gpu_src_addr, dest_buffer, size, which);
418} 400}
419 401
420void MemoryManager::ReadBlockUnsafe(GPUVAddr gpu_src_addr, void* dest_buffer, 402void MemoryManager::ReadBlockUnsafe(GPUVAddr gpu_src_addr, void* dest_buffer,
421 const std::size_t size) const { 403 const std::size_t size) const {
422 if (fastmem_arena) [[likely]] { 404 ReadBlockImpl<false>(gpu_src_addr, dest_buffer, size, VideoCommon::CacheType::None);
423 ReadBlockImpl<false, true>(gpu_src_addr, dest_buffer, size, VideoCommon::CacheType::None);
424 return;
425 }
426 ReadBlockImpl<false, false>(gpu_src_addr, dest_buffer, size, VideoCommon::CacheType::None);
427} 405}
428 406
429template <bool is_safe> 407template <bool is_safe>
diff --git a/src/video_core/memory_manager.h b/src/video_core/memory_manager.h
index 2936364f0..9ebfb6179 100644
--- a/src/video_core/memory_manager.h
+++ b/src/video_core/memory_manager.h
@@ -141,7 +141,7 @@ private:
141 inline void MemoryOperation(GPUVAddr gpu_src_addr, std::size_t size, FuncMapped&& func_mapped, 141 inline void MemoryOperation(GPUVAddr gpu_src_addr, std::size_t size, FuncMapped&& func_mapped,
142 FuncReserved&& func_reserved, FuncUnmapped&& func_unmapped) const; 142 FuncReserved&& func_reserved, FuncUnmapped&& func_unmapped) const;
143 143
144 template <bool is_safe, bool use_fastmem> 144 template <bool is_safe>
145 void ReadBlockImpl(GPUVAddr gpu_src_addr, void* dest_buffer, std::size_t size, 145 void ReadBlockImpl(GPUVAddr gpu_src_addr, void* dest_buffer, std::size_t size,
146 VideoCommon::CacheType which) const; 146 VideoCommon::CacheType which) const;
147 147
@@ -215,7 +215,6 @@ private:
215 215
216 std::vector<u64> big_page_continous; 216 std::vector<u64> big_page_continous;
217 std::vector<std::pair<VAddr, std::size_t>> page_stash{}; 217 std::vector<std::pair<VAddr, std::size_t>> page_stash{};
218 u8* fastmem_arena{};
219 218
220 constexpr static size_t continous_bits = 64; 219 constexpr static size_t continous_bits = 64;
221 220
diff --git a/src/video_core/rasterizer_interface.h b/src/video_core/rasterizer_interface.h
index 1735b6164..33e2610bc 100644
--- a/src/video_core/rasterizer_interface.h
+++ b/src/video_core/rasterizer_interface.h
@@ -47,6 +47,9 @@ public:
47 /// Dispatches an indirect draw invocation 47 /// Dispatches an indirect draw invocation
48 virtual void DrawIndirect() {} 48 virtual void DrawIndirect() {}
49 49
50 /// Dispatches an draw texture invocation
51 virtual void DrawTexture() = 0;
52
50 /// Clear the current framebuffer 53 /// Clear the current framebuffer
51 virtual void Clear(u32 layer_count) = 0; 54 virtual void Clear(u32 layer_count) = 0;
52 55
diff --git a/src/video_core/renderer_null/null_rasterizer.cpp b/src/video_core/renderer_null/null_rasterizer.cpp
index 2c11345d7..2b5c7defa 100644
--- a/src/video_core/renderer_null/null_rasterizer.cpp
+++ b/src/video_core/renderer_null/null_rasterizer.cpp
@@ -21,6 +21,7 @@ RasterizerNull::RasterizerNull(Core::Memory::Memory& cpu_memory_, Tegra::GPU& gp
21RasterizerNull::~RasterizerNull() = default; 21RasterizerNull::~RasterizerNull() = default;
22 22
23void RasterizerNull::Draw(bool is_indexed, u32 instance_count) {} 23void RasterizerNull::Draw(bool is_indexed, u32 instance_count) {}
24void RasterizerNull::DrawTexture() {}
24void RasterizerNull::Clear(u32 layer_count) {} 25void RasterizerNull::Clear(u32 layer_count) {}
25void RasterizerNull::DispatchCompute() {} 26void RasterizerNull::DispatchCompute() {}
26void RasterizerNull::ResetCounter(VideoCore::QueryType type) {} 27void RasterizerNull::ResetCounter(VideoCore::QueryType type) {}
diff --git a/src/video_core/renderer_null/null_rasterizer.h b/src/video_core/renderer_null/null_rasterizer.h
index 2112aa70e..51f896e43 100644
--- a/src/video_core/renderer_null/null_rasterizer.h
+++ b/src/video_core/renderer_null/null_rasterizer.h
@@ -31,6 +31,7 @@ public:
31 ~RasterizerNull() override; 31 ~RasterizerNull() override;
32 32
33 void Draw(bool is_indexed, u32 instance_count) override; 33 void Draw(bool is_indexed, u32 instance_count) override;
34 void DrawTexture() override;
34 void Clear(u32 layer_count) override; 35 void Clear(u32 layer_count) override;
35 void DispatchCompute() override; 36 void DispatchCompute() override;
36 void ResetCounter(VideoCore::QueryType type) override; 37 void ResetCounter(VideoCore::QueryType type) override;
diff --git a/src/video_core/renderer_opengl/blit_image.cpp b/src/video_core/renderer_opengl/blit_image.cpp
new file mode 100644
index 000000000..9a560a73b
--- /dev/null
+++ b/src/video_core/renderer_opengl/blit_image.cpp
@@ -0,0 +1,59 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include <algorithm>
5
6#include "video_core/host_shaders/blit_color_float_frag.h"
7#include "video_core/host_shaders/full_screen_triangle_vert.h"
8#include "video_core/renderer_opengl/blit_image.h"
9#include "video_core/renderer_opengl/gl_shader_manager.h"
10#include "video_core/renderer_opengl/gl_shader_util.h"
11
12namespace OpenGL {
13
14BlitImageHelper::BlitImageHelper(ProgramManager& program_manager_)
15 : program_manager(program_manager_),
16 full_screen_vert(CreateProgram(HostShaders::FULL_SCREEN_TRIANGLE_VERT, GL_VERTEX_SHADER)),
17 blit_color_to_color_frag(
18 CreateProgram(HostShaders::BLIT_COLOR_FLOAT_FRAG, GL_FRAGMENT_SHADER)) {}
19
20BlitImageHelper::~BlitImageHelper() = default;
21
22void BlitImageHelper::BlitColor(GLuint dst_framebuffer, GLuint src_image_view, GLuint src_sampler,
23 const Region2D& dst_region, const Region2D& src_region,
24 const Extent3D& src_size) {
25 glEnable(GL_CULL_FACE);
26 glDisable(GL_COLOR_LOGIC_OP);
27 glDisable(GL_DEPTH_TEST);
28 glDisable(GL_STENCIL_TEST);
29 glDisable(GL_POLYGON_OFFSET_FILL);
30 glDisable(GL_RASTERIZER_DISCARD);
31 glDisable(GL_ALPHA_TEST);
32 glDisablei(GL_BLEND, 0);
33 glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
34 glCullFace(GL_BACK);
35 glFrontFace(GL_CW);
36 glColorMaski(0, GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
37 glDepthRangeIndexed(0, 0.0, 0.0);
38
39 program_manager.BindPresentPrograms(full_screen_vert.handle, blit_color_to_color_frag.handle);
40 glProgramUniform2f(full_screen_vert.handle, 0,
41 static_cast<float>(src_region.end.x - src_region.start.x) /
42 static_cast<float>(src_size.width),
43 static_cast<float>(src_region.end.y - src_region.start.y) /
44 static_cast<float>(src_size.height));
45 glProgramUniform2f(full_screen_vert.handle, 1,
46 static_cast<float>(src_region.start.x) / static_cast<float>(src_size.width),
47 static_cast<float>(src_region.start.y) /
48 static_cast<float>(src_size.height));
49 glViewport(std::min(dst_region.start.x, dst_region.end.x),
50 std::min(dst_region.start.y, dst_region.end.y),
51 std::abs(dst_region.end.x - dst_region.start.x),
52 std::abs(dst_region.end.y - dst_region.start.y));
53 glBindFramebuffer(GL_DRAW_FRAMEBUFFER, dst_framebuffer);
54 glBindSampler(0, src_sampler);
55 glBindTextureUnit(0, src_image_view);
56 glClear(GL_COLOR_BUFFER_BIT);
57 glDrawArrays(GL_TRIANGLES, 0, 3);
58}
59} // namespace OpenGL
diff --git a/src/video_core/renderer_opengl/blit_image.h b/src/video_core/renderer_opengl/blit_image.h
new file mode 100644
index 000000000..5a2b12d16
--- /dev/null
+++ b/src/video_core/renderer_opengl/blit_image.h
@@ -0,0 +1,38 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include <glad/glad.h>
7
8#include "video_core/engines/fermi_2d.h"
9#include "video_core/renderer_opengl/gl_resource_manager.h"
10#include "video_core/texture_cache/types.h"
11
12namespace OpenGL {
13
14using VideoCommon::Extent3D;
15using VideoCommon::Offset2D;
16using VideoCommon::Region2D;
17
18class ProgramManager;
19class Framebuffer;
20class ImageView;
21
22class BlitImageHelper {
23public:
24 explicit BlitImageHelper(ProgramManager& program_manager);
25 ~BlitImageHelper();
26
27 void BlitColor(GLuint dst_framebuffer, GLuint src_image_view, GLuint src_sampler,
28 const Region2D& dst_region, const Region2D& src_region,
29 const Extent3D& src_size);
30
31private:
32 ProgramManager& program_manager;
33
34 OGLProgram full_screen_vert;
35 OGLProgram blit_color_to_color_frag;
36};
37
38} // namespace OpenGL
diff --git a/src/video_core/renderer_opengl/gl_compute_pipeline.cpp b/src/video_core/renderer_opengl/gl_compute_pipeline.cpp
index 26d066004..1a0cea9b7 100644
--- a/src/video_core/renderer_opengl/gl_compute_pipeline.cpp
+++ b/src/video_core/renderer_opengl/gl_compute_pipeline.cpp
@@ -30,7 +30,7 @@ bool ComputePipelineKey::operator==(const ComputePipelineKey& rhs) const noexcep
30ComputePipeline::ComputePipeline(const Device& device, TextureCache& texture_cache_, 30ComputePipeline::ComputePipeline(const Device& device, TextureCache& texture_cache_,
31 BufferCache& buffer_cache_, ProgramManager& program_manager_, 31 BufferCache& buffer_cache_, ProgramManager& program_manager_,
32 const Shader::Info& info_, std::string code, 32 const Shader::Info& info_, std::string code,
33 std::vector<u32> code_v) 33 std::vector<u32> code_v, bool force_context_flush)
34 : texture_cache{texture_cache_}, buffer_cache{buffer_cache_}, 34 : texture_cache{texture_cache_}, buffer_cache{buffer_cache_},
35 program_manager{program_manager_}, info{info_} { 35 program_manager{program_manager_}, info{info_} {
36 switch (device.GetShaderBackend()) { 36 switch (device.GetShaderBackend()) {
@@ -63,6 +63,15 @@ ComputePipeline::ComputePipeline(const Device& device, TextureCache& texture_cac
63 writes_global_memory = !use_storage_buffers && 63 writes_global_memory = !use_storage_buffers &&
64 std::ranges::any_of(info.storage_buffers_descriptors, 64 std::ranges::any_of(info.storage_buffers_descriptors,
65 [](const auto& desc) { return desc.is_written; }); 65 [](const auto& desc) { return desc.is_written; });
66 if (force_context_flush) {
67 std::scoped_lock lock{built_mutex};
68 built_fence.Create();
69 // Flush this context to ensure compilation commands and fence are in the GPU pipe.
70 glFlush();
71 built_condvar.notify_one();
72 } else {
73 is_built = true;
74 }
66} 75}
67 76
68void ComputePipeline::Configure() { 77void ComputePipeline::Configure() {
@@ -142,6 +151,9 @@ void ComputePipeline::Configure() {
142 } 151 }
143 texture_cache.FillComputeImageViews(std::span(views.data(), views.size())); 152 texture_cache.FillComputeImageViews(std::span(views.data(), views.size()));
144 153
154 if (!is_built) {
155 WaitForBuild();
156 }
145 if (assembly_program.handle != 0) { 157 if (assembly_program.handle != 0) {
146 program_manager.BindComputeAssemblyProgram(assembly_program.handle); 158 program_manager.BindComputeAssemblyProgram(assembly_program.handle);
147 } else { 159 } else {
@@ -223,4 +235,13 @@ void ComputePipeline::Configure() {
223 } 235 }
224} 236}
225 237
238void ComputePipeline::WaitForBuild() {
239 if (built_fence.handle == 0) {
240 std::unique_lock lock{built_mutex};
241 built_condvar.wait(lock, [this] { return built_fence.handle != 0; });
242 }
243 ASSERT(glClientWaitSync(built_fence.handle, 0, GL_TIMEOUT_IGNORED) != GL_WAIT_FAILED);
244 is_built = true;
245}
246
226} // namespace OpenGL 247} // namespace OpenGL
diff --git a/src/video_core/renderer_opengl/gl_compute_pipeline.h b/src/video_core/renderer_opengl/gl_compute_pipeline.h
index 6534dec32..9bcc72b59 100644
--- a/src/video_core/renderer_opengl/gl_compute_pipeline.h
+++ b/src/video_core/renderer_opengl/gl_compute_pipeline.h
@@ -50,7 +50,8 @@ class ComputePipeline {
50public: 50public:
51 explicit ComputePipeline(const Device& device, TextureCache& texture_cache_, 51 explicit ComputePipeline(const Device& device, TextureCache& texture_cache_,
52 BufferCache& buffer_cache_, ProgramManager& program_manager_, 52 BufferCache& buffer_cache_, ProgramManager& program_manager_,
53 const Shader::Info& info_, std::string code, std::vector<u32> code_v); 53 const Shader::Info& info_, std::string code, std::vector<u32> code_v,
54 bool force_context_flush = false);
54 55
55 void Configure(); 56 void Configure();
56 57
@@ -65,6 +66,8 @@ public:
65 } 66 }
66 67
67private: 68private:
69 void WaitForBuild();
70
68 TextureCache& texture_cache; 71 TextureCache& texture_cache;
69 BufferCache& buffer_cache; 72 BufferCache& buffer_cache;
70 Tegra::MemoryManager* gpu_memory; 73 Tegra::MemoryManager* gpu_memory;
@@ -81,6 +84,11 @@ private:
81 84
82 bool use_storage_buffers{}; 85 bool use_storage_buffers{};
83 bool writes_global_memory{}; 86 bool writes_global_memory{};
87
88 std::mutex built_mutex;
89 std::condition_variable built_condvar;
90 OGLSync built_fence{};
91 bool is_built{false};
84}; 92};
85 93
86} // namespace OpenGL 94} // namespace OpenGL
diff --git a/src/video_core/renderer_opengl/gl_device.cpp b/src/video_core/renderer_opengl/gl_device.cpp
index cee5c3247..22ed16ebf 100644
--- a/src/video_core/renderer_opengl/gl_device.cpp
+++ b/src/video_core/renderer_opengl/gl_device.cpp
@@ -166,6 +166,7 @@ Device::Device(Core::Frontend::EmuWindow& emu_window) {
166 has_shader_int64 = HasExtension(extensions, "GL_ARB_gpu_shader_int64"); 166 has_shader_int64 = HasExtension(extensions, "GL_ARB_gpu_shader_int64");
167 has_amd_shader_half_float = GLAD_GL_AMD_gpu_shader_half_float; 167 has_amd_shader_half_float = GLAD_GL_AMD_gpu_shader_half_float;
168 has_sparse_texture_2 = GLAD_GL_ARB_sparse_texture2; 168 has_sparse_texture_2 = GLAD_GL_ARB_sparse_texture2;
169 has_draw_texture = GLAD_GL_NV_draw_texture;
169 warp_size_potentially_larger_than_guest = !is_nvidia && !is_intel; 170 warp_size_potentially_larger_than_guest = !is_nvidia && !is_intel;
170 need_fastmath_off = is_nvidia; 171 need_fastmath_off = is_nvidia;
171 can_report_memory = GLAD_GL_NVX_gpu_memory_info; 172 can_report_memory = GLAD_GL_NVX_gpu_memory_info;
diff --git a/src/video_core/renderer_opengl/gl_device.h b/src/video_core/renderer_opengl/gl_device.h
index 2a72d84be..3ff8cad83 100644
--- a/src/video_core/renderer_opengl/gl_device.h
+++ b/src/video_core/renderer_opengl/gl_device.h
@@ -4,6 +4,8 @@
4#pragma once 4#pragma once
5 5
6#include <cstddef> 6#include <cstddef>
7#include <string>
8
7#include "common/common_types.h" 9#include "common/common_types.h"
8#include "core/frontend/emu_window.h" 10#include "core/frontend/emu_window.h"
9#include "shader_recompiler/stage.h" 11#include "shader_recompiler/stage.h"
@@ -146,6 +148,10 @@ public:
146 return has_sparse_texture_2; 148 return has_sparse_texture_2;
147 } 149 }
148 150
151 bool HasDrawTexture() const {
152 return has_draw_texture;
153 }
154
149 bool IsWarpSizePotentiallyLargerThanGuest() const { 155 bool IsWarpSizePotentiallyLargerThanGuest() const {
150 return warp_size_potentially_larger_than_guest; 156 return warp_size_potentially_larger_than_guest;
151 } 157 }
@@ -216,6 +222,7 @@ private:
216 bool has_shader_int64{}; 222 bool has_shader_int64{};
217 bool has_amd_shader_half_float{}; 223 bool has_amd_shader_half_float{};
218 bool has_sparse_texture_2{}; 224 bool has_sparse_texture_2{};
225 bool has_draw_texture{};
219 bool warp_size_potentially_larger_than_guest{}; 226 bool warp_size_potentially_larger_than_guest{};
220 bool need_fastmath_off{}; 227 bool need_fastmath_off{};
221 bool has_cbuf_ftou_bug{}; 228 bool has_cbuf_ftou_bug{};
diff --git a/src/video_core/renderer_opengl/gl_fsr.cpp b/src/video_core/renderer_opengl/gl_fsr.cpp
new file mode 100644
index 000000000..77262dcf1
--- /dev/null
+++ b/src/video_core/renderer_opengl/gl_fsr.cpp
@@ -0,0 +1,101 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "common/settings.h"
5#include "video_core/fsr.h"
6#include "video_core/renderer_opengl/gl_fsr.h"
7#include "video_core/renderer_opengl/gl_shader_manager.h"
8#include "video_core/renderer_opengl/gl_shader_util.h"
9
10namespace OpenGL {
11using namespace FSR;
12
13using FsrConstants = std::array<u32, 4 * 4>;
14
15FSR::FSR(std::string_view fsr_vertex_source, std::string_view fsr_easu_source,
16 std::string_view fsr_rcas_source)
17 : fsr_vertex{CreateProgram(fsr_vertex_source, GL_VERTEX_SHADER)},
18 fsr_easu_frag{CreateProgram(fsr_easu_source, GL_FRAGMENT_SHADER)},
19 fsr_rcas_frag{CreateProgram(fsr_rcas_source, GL_FRAGMENT_SHADER)} {
20 glProgramUniform2f(fsr_vertex.handle, 0, 1.0f, 1.0f);
21 glProgramUniform2f(fsr_vertex.handle, 1, 0.0f, 0.0f);
22}
23
24FSR::~FSR() = default;
25
26void FSR::Draw(ProgramManager& program_manager, const Common::Rectangle<u32>& screen,
27 u32 input_image_width, u32 input_image_height,
28 const Common::Rectangle<int>& crop_rect) {
29
30 const auto output_image_width = screen.GetWidth();
31 const auto output_image_height = screen.GetHeight();
32
33 if (fsr_intermediate_tex.handle) {
34 GLint fsr_tex_width, fsr_tex_height;
35 glGetTextureLevelParameteriv(fsr_intermediate_tex.handle, 0, GL_TEXTURE_WIDTH,
36 &fsr_tex_width);
37 glGetTextureLevelParameteriv(fsr_intermediate_tex.handle, 0, GL_TEXTURE_HEIGHT,
38 &fsr_tex_height);
39 if (static_cast<u32>(fsr_tex_width) != output_image_width ||
40 static_cast<u32>(fsr_tex_height) != output_image_height) {
41 fsr_intermediate_tex.Release();
42 }
43 }
44 if (!fsr_intermediate_tex.handle) {
45 fsr_intermediate_tex.Create(GL_TEXTURE_2D);
46 glTextureStorage2D(fsr_intermediate_tex.handle, 1, GL_RGB16F, output_image_width,
47 output_image_height);
48 glNamedFramebufferTexture(fsr_framebuffer.handle, GL_COLOR_ATTACHMENT0,
49 fsr_intermediate_tex.handle, 0);
50 }
51
52 GLint old_draw_fb;
53 glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, &old_draw_fb);
54
55 glFrontFace(GL_CW);
56 glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fsr_framebuffer.handle);
57 glViewportIndexedf(0, 0.0f, 0.0f, static_cast<GLfloat>(output_image_width),
58 static_cast<GLfloat>(output_image_height));
59
60 FsrConstants constants;
61 FsrEasuConOffset(
62 constants.data() + 0, constants.data() + 4, constants.data() + 8, constants.data() + 12,
63
64 static_cast<f32>(crop_rect.GetWidth()), static_cast<f32>(crop_rect.GetHeight()),
65 static_cast<f32>(input_image_width), static_cast<f32>(input_image_height),
66 static_cast<f32>(output_image_width), static_cast<f32>(output_image_height),
67 static_cast<f32>(crop_rect.left), static_cast<f32>(crop_rect.top));
68
69 glProgramUniform4uiv(fsr_easu_frag.handle, 0, sizeof(constants), std::data(constants));
70
71 program_manager.BindPresentPrograms(fsr_vertex.handle, fsr_easu_frag.handle);
72 glDrawArrays(GL_TRIANGLES, 0, 3);
73
74 glBindFramebuffer(GL_DRAW_FRAMEBUFFER, old_draw_fb);
75 glBindTextureUnit(0, fsr_intermediate_tex.handle);
76
77 const float sharpening =
78 static_cast<float>(Settings::values.fsr_sharpening_slider.GetValue()) / 100.0f;
79
80 FsrRcasCon(constants.data(), sharpening);
81 glProgramUniform4uiv(fsr_rcas_frag.handle, 0, sizeof(constants), std::data(constants));
82}
83
84void FSR::InitBuffers() {
85 fsr_framebuffer.Create();
86}
87
88void FSR::ReleaseBuffers() {
89 fsr_framebuffer.Release();
90 fsr_intermediate_tex.Release();
91}
92
93const OGLProgram& FSR::GetPresentFragmentProgram() const noexcept {
94 return fsr_rcas_frag;
95}
96
97bool FSR::AreBuffersInitialized() const noexcept {
98 return fsr_framebuffer.handle;
99}
100
101} // namespace OpenGL
diff --git a/src/video_core/renderer_opengl/gl_fsr.h b/src/video_core/renderer_opengl/gl_fsr.h
new file mode 100644
index 000000000..1f6ae3115
--- /dev/null
+++ b/src/video_core/renderer_opengl/gl_fsr.h
@@ -0,0 +1,43 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include <string_view>
7
8#include "common/common_types.h"
9#include "common/math_util.h"
10#include "video_core/fsr.h"
11#include "video_core/renderer_opengl/gl_resource_manager.h"
12
13namespace OpenGL {
14
15class ProgramManager;
16
17class FSR {
18public:
19 explicit FSR(std::string_view fsr_vertex_source, std::string_view fsr_easu_source,
20 std::string_view fsr_rcas_source);
21 ~FSR();
22
23 void Draw(ProgramManager& program_manager, const Common::Rectangle<u32>& screen,
24 u32 input_image_width, u32 input_image_height,
25 const Common::Rectangle<int>& crop_rect);
26
27 void InitBuffers();
28
29 void ReleaseBuffers();
30
31 [[nodiscard]] const OGLProgram& GetPresentFragmentProgram() const noexcept;
32
33 [[nodiscard]] bool AreBuffersInitialized() const noexcept;
34
35private:
36 OGLFramebuffer fsr_framebuffer;
37 OGLProgram fsr_vertex;
38 OGLProgram fsr_easu_frag;
39 OGLProgram fsr_rcas_frag;
40 OGLTexture fsr_intermediate_tex;
41};
42
43} // namespace OpenGL
diff --git a/src/video_core/renderer_opengl/gl_graphics_pipeline.cpp b/src/video_core/renderer_opengl/gl_graphics_pipeline.cpp
index c115dabe1..29491e762 100644
--- a/src/video_core/renderer_opengl/gl_graphics_pipeline.cpp
+++ b/src/video_core/renderer_opengl/gl_graphics_pipeline.cpp
@@ -176,7 +176,7 @@ GraphicsPipeline::GraphicsPipeline(const Device& device, TextureCache& texture_c
176 std::array<std::string, 5> sources, 176 std::array<std::string, 5> sources,
177 std::array<std::vector<u32>, 5> sources_spirv, 177 std::array<std::vector<u32>, 5> sources_spirv,
178 const std::array<const Shader::Info*, 5>& infos, 178 const std::array<const Shader::Info*, 5>& infos,
179 const GraphicsPipelineKey& key_) 179 const GraphicsPipelineKey& key_, bool force_context_flush)
180 : texture_cache{texture_cache_}, buffer_cache{buffer_cache_}, program_manager{program_manager_}, 180 : texture_cache{texture_cache_}, buffer_cache{buffer_cache_}, program_manager{program_manager_},
181 state_tracker{state_tracker_}, key{key_} { 181 state_tracker{state_tracker_}, key{key_} {
182 if (shader_notify) { 182 if (shader_notify) {
@@ -231,7 +231,8 @@ GraphicsPipeline::GraphicsPipeline(const Device& device, TextureCache& texture_c
231 const bool in_parallel = thread_worker != nullptr; 231 const bool in_parallel = thread_worker != nullptr;
232 const auto backend = device.GetShaderBackend(); 232 const auto backend = device.GetShaderBackend();
233 auto func{[this, sources = std::move(sources), sources_spirv = std::move(sources_spirv), 233 auto func{[this, sources = std::move(sources), sources_spirv = std::move(sources_spirv),
234 shader_notify, backend, in_parallel](ShaderContext::Context*) mutable { 234 shader_notify, backend, in_parallel,
235 force_context_flush](ShaderContext::Context*) mutable {
235 for (size_t stage = 0; stage < 5; ++stage) { 236 for (size_t stage = 0; stage < 5; ++stage) {
236 switch (backend) { 237 switch (backend) {
237 case Settings::ShaderBackend::GLSL: 238 case Settings::ShaderBackend::GLSL:
@@ -251,7 +252,7 @@ GraphicsPipeline::GraphicsPipeline(const Device& device, TextureCache& texture_c
251 break; 252 break;
252 } 253 }
253 } 254 }
254 if (in_parallel) { 255 if (force_context_flush || in_parallel) {
255 std::scoped_lock lock{built_mutex}; 256 std::scoped_lock lock{built_mutex};
256 built_fence.Create(); 257 built_fence.Create();
257 // 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.
diff --git a/src/video_core/renderer_opengl/gl_graphics_pipeline.h b/src/video_core/renderer_opengl/gl_graphics_pipeline.h
index 1c06b3655..7bab3be0a 100644
--- a/src/video_core/renderer_opengl/gl_graphics_pipeline.h
+++ b/src/video_core/renderer_opengl/gl_graphics_pipeline.h
@@ -78,7 +78,7 @@ public:
78 std::array<std::string, 5> sources, 78 std::array<std::string, 5> sources,
79 std::array<std::vector<u32>, 5> sources_spirv, 79 std::array<std::vector<u32>, 5> sources_spirv,
80 const std::array<const Shader::Info*, 5>& infos, 80 const std::array<const Shader::Info*, 5>& infos,
81 const GraphicsPipelineKey& key_); 81 const GraphicsPipelineKey& key_, bool force_context_flush = false);
82 82
83 void Configure(bool is_indexed) { 83 void Configure(bool is_indexed) {
84 configure_func(this, is_indexed); 84 configure_func(this, is_indexed);
diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp
index 181857d9c..7bced675c 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer.cpp
+++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp
@@ -64,7 +64,8 @@ RasterizerOpenGL::RasterizerOpenGL(Core::Frontend::EmuWindow& emu_window_, Tegra
64 shader_cache(*this, emu_window_, device, texture_cache, buffer_cache, program_manager, 64 shader_cache(*this, emu_window_, device, texture_cache, buffer_cache, program_manager,
65 state_tracker, gpu.ShaderNotify()), 65 state_tracker, gpu.ShaderNotify()),
66 query_cache(*this), accelerate_dma(buffer_cache), 66 query_cache(*this), accelerate_dma(buffer_cache),
67 fence_manager(*this, gpu, texture_cache, buffer_cache, query_cache) {} 67 fence_manager(*this, gpu, texture_cache, buffer_cache, query_cache),
68 blit_image(program_manager_) {}
68 69
69RasterizerOpenGL::~RasterizerOpenGL() = default; 70RasterizerOpenGL::~RasterizerOpenGL() = default;
70 71
@@ -320,6 +321,47 @@ void RasterizerOpenGL::DrawIndirect() {
320 buffer_cache.SetDrawIndirect(nullptr); 321 buffer_cache.SetDrawIndirect(nullptr);
321} 322}
322 323
324void RasterizerOpenGL::DrawTexture() {
325 MICROPROFILE_SCOPE(OpenGL_Drawing);
326
327 SCOPE_EXIT({ gpu.TickWork(); });
328 query_cache.UpdateCounters();
329
330 texture_cache.SynchronizeGraphicsDescriptors();
331 texture_cache.UpdateRenderTargets(false);
332
333 SyncState();
334
335 const auto& draw_texture_state = maxwell3d->draw_manager->GetDrawTextureState();
336 const auto& sampler = texture_cache.GetGraphicsSampler(draw_texture_state.src_sampler);
337 const auto& texture = texture_cache.GetImageView(draw_texture_state.src_texture);
338
339 if (device.HasDrawTexture()) {
340 state_tracker.BindFramebuffer(texture_cache.GetFramebuffer()->Handle());
341
342 glDrawTextureNV(texture.DefaultHandle(), sampler->Handle(), draw_texture_state.dst_x0,
343 draw_texture_state.dst_y0, draw_texture_state.dst_x1,
344 draw_texture_state.dst_y1, 0,
345 draw_texture_state.src_x0 / static_cast<float>(texture.size.width),
346 draw_texture_state.src_y0 / static_cast<float>(texture.size.height),
347 draw_texture_state.src_x1 / static_cast<float>(texture.size.width),
348 draw_texture_state.src_y1 / static_cast<float>(texture.size.height));
349 } else {
350 Region2D dst_region = {Offset2D{.x = static_cast<s32>(draw_texture_state.dst_x0),
351 .y = static_cast<s32>(draw_texture_state.dst_y0)},
352 Offset2D{.x = static_cast<s32>(draw_texture_state.dst_x1),
353 .y = static_cast<s32>(draw_texture_state.dst_y1)}};
354 Region2D src_region = {Offset2D{.x = static_cast<s32>(draw_texture_state.src_x0),
355 .y = static_cast<s32>(draw_texture_state.src_y0)},
356 Offset2D{.x = static_cast<s32>(draw_texture_state.src_x1),
357 .y = static_cast<s32>(draw_texture_state.src_y1)}};
358 blit_image.BlitColor(texture_cache.GetFramebuffer()->Handle(), texture.DefaultHandle(),
359 sampler->Handle(), dst_region, src_region, texture.size);
360 }
361
362 ++num_queued_commands;
363}
364
323void RasterizerOpenGL::DispatchCompute() { 365void RasterizerOpenGL::DispatchCompute() {
324 gpu_memory->FlushCaching(); 366 gpu_memory->FlushCaching();
325 ComputePipeline* const pipeline{shader_cache.CurrentComputePipeline()}; 367 ComputePipeline* const pipeline{shader_cache.CurrentComputePipeline()};
diff --git a/src/video_core/renderer_opengl/gl_rasterizer.h b/src/video_core/renderer_opengl/gl_rasterizer.h
index be4f76c18..0c45832ae 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer.h
+++ b/src/video_core/renderer_opengl/gl_rasterizer.h
@@ -16,6 +16,7 @@
16#include "video_core/engines/maxwell_dma.h" 16#include "video_core/engines/maxwell_dma.h"
17#include "video_core/rasterizer_accelerated.h" 17#include "video_core/rasterizer_accelerated.h"
18#include "video_core/rasterizer_interface.h" 18#include "video_core/rasterizer_interface.h"
19#include "video_core/renderer_opengl/blit_image.h"
19#include "video_core/renderer_opengl/gl_buffer_cache.h" 20#include "video_core/renderer_opengl/gl_buffer_cache.h"
20#include "video_core/renderer_opengl/gl_device.h" 21#include "video_core/renderer_opengl/gl_device.h"
21#include "video_core/renderer_opengl/gl_fence_manager.h" 22#include "video_core/renderer_opengl/gl_fence_manager.h"
@@ -70,6 +71,7 @@ public:
70 71
71 void Draw(bool is_indexed, u32 instance_count) override; 72 void Draw(bool is_indexed, u32 instance_count) override;
72 void DrawIndirect() override; 73 void DrawIndirect() override;
74 void DrawTexture() override;
73 void Clear(u32 layer_count) override; 75 void Clear(u32 layer_count) override;
74 void DispatchCompute() override; 76 void DispatchCompute() override;
75 void ResetCounter(VideoCore::QueryType type) override; 77 void ResetCounter(VideoCore::QueryType type) override;
@@ -224,6 +226,8 @@ private:
224 AccelerateDMA accelerate_dma; 226 AccelerateDMA accelerate_dma;
225 FenceManagerOpenGL fence_manager; 227 FenceManagerOpenGL fence_manager;
226 228
229 BlitImageHelper blit_image;
230
227 boost::container::static_vector<u32, MAX_IMAGE_VIEWS> image_view_indices; 231 boost::container::static_vector<u32, MAX_IMAGE_VIEWS> image_view_indices;
228 std::array<ImageViewId, MAX_IMAGE_VIEWS> image_view_ids; 232 std::array<ImageViewId, MAX_IMAGE_VIEWS> image_view_ids;
229 boost::container::static_vector<GLuint, MAX_TEXTURES> sampler_handles; 233 boost::container::static_vector<GLuint, MAX_TEXTURES> sampler_handles;
diff --git a/src/video_core/renderer_opengl/gl_shader_cache.cpp b/src/video_core/renderer_opengl/gl_shader_cache.cpp
index 7dd854e0f..626ea7dcb 100644
--- a/src/video_core/renderer_opengl/gl_shader_cache.cpp
+++ b/src/video_core/renderer_opengl/gl_shader_cache.cpp
@@ -286,7 +286,7 @@ void ShaderCache::LoadDiskResources(u64 title_id, std::stop_token stop_loading,
286 file.read(reinterpret_cast<char*>(&key), sizeof(key)); 286 file.read(reinterpret_cast<char*>(&key), sizeof(key));
287 queue_work([this, key, env = std::move(env), &state, &callback](Context* ctx) mutable { 287 queue_work([this, key, env = std::move(env), &state, &callback](Context* ctx) mutable {
288 ctx->pools.ReleaseContents(); 288 ctx->pools.ReleaseContents();
289 auto pipeline{CreateComputePipeline(ctx->pools, key, env)}; 289 auto pipeline{CreateComputePipeline(ctx->pools, key, env, true)};
290 std::scoped_lock lock{state.mutex}; 290 std::scoped_lock lock{state.mutex};
291 if (pipeline) { 291 if (pipeline) {
292 compute_cache.emplace(key, std::move(pipeline)); 292 compute_cache.emplace(key, std::move(pipeline));
@@ -307,7 +307,7 @@ void ShaderCache::LoadDiskResources(u64 title_id, std::stop_token stop_loading,
307 env_ptrs.push_back(&env); 307 env_ptrs.push_back(&env);
308 } 308 }
309 ctx->pools.ReleaseContents(); 309 ctx->pools.ReleaseContents();
310 auto pipeline{CreateGraphicsPipeline(ctx->pools, key, MakeSpan(env_ptrs), false)}; 310 auto pipeline{CreateGraphicsPipeline(ctx->pools, key, MakeSpan(env_ptrs), false, true)};
311 std::scoped_lock lock{state.mutex}; 311 std::scoped_lock lock{state.mutex};
312 if (pipeline) { 312 if (pipeline) {
313 graphics_cache.emplace(key, std::move(pipeline)); 313 graphics_cache.emplace(key, std::move(pipeline));
@@ -439,7 +439,8 @@ std::unique_ptr<GraphicsPipeline> ShaderCache::CreateGraphicsPipeline() {
439 439
440std::unique_ptr<GraphicsPipeline> ShaderCache::CreateGraphicsPipeline( 440std::unique_ptr<GraphicsPipeline> ShaderCache::CreateGraphicsPipeline(
441 ShaderContext::ShaderPools& pools, const GraphicsPipelineKey& key, 441 ShaderContext::ShaderPools& pools, const GraphicsPipelineKey& key,
442 std::span<Shader::Environment* const> envs, bool build_in_parallel) try { 442 std::span<Shader::Environment* const> envs, bool use_shader_workers,
443 bool force_context_flush) try {
443 LOG_INFO(Render_OpenGL, "0x{:016x}", key.Hash()); 444 LOG_INFO(Render_OpenGL, "0x{:016x}", key.Hash());
444 size_t env_index{}; 445 size_t env_index{};
445 u32 total_storage_buffers{}; 446 u32 total_storage_buffers{};
@@ -531,10 +532,10 @@ std::unique_ptr<GraphicsPipeline> ShaderCache::CreateGraphicsPipeline(
531 } 532 }
532 previous_program = &program; 533 previous_program = &program;
533 } 534 }
534 auto* const thread_worker{build_in_parallel ? workers.get() : nullptr}; 535 auto* const thread_worker{use_shader_workers ? workers.get() : nullptr};
535 return std::make_unique<GraphicsPipeline>(device, texture_cache, buffer_cache, program_manager, 536 return std::make_unique<GraphicsPipeline>(device, texture_cache, buffer_cache, program_manager,
536 state_tracker, thread_worker, &shader_notify, sources, 537 state_tracker, thread_worker, &shader_notify, sources,
537 sources_spirv, infos, key); 538 sources_spirv, infos, key, force_context_flush);
538 539
539} catch (Shader::Exception& exception) { 540} catch (Shader::Exception& exception) {
540 LOG_ERROR(Render_OpenGL, "{}", exception.what()); 541 LOG_ERROR(Render_OpenGL, "{}", exception.what());
@@ -559,8 +560,8 @@ std::unique_ptr<ComputePipeline> ShaderCache::CreateComputePipeline(
559} 560}
560 561
561std::unique_ptr<ComputePipeline> ShaderCache::CreateComputePipeline( 562std::unique_ptr<ComputePipeline> ShaderCache::CreateComputePipeline(
562 ShaderContext::ShaderPools& pools, const ComputePipelineKey& key, 563 ShaderContext::ShaderPools& pools, const ComputePipelineKey& key, Shader::Environment& env,
563 Shader::Environment& env) try { 564 bool force_context_flush) try {
564 LOG_INFO(Render_OpenGL, "0x{:016x}", key.Hash()); 565 LOG_INFO(Render_OpenGL, "0x{:016x}", key.Hash());
565 566
566 Shader::Maxwell::Flow::CFG cfg{env, pools.flow_block, env.StartAddress()}; 567 Shader::Maxwell::Flow::CFG cfg{env, pools.flow_block, env.StartAddress()};
@@ -589,7 +590,7 @@ std::unique_ptr<ComputePipeline> ShaderCache::CreateComputePipeline(
589 } 590 }
590 591
591 return std::make_unique<ComputePipeline>(device, texture_cache, buffer_cache, program_manager, 592 return std::make_unique<ComputePipeline>(device, texture_cache, buffer_cache, program_manager,
592 program.info, code, code_spirv); 593 program.info, code, code_spirv, force_context_flush);
593} catch (Shader::Exception& exception) { 594} catch (Shader::Exception& exception) {
594 LOG_ERROR(Render_OpenGL, "{}", exception.what()); 595 LOG_ERROR(Render_OpenGL, "{}", exception.what());
595 return nullptr; 596 return nullptr;
diff --git a/src/video_core/renderer_opengl/gl_shader_cache.h b/src/video_core/renderer_opengl/gl_shader_cache.h
index f82420592..6b9732fca 100644
--- a/src/video_core/renderer_opengl/gl_shader_cache.h
+++ b/src/video_core/renderer_opengl/gl_shader_cache.h
@@ -50,14 +50,16 @@ private:
50 50
51 std::unique_ptr<GraphicsPipeline> CreateGraphicsPipeline( 51 std::unique_ptr<GraphicsPipeline> CreateGraphicsPipeline(
52 ShaderContext::ShaderPools& pools, const GraphicsPipelineKey& key, 52 ShaderContext::ShaderPools& pools, const GraphicsPipelineKey& key,
53 std::span<Shader::Environment* const> envs, bool build_in_parallel); 53 std::span<Shader::Environment* const> envs, bool use_shader_workers,
54 bool force_context_flush = false);
54 55
55 std::unique_ptr<ComputePipeline> CreateComputePipeline(const ComputePipelineKey& key, 56 std::unique_ptr<ComputePipeline> CreateComputePipeline(const ComputePipelineKey& key,
56 const VideoCommon::ShaderInfo* shader); 57 const VideoCommon::ShaderInfo* shader);
57 58
58 std::unique_ptr<ComputePipeline> CreateComputePipeline(ShaderContext::ShaderPools& pools, 59 std::unique_ptr<ComputePipeline> CreateComputePipeline(ShaderContext::ShaderPools& pools,
59 const ComputePipelineKey& key, 60 const ComputePipelineKey& key,
60 Shader::Environment& env); 61 Shader::Environment& env,
62 bool force_context_flush = false);
61 63
62 std::unique_ptr<ShaderWorker> CreateWorkers() const; 64 std::unique_ptr<ShaderWorker> CreateWorkers() const;
63 65
diff --git a/src/video_core/renderer_opengl/gl_shader_manager.cpp b/src/video_core/renderer_opengl/gl_shader_manager.cpp
index d9c29d8b7..98841ae65 100644
--- a/src/video_core/renderer_opengl/gl_shader_manager.cpp
+++ b/src/video_core/renderer_opengl/gl_shader_manager.cpp
@@ -1,2 +1,123 @@
1// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project 1// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later 2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include <glad/glad.h>
5
6#include "video_core/renderer_opengl/gl_shader_manager.h"
7
8namespace OpenGL {
9
10static constexpr std::array ASSEMBLY_PROGRAM_ENUMS{
11 GL_VERTEX_PROGRAM_NV, GL_TESS_CONTROL_PROGRAM_NV, GL_TESS_EVALUATION_PROGRAM_NV,
12 GL_GEOMETRY_PROGRAM_NV, GL_FRAGMENT_PROGRAM_NV,
13};
14
15ProgramManager::ProgramManager(const Device& device) {
16 glCreateProgramPipelines(1, &pipeline.handle);
17 if (device.UseAssemblyShaders()) {
18 glEnable(GL_COMPUTE_PROGRAM_NV);
19 }
20}
21
22void ProgramManager::BindComputeProgram(GLuint program) {
23 glUseProgram(program);
24 is_compute_bound = true;
25}
26
27void ProgramManager::BindComputeAssemblyProgram(GLuint program) {
28 if (current_assembly_compute_program != program) {
29 current_assembly_compute_program = program;
30 glBindProgramARB(GL_COMPUTE_PROGRAM_NV, program);
31 }
32 UnbindPipeline();
33}
34
35void ProgramManager::BindSourcePrograms(std::span<const OGLProgram, NUM_STAGES> programs) {
36 static constexpr std::array<GLenum, 5> stage_enums{
37 GL_VERTEX_SHADER_BIT, GL_TESS_CONTROL_SHADER_BIT, GL_TESS_EVALUATION_SHADER_BIT,
38 GL_GEOMETRY_SHADER_BIT, GL_FRAGMENT_SHADER_BIT,
39 };
40 for (size_t stage = 0; stage < NUM_STAGES; ++stage) {
41 if (current_programs[stage] != programs[stage].handle) {
42 current_programs[stage] = programs[stage].handle;
43 glUseProgramStages(pipeline.handle, stage_enums[stage], programs[stage].handle);
44 }
45 }
46 BindPipeline();
47}
48
49void ProgramManager::BindPresentPrograms(GLuint vertex, GLuint fragment) {
50 if (current_programs[0] != vertex) {
51 current_programs[0] = vertex;
52 glUseProgramStages(pipeline.handle, GL_VERTEX_SHADER_BIT, vertex);
53 }
54 if (current_programs[4] != fragment) {
55 current_programs[4] = fragment;
56 glUseProgramStages(pipeline.handle, GL_FRAGMENT_SHADER_BIT, fragment);
57 }
58 glUseProgramStages(
59 pipeline.handle,
60 GL_TESS_CONTROL_SHADER_BIT | GL_TESS_EVALUATION_SHADER_BIT | GL_GEOMETRY_SHADER_BIT, 0);
61 current_programs[1] = 0;
62 current_programs[2] = 0;
63 current_programs[3] = 0;
64
65 if (current_stage_mask != 0) {
66 current_stage_mask = 0;
67 for (const GLenum program_type : ASSEMBLY_PROGRAM_ENUMS) {
68 glDisable(program_type);
69 }
70 }
71 BindPipeline();
72}
73
74void ProgramManager::BindAssemblyPrograms(std::span<const OGLAssemblyProgram, NUM_STAGES> programs,
75 u32 stage_mask) {
76 const u32 changed_mask = current_stage_mask ^ stage_mask;
77 current_stage_mask = stage_mask;
78
79 if (changed_mask != 0) {
80 for (size_t stage = 0; stage < NUM_STAGES; ++stage) {
81 if (((changed_mask >> stage) & 1) != 0) {
82 if (((stage_mask >> stage) & 1) != 0) {
83 glEnable(ASSEMBLY_PROGRAM_ENUMS[stage]);
84 } else {
85 glDisable(ASSEMBLY_PROGRAM_ENUMS[stage]);
86 }
87 }
88 }
89 }
90 for (size_t stage = 0; stage < NUM_STAGES; ++stage) {
91 if (current_programs[stage] != programs[stage].handle) {
92 current_programs[stage] = programs[stage].handle;
93 glBindProgramARB(ASSEMBLY_PROGRAM_ENUMS[stage], programs[stage].handle);
94 }
95 }
96 UnbindPipeline();
97}
98
99void ProgramManager::RestoreGuestCompute() {}
100
101void ProgramManager::BindPipeline() {
102 if (!is_pipeline_bound) {
103 is_pipeline_bound = true;
104 glBindProgramPipeline(pipeline.handle);
105 }
106 UnbindCompute();
107}
108
109void ProgramManager::UnbindPipeline() {
110 if (is_pipeline_bound) {
111 is_pipeline_bound = false;
112 glBindProgramPipeline(0);
113 }
114 UnbindCompute();
115}
116
117void ProgramManager::UnbindCompute() {
118 if (is_compute_bound) {
119 is_compute_bound = false;
120 glUseProgram(0);
121 }
122}
123} // namespace OpenGL
diff --git a/src/video_core/renderer_opengl/gl_shader_manager.h b/src/video_core/renderer_opengl/gl_shader_manager.h
index a84f5aeb3..07ffab77f 100644
--- a/src/video_core/renderer_opengl/gl_shader_manager.h
+++ b/src/video_core/renderer_opengl/gl_shader_manager.h
@@ -6,8 +6,6 @@
6#include <array> 6#include <array>
7#include <span> 7#include <span>
8 8
9#include <glad/glad.h>
10
11#include "video_core/renderer_opengl/gl_device.h" 9#include "video_core/renderer_opengl/gl_device.h"
12#include "video_core/renderer_opengl/gl_resource_manager.h" 10#include "video_core/renderer_opengl/gl_resource_manager.h"
13 11
@@ -16,121 +14,28 @@ namespace OpenGL {
16class ProgramManager { 14class ProgramManager {
17 static constexpr size_t NUM_STAGES = 5; 15 static constexpr size_t NUM_STAGES = 5;
18 16
19 static constexpr std::array ASSEMBLY_PROGRAM_ENUMS{
20 GL_VERTEX_PROGRAM_NV, GL_TESS_CONTROL_PROGRAM_NV, GL_TESS_EVALUATION_PROGRAM_NV,
21 GL_GEOMETRY_PROGRAM_NV, GL_FRAGMENT_PROGRAM_NV,
22 };
23
24public: 17public:
25 explicit ProgramManager(const Device& device) { 18 explicit ProgramManager(const Device& device);
26 glCreateProgramPipelines(1, &pipeline.handle); 19
27 if (device.UseAssemblyShaders()) { 20 void BindComputeProgram(GLuint program);
28 glEnable(GL_COMPUTE_PROGRAM_NV); 21
29 } 22 void BindComputeAssemblyProgram(GLuint program);
30 } 23
31 24 void BindSourcePrograms(std::span<const OGLProgram, NUM_STAGES> programs);
32 void BindComputeProgram(GLuint program) { 25
33 glUseProgram(program); 26 void BindPresentPrograms(GLuint vertex, GLuint fragment);
34 is_compute_bound = true;
35 }
36
37 void BindComputeAssemblyProgram(GLuint program) {
38 if (current_assembly_compute_program != program) {
39 current_assembly_compute_program = program;
40 glBindProgramARB(GL_COMPUTE_PROGRAM_NV, program);
41 }
42 UnbindPipeline();
43 }
44
45 void BindSourcePrograms(std::span<const OGLProgram, NUM_STAGES> programs) {
46 static constexpr std::array<GLenum, 5> stage_enums{
47 GL_VERTEX_SHADER_BIT, GL_TESS_CONTROL_SHADER_BIT, GL_TESS_EVALUATION_SHADER_BIT,
48 GL_GEOMETRY_SHADER_BIT, GL_FRAGMENT_SHADER_BIT,
49 };
50 for (size_t stage = 0; stage < NUM_STAGES; ++stage) {
51 if (current_programs[stage] != programs[stage].handle) {
52 current_programs[stage] = programs[stage].handle;
53 glUseProgramStages(pipeline.handle, stage_enums[stage], programs[stage].handle);
54 }
55 }
56 BindPipeline();
57 }
58
59 void BindPresentPrograms(GLuint vertex, GLuint fragment) {
60 if (current_programs[0] != vertex) {
61 current_programs[0] = vertex;
62 glUseProgramStages(pipeline.handle, GL_VERTEX_SHADER_BIT, vertex);
63 }
64 if (current_programs[4] != fragment) {
65 current_programs[4] = fragment;
66 glUseProgramStages(pipeline.handle, GL_FRAGMENT_SHADER_BIT, fragment);
67 }
68 glUseProgramStages(
69 pipeline.handle,
70 GL_TESS_CONTROL_SHADER_BIT | GL_TESS_EVALUATION_SHADER_BIT | GL_GEOMETRY_SHADER_BIT, 0);
71 current_programs[1] = 0;
72 current_programs[2] = 0;
73 current_programs[3] = 0;
74
75 if (current_stage_mask != 0) {
76 current_stage_mask = 0;
77 for (const GLenum program_type : ASSEMBLY_PROGRAM_ENUMS) {
78 glDisable(program_type);
79 }
80 }
81 BindPipeline();
82 }
83 27
84 void BindAssemblyPrograms(std::span<const OGLAssemblyProgram, NUM_STAGES> programs, 28 void BindAssemblyPrograms(std::span<const OGLAssemblyProgram, NUM_STAGES> programs,
85 u32 stage_mask) { 29 u32 stage_mask);
86 const u32 changed_mask = current_stage_mask ^ stage_mask; 30
87 current_stage_mask = stage_mask; 31 void RestoreGuestCompute();
88
89 if (changed_mask != 0) {
90 for (size_t stage = 0; stage < NUM_STAGES; ++stage) {
91 if (((changed_mask >> stage) & 1) != 0) {
92 if (((stage_mask >> stage) & 1) != 0) {
93 glEnable(ASSEMBLY_PROGRAM_ENUMS[stage]);
94 } else {
95 glDisable(ASSEMBLY_PROGRAM_ENUMS[stage]);
96 }
97 }
98 }
99 }
100 for (size_t stage = 0; stage < NUM_STAGES; ++stage) {
101 if (current_programs[stage] != programs[stage].handle) {
102 current_programs[stage] = programs[stage].handle;
103 glBindProgramARB(ASSEMBLY_PROGRAM_ENUMS[stage], programs[stage].handle);
104 }
105 }
106 UnbindPipeline();
107 }
108
109 void RestoreGuestCompute() {}
110 32
111private: 33private:
112 void BindPipeline() { 34 void BindPipeline();
113 if (!is_pipeline_bound) { 35
114 is_pipeline_bound = true; 36 void UnbindPipeline();
115 glBindProgramPipeline(pipeline.handle); 37
116 } 38 void UnbindCompute();
117 UnbindCompute();
118 }
119
120 void UnbindPipeline() {
121 if (is_pipeline_bound) {
122 is_pipeline_bound = false;
123 glBindProgramPipeline(0);
124 }
125 UnbindCompute();
126 }
127
128 void UnbindCompute() {
129 if (is_compute_bound) {
130 is_compute_bound = false;
131 glUseProgram(0);
132 }
133 }
134 39
135 OGLPipeline pipeline; 40 OGLPipeline pipeline;
136 bool is_pipeline_bound{}; 41 bool is_pipeline_bound{};
diff --git a/src/video_core/renderer_opengl/renderer_opengl.cpp b/src/video_core/renderer_opengl/renderer_opengl.cpp
index de95f2634..2a74c1d05 100644
--- a/src/video_core/renderer_opengl/renderer_opengl.cpp
+++ b/src/video_core/renderer_opengl/renderer_opengl.cpp
@@ -17,8 +17,14 @@
17#include "core/frontend/emu_window.h" 17#include "core/frontend/emu_window.h"
18#include "core/memory.h" 18#include "core/memory.h"
19#include "core/telemetry_session.h" 19#include "core/telemetry_session.h"
20#include "video_core/host_shaders/ffx_a_h.h"
21#include "video_core/host_shaders/ffx_fsr1_h.h"
22#include "video_core/host_shaders/full_screen_triangle_vert.h"
20#include "video_core/host_shaders/fxaa_frag.h" 23#include "video_core/host_shaders/fxaa_frag.h"
21#include "video_core/host_shaders/fxaa_vert.h" 24#include "video_core/host_shaders/fxaa_vert.h"
25#include "video_core/host_shaders/opengl_fidelityfx_fsr_easu_frag.h"
26#include "video_core/host_shaders/opengl_fidelityfx_fsr_frag.h"
27#include "video_core/host_shaders/opengl_fidelityfx_fsr_rcas_frag.h"
22#include "video_core/host_shaders/opengl_present_frag.h" 28#include "video_core/host_shaders/opengl_present_frag.h"
23#include "video_core/host_shaders/opengl_present_scaleforce_frag.h" 29#include "video_core/host_shaders/opengl_present_scaleforce_frag.h"
24#include "video_core/host_shaders/opengl_present_vert.h" 30#include "video_core/host_shaders/opengl_present_vert.h"
@@ -31,6 +37,7 @@
31#include "video_core/host_shaders/smaa_edge_detection_vert.h" 37#include "video_core/host_shaders/smaa_edge_detection_vert.h"
32#include "video_core/host_shaders/smaa_neighborhood_blending_frag.h" 38#include "video_core/host_shaders/smaa_neighborhood_blending_frag.h"
33#include "video_core/host_shaders/smaa_neighborhood_blending_vert.h" 39#include "video_core/host_shaders/smaa_neighborhood_blending_vert.h"
40#include "video_core/renderer_opengl/gl_fsr.h"
34#include "video_core/renderer_opengl/gl_rasterizer.h" 41#include "video_core/renderer_opengl/gl_rasterizer.h"
35#include "video_core/renderer_opengl/gl_shader_manager.h" 42#include "video_core/renderer_opengl/gl_shader_manager.h"
36#include "video_core/renderer_opengl/gl_shader_util.h" 43#include "video_core/renderer_opengl/gl_shader_util.h"
@@ -268,12 +275,17 @@ void RendererOpenGL::InitOpenGLObjects() {
268 fxaa_vertex = CreateProgram(HostShaders::FXAA_VERT, GL_VERTEX_SHADER); 275 fxaa_vertex = CreateProgram(HostShaders::FXAA_VERT, GL_VERTEX_SHADER);
269 fxaa_fragment = CreateProgram(HostShaders::FXAA_FRAG, GL_FRAGMENT_SHADER); 276 fxaa_fragment = CreateProgram(HostShaders::FXAA_FRAG, GL_FRAGMENT_SHADER);
270 277
271 const auto SmaaShader = [](std::string_view specialized_source, GLenum stage) { 278 const auto replace_include = [](std::string& shader_source, std::string_view include_name,
272 std::string shader_source{specialized_source}; 279 std::string_view include_content) {
273 constexpr std::string_view include_string = "#include \"opengl_smaa.glsl\""; 280 const std::string include_string = fmt::format("#include \"{}\"", include_name);
274 const std::size_t pos = shader_source.find(include_string); 281 const std::size_t pos = shader_source.find(include_string);
275 ASSERT(pos != std::string::npos); 282 ASSERT(pos != std::string::npos);
276 shader_source.replace(pos, include_string.size(), HostShaders::OPENGL_SMAA_GLSL); 283 shader_source.replace(pos, include_string.size(), include_content);
284 };
285
286 const auto SmaaShader = [&](std::string_view specialized_source, GLenum stage) {
287 std::string shader_source{specialized_source};
288 replace_include(shader_source, "opengl_smaa.glsl", HostShaders::OPENGL_SMAA_GLSL);
277 return CreateProgram(shader_source, stage); 289 return CreateProgram(shader_source, stage);
278 }; 290 };
279 291
@@ -298,14 +310,32 @@ void RendererOpenGL::InitOpenGLObjects() {
298 CreateProgram(fmt::format("#version 460\n{}", HostShaders::OPENGL_PRESENT_SCALEFORCE_FRAG), 310 CreateProgram(fmt::format("#version 460\n{}", HostShaders::OPENGL_PRESENT_SCALEFORCE_FRAG),
299 GL_FRAGMENT_SHADER); 311 GL_FRAGMENT_SHADER);
300 312
313 std::string fsr_source{HostShaders::OPENGL_FIDELITYFX_FSR_FRAG};
314 replace_include(fsr_source, "ffx_a.h", HostShaders::FFX_A_H);
315 replace_include(fsr_source, "ffx_fsr1.h", HostShaders::FFX_FSR1_H);
316
317 std::string fsr_easu_frag_source{HostShaders::OPENGL_FIDELITYFX_FSR_EASU_FRAG};
318 std::string fsr_rcas_frag_source{HostShaders::OPENGL_FIDELITYFX_FSR_RCAS_FRAG};
319 replace_include(fsr_easu_frag_source, "opengl_fidelityfx_fsr.frag", fsr_source);
320 replace_include(fsr_rcas_frag_source, "opengl_fidelityfx_fsr.frag", fsr_source);
321
322 fsr = std::make_unique<FSR>(HostShaders::FULL_SCREEN_TRIANGLE_VERT, fsr_easu_frag_source,
323 fsr_rcas_frag_source);
324
301 // Generate presentation sampler 325 // Generate presentation sampler
302 present_sampler.Create(); 326 present_sampler.Create();
303 glSamplerParameteri(present_sampler.handle, GL_TEXTURE_MIN_FILTER, GL_LINEAR); 327 glSamplerParameteri(present_sampler.handle, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
304 glSamplerParameteri(present_sampler.handle, GL_TEXTURE_MAG_FILTER, GL_LINEAR); 328 glSamplerParameteri(present_sampler.handle, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
329 glSamplerParameteri(present_sampler.handle, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
330 glSamplerParameteri(present_sampler.handle, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
331 glSamplerParameteri(present_sampler.handle, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
305 332
306 present_sampler_nn.Create(); 333 present_sampler_nn.Create();
307 glSamplerParameteri(present_sampler_nn.handle, GL_TEXTURE_MIN_FILTER, GL_NEAREST); 334 glSamplerParameteri(present_sampler_nn.handle, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
308 glSamplerParameteri(present_sampler_nn.handle, GL_TEXTURE_MAG_FILTER, GL_NEAREST); 335 glSamplerParameteri(present_sampler_nn.handle, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
336 glSamplerParameteri(present_sampler_nn.handle, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
337 glSamplerParameteri(present_sampler_nn.handle, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
338 glSamplerParameteri(present_sampler_nn.handle, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
309 339
310 // Generate VBO handle for drawing 340 // Generate VBO handle for drawing
311 vertex_buffer.Create(); 341 vertex_buffer.Create();
@@ -525,6 +555,31 @@ void RendererOpenGL::DrawScreen(const Layout::FramebufferLayout& layout) {
525 555
526 glBindTextureUnit(0, aa_texture.handle); 556 glBindTextureUnit(0, aa_texture.handle);
527 } 557 }
558 glDisablei(GL_SCISSOR_TEST, 0);
559
560 if (Settings::values.scaling_filter.GetValue() == Settings::ScalingFilter::Fsr) {
561 if (!fsr->AreBuffersInitialized()) {
562 fsr->InitBuffers();
563 }
564
565 auto crop_rect = framebuffer_crop_rect;
566 if (crop_rect.GetWidth() == 0) {
567 crop_rect.right = framebuffer_width;
568 }
569 if (crop_rect.GetHeight() == 0) {
570 crop_rect.bottom = framebuffer_height;
571 }
572 crop_rect = crop_rect.Scale(Settings::values.resolution_info.up_factor);
573 const auto fsr_input_width = Settings::values.resolution_info.ScaleUp(framebuffer_width);
574 const auto fsr_input_height = Settings::values.resolution_info.ScaleUp(framebuffer_height);
575 glBindSampler(0, present_sampler.handle);
576 fsr->Draw(program_manager, layout.screen, fsr_input_width, fsr_input_height, crop_rect);
577 } else {
578 if (fsr->AreBuffersInitialized()) {
579 fsr->ReleaseBuffers();
580 }
581 }
582
528 const std::array ortho_matrix = 583 const std::array ortho_matrix =
529 MakeOrthographicMatrix(static_cast<float>(layout.width), static_cast<float>(layout.height)); 584 MakeOrthographicMatrix(static_cast<float>(layout.width), static_cast<float>(layout.height));
530 585
@@ -540,10 +595,7 @@ void RendererOpenGL::DrawScreen(const Layout::FramebufferLayout& layout) {
540 case Settings::ScalingFilter::ScaleForce: 595 case Settings::ScalingFilter::ScaleForce:
541 return present_scaleforce_fragment.handle; 596 return present_scaleforce_fragment.handle;
542 case Settings::ScalingFilter::Fsr: 597 case Settings::ScalingFilter::Fsr:
543 LOG_WARNING( 598 return fsr->GetPresentFragmentProgram().handle;
544 Render_OpenGL,
545 "FidelityFX Super Resolution is not supported in OpenGL, changing to ScaleForce");
546 return present_scaleforce_fragment.handle;
547 default: 599 default:
548 return present_bilinear_fragment.handle; 600 return present_bilinear_fragment.handle;
549 } 601 }
@@ -578,15 +630,18 @@ void RendererOpenGL::DrawScreen(const Layout::FramebufferLayout& layout) {
578 f32 scale_u = static_cast<f32>(framebuffer_width) / static_cast<f32>(screen_info.texture.width); 630 f32 scale_u = static_cast<f32>(framebuffer_width) / static_cast<f32>(screen_info.texture.width);
579 f32 scale_v = 631 f32 scale_v =
580 static_cast<f32>(framebuffer_height) / static_cast<f32>(screen_info.texture.height); 632 static_cast<f32>(framebuffer_height) / static_cast<f32>(screen_info.texture.height);
581 // Scale the output by the crop width/height. This is commonly used with 1280x720 rendering 633
582 // (e.g. handheld mode) on a 1920x1080 framebuffer. 634 if (Settings::values.scaling_filter.GetValue() != Settings::ScalingFilter::Fsr) {
583 if (framebuffer_crop_rect.GetWidth() > 0) { 635 // Scale the output by the crop width/height. This is commonly used with 1280x720 rendering
584 scale_u = static_cast<f32>(framebuffer_crop_rect.GetWidth()) / 636 // (e.g. handheld mode) on a 1920x1080 framebuffer.
585 static_cast<f32>(screen_info.texture.width); 637 if (framebuffer_crop_rect.GetWidth() > 0) {
586 } 638 scale_u = static_cast<f32>(framebuffer_crop_rect.GetWidth()) /
587 if (framebuffer_crop_rect.GetHeight() > 0) { 639 static_cast<f32>(screen_info.texture.width);
588 scale_v = static_cast<f32>(framebuffer_crop_rect.GetHeight()) / 640 }
589 static_cast<f32>(screen_info.texture.height); 641 if (framebuffer_crop_rect.GetHeight() > 0) {
642 scale_v = static_cast<f32>(framebuffer_crop_rect.GetHeight()) /
643 static_cast<f32>(screen_info.texture.height);
644 }
590 } 645 }
591 if (Settings::values.anti_aliasing.GetValue() == Settings::AntiAliasing::Fxaa && 646 if (Settings::values.anti_aliasing.GetValue() == Settings::AntiAliasing::Fxaa &&
592 !screen_info.was_accelerated) { 647 !screen_info.was_accelerated) {
@@ -612,7 +667,6 @@ void RendererOpenGL::DrawScreen(const Layout::FramebufferLayout& layout) {
612 } else { 667 } else {
613 glDisable(GL_FRAMEBUFFER_SRGB); 668 glDisable(GL_FRAMEBUFFER_SRGB);
614 } 669 }
615 glDisablei(GL_SCISSOR_TEST, 0);
616 glViewportIndexedf(0, 0.0f, 0.0f, static_cast<GLfloat>(layout.width), 670 glViewportIndexedf(0, 0.0f, 0.0f, static_cast<GLfloat>(layout.width),
617 static_cast<GLfloat>(layout.height)); 671 static_cast<GLfloat>(layout.height));
618 672
diff --git a/src/video_core/renderer_opengl/renderer_opengl.h b/src/video_core/renderer_opengl/renderer_opengl.h
index cc97d7b26..f1d5fd954 100644
--- a/src/video_core/renderer_opengl/renderer_opengl.h
+++ b/src/video_core/renderer_opengl/renderer_opengl.h
@@ -10,6 +10,7 @@
10 10
11#include "video_core/renderer_base.h" 11#include "video_core/renderer_base.h"
12#include "video_core/renderer_opengl/gl_device.h" 12#include "video_core/renderer_opengl/gl_device.h"
13#include "video_core/renderer_opengl/gl_fsr.h"
13#include "video_core/renderer_opengl/gl_rasterizer.h" 14#include "video_core/renderer_opengl/gl_rasterizer.h"
14#include "video_core/renderer_opengl/gl_resource_manager.h" 15#include "video_core/renderer_opengl/gl_resource_manager.h"
15#include "video_core/renderer_opengl/gl_shader_manager.h" 16#include "video_core/renderer_opengl/gl_shader_manager.h"
@@ -141,6 +142,8 @@ private:
141 OGLTexture smaa_edges_tex; 142 OGLTexture smaa_edges_tex;
142 OGLTexture smaa_blend_tex; 143 OGLTexture smaa_blend_tex;
143 144
145 std::unique_ptr<FSR> fsr;
146
144 /// OpenGL framebuffer data 147 /// OpenGL framebuffer data
145 std::vector<u8> gl_framebuffer_data; 148 std::vector<u8> gl_framebuffer_data;
146 149
diff --git a/src/video_core/renderer_vulkan/blit_image.cpp b/src/video_core/renderer_vulkan/blit_image.cpp
index 3f2b139e0..cf2964a3f 100644
--- a/src/video_core/renderer_vulkan/blit_image.cpp
+++ b/src/video_core/renderer_vulkan/blit_image.cpp
@@ -4,14 +4,16 @@
4#include <algorithm> 4#include <algorithm>
5 5
6#include "common/settings.h" 6#include "common/settings.h"
7#include "video_core/host_shaders/blit_color_float_frag_spv.h"
7#include "video_core/host_shaders/convert_abgr8_to_d24s8_frag_spv.h" 8#include "video_core/host_shaders/convert_abgr8_to_d24s8_frag_spv.h"
8#include "video_core/host_shaders/convert_d24s8_to_abgr8_frag_spv.h" 9#include "video_core/host_shaders/convert_d24s8_to_abgr8_frag_spv.h"
9#include "video_core/host_shaders/convert_depth_to_float_frag_spv.h" 10#include "video_core/host_shaders/convert_depth_to_float_frag_spv.h"
10#include "video_core/host_shaders/convert_float_to_depth_frag_spv.h" 11#include "video_core/host_shaders/convert_float_to_depth_frag_spv.h"
11#include "video_core/host_shaders/convert_s8d24_to_abgr8_frag_spv.h" 12#include "video_core/host_shaders/convert_s8d24_to_abgr8_frag_spv.h"
12#include "video_core/host_shaders/full_screen_triangle_vert_spv.h" 13#include "video_core/host_shaders/full_screen_triangle_vert_spv.h"
13#include "video_core/host_shaders/vulkan_blit_color_float_frag_spv.h"
14#include "video_core/host_shaders/vulkan_blit_depth_stencil_frag_spv.h" 14#include "video_core/host_shaders/vulkan_blit_depth_stencil_frag_spv.h"
15#include "video_core/host_shaders/vulkan_color_clear_frag_spv.h"
16#include "video_core/host_shaders/vulkan_color_clear_vert_spv.h"
15#include "video_core/renderer_vulkan/blit_image.h" 17#include "video_core/renderer_vulkan/blit_image.h"
16#include "video_core/renderer_vulkan/maxwell_to_vk.h" 18#include "video_core/renderer_vulkan/maxwell_to_vk.h"
17#include "video_core/renderer_vulkan/vk_scheduler.h" 19#include "video_core/renderer_vulkan/vk_scheduler.h"
@@ -69,10 +71,11 @@ constexpr VkDescriptorSetLayoutCreateInfo TWO_TEXTURES_DESCRIPTOR_SET_LAYOUT_CRE
69 .bindingCount = static_cast<u32>(TWO_TEXTURES_DESCRIPTOR_SET_LAYOUT_BINDINGS.size()), 71 .bindingCount = static_cast<u32>(TWO_TEXTURES_DESCRIPTOR_SET_LAYOUT_BINDINGS.size()),
70 .pBindings = TWO_TEXTURES_DESCRIPTOR_SET_LAYOUT_BINDINGS.data(), 72 .pBindings = TWO_TEXTURES_DESCRIPTOR_SET_LAYOUT_BINDINGS.data(),
71}; 73};
72constexpr VkPushConstantRange PUSH_CONSTANT_RANGE{ 74template <VkShaderStageFlags stageFlags, size_t size>
73 .stageFlags = VK_SHADER_STAGE_VERTEX_BIT, 75inline constexpr VkPushConstantRange PUSH_CONSTANT_RANGE{
76 .stageFlags = stageFlags,
74 .offset = 0, 77 .offset = 0,
75 .size = sizeof(PushConstants), 78 .size = static_cast<u32>(size),
76}; 79};
77constexpr VkPipelineVertexInputStateCreateInfo PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO{ 80constexpr VkPipelineVertexInputStateCreateInfo PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO{
78 .sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO, 81 .sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO,
@@ -125,10 +128,8 @@ constexpr VkPipelineMultisampleStateCreateInfo PIPELINE_MULTISAMPLE_STATE_CREATE
125 .alphaToCoverageEnable = VK_FALSE, 128 .alphaToCoverageEnable = VK_FALSE,
126 .alphaToOneEnable = VK_FALSE, 129 .alphaToOneEnable = VK_FALSE,
127}; 130};
128constexpr std::array DYNAMIC_STATES{ 131constexpr std::array DYNAMIC_STATES{VK_DYNAMIC_STATE_VIEWPORT, VK_DYNAMIC_STATE_SCISSOR,
129 VK_DYNAMIC_STATE_VIEWPORT, 132 VK_DYNAMIC_STATE_BLEND_CONSTANTS};
130 VK_DYNAMIC_STATE_SCISSOR,
131};
132constexpr VkPipelineDynamicStateCreateInfo PIPELINE_DYNAMIC_STATE_CREATE_INFO{ 133constexpr VkPipelineDynamicStateCreateInfo PIPELINE_DYNAMIC_STATE_CREATE_INFO{
133 .sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO, 134 .sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO,
134 .pNext = nullptr, 135 .pNext = nullptr,
@@ -205,15 +206,15 @@ inline constexpr VkSamplerCreateInfo SAMPLER_CREATE_INFO{
205}; 206};
206 207
207constexpr VkPipelineLayoutCreateInfo PipelineLayoutCreateInfo( 208constexpr VkPipelineLayoutCreateInfo PipelineLayoutCreateInfo(
208 const VkDescriptorSetLayout* set_layout) { 209 const VkDescriptorSetLayout* set_layout, vk::Span<VkPushConstantRange> push_constants) {
209 return VkPipelineLayoutCreateInfo{ 210 return VkPipelineLayoutCreateInfo{
210 .sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO, 211 .sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO,
211 .pNext = nullptr, 212 .pNext = nullptr,
212 .flags = 0, 213 .flags = 0,
213 .setLayoutCount = 1, 214 .setLayoutCount = (set_layout != nullptr ? 1u : 0u),
214 .pSetLayouts = set_layout, 215 .pSetLayouts = set_layout,
215 .pushConstantRangeCount = 1, 216 .pushConstantRangeCount = push_constants.size(),
216 .pPushConstantRanges = &PUSH_CONSTANT_RANGE, 217 .pPushConstantRanges = push_constants.data(),
217 }; 218 };
218} 219}
219 220
@@ -302,8 +303,7 @@ void UpdateTwoTexturesDescriptorSet(const Device& device, VkDescriptorSet descri
302 device.GetLogical().UpdateDescriptorSets(write_descriptor_sets, nullptr); 303 device.GetLogical().UpdateDescriptorSets(write_descriptor_sets, nullptr);
303} 304}
304 305
305void BindBlitState(vk::CommandBuffer cmdbuf, VkPipelineLayout layout, const Region2D& dst_region, 306void BindBlitState(vk::CommandBuffer cmdbuf, const Region2D& dst_region) {
306 const Region2D& src_region) {
307 const VkOffset2D offset{ 307 const VkOffset2D offset{
308 .x = std::min(dst_region.start.x, dst_region.end.x), 308 .x = std::min(dst_region.start.x, dst_region.end.x),
309 .y = std::min(dst_region.start.y, dst_region.end.y), 309 .y = std::min(dst_region.start.y, dst_region.end.y),
@@ -325,15 +325,23 @@ void BindBlitState(vk::CommandBuffer cmdbuf, VkPipelineLayout layout, const Regi
325 .offset = offset, 325 .offset = offset,
326 .extent = extent, 326 .extent = extent,
327 }; 327 };
328 const float scale_x = static_cast<float>(src_region.end.x - src_region.start.x); 328 cmdbuf.SetViewport(0, viewport);
329 const float scale_y = static_cast<float>(src_region.end.y - src_region.start.y); 329 cmdbuf.SetScissor(0, scissor);
330}
331
332void BindBlitState(vk::CommandBuffer cmdbuf, VkPipelineLayout layout, const Region2D& dst_region,
333 const Region2D& src_region, const Extent3D& src_size = {1, 1, 1}) {
334 BindBlitState(cmdbuf, dst_region);
335 const float scale_x = static_cast<float>(src_region.end.x - src_region.start.x) /
336 static_cast<float>(src_size.width);
337 const float scale_y = static_cast<float>(src_region.end.y - src_region.start.y) /
338 static_cast<float>(src_size.height);
330 const PushConstants push_constants{ 339 const PushConstants push_constants{
331 .tex_scale = {scale_x, scale_y}, 340 .tex_scale = {scale_x, scale_y},
332 .tex_offset = {static_cast<float>(src_region.start.x), 341 .tex_offset = {static_cast<float>(src_region.start.x) / static_cast<float>(src_size.width),
333 static_cast<float>(src_region.start.y)}, 342 static_cast<float>(src_region.start.y) /
343 static_cast<float>(src_size.height)},
334 }; 344 };
335 cmdbuf.SetViewport(0, viewport);
336 cmdbuf.SetScissor(0, scissor);
337 cmdbuf.PushConstants(layout, VK_SHADER_STAGE_VERTEX_BIT, push_constants); 345 cmdbuf.PushConstants(layout, VK_SHADER_STAGE_VERTEX_BIT, push_constants);
338} 346}
339 347
@@ -347,6 +355,51 @@ VkExtent2D GetConversionExtent(const ImageView& src_image_view) {
347 .height = is_rescaled ? resolution.ScaleUp(height) : height, 355 .height = is_rescaled ? resolution.ScaleUp(height) : height,
348 }; 356 };
349} 357}
358
359void TransitionImageLayout(vk::CommandBuffer& cmdbuf, VkImage image, VkImageLayout target_layout,
360 VkImageLayout source_layout = VK_IMAGE_LAYOUT_GENERAL) {
361 constexpr VkFlags flags{VK_ACCESS_COLOR_ATTACHMENT_READ_BIT |
362 VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT | VK_ACCESS_SHADER_READ_BIT};
363 const VkImageMemoryBarrier barrier{
364 .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
365 .pNext = nullptr,
366 .srcAccessMask = flags,
367 .dstAccessMask = flags,
368 .oldLayout = source_layout,
369 .newLayout = target_layout,
370 .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
371 .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
372 .image = image,
373 .subresourceRange{
374 .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
375 .baseMipLevel = 0,
376 .levelCount = 1,
377 .baseArrayLayer = 0,
378 .layerCount = 1,
379 },
380 };
381 cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT,
382 0, barrier);
383}
384
385void BeginRenderPass(vk::CommandBuffer& cmdbuf, const Framebuffer* framebuffer) {
386 const VkRenderPass render_pass = framebuffer->RenderPass();
387 const VkFramebuffer framebuffer_handle = framebuffer->Handle();
388 const VkExtent2D render_area = framebuffer->RenderArea();
389 const VkRenderPassBeginInfo renderpass_bi{
390 .sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO,
391 .pNext = nullptr,
392 .renderPass = render_pass,
393 .framebuffer = framebuffer_handle,
394 .renderArea{
395 .offset{},
396 .extent = render_area,
397 },
398 .clearValueCount = 0,
399 .pClearValues = nullptr,
400 };
401 cmdbuf.BeginRenderPass(renderpass_bi, VK_SUBPASS_CONTENTS_INLINE);
402}
350} // Anonymous namespace 403} // Anonymous namespace
351 404
352BlitImageHelper::BlitImageHelper(const Device& device_, Scheduler& scheduler_, 405BlitImageHelper::BlitImageHelper(const Device& device_, Scheduler& scheduler_,
@@ -360,13 +413,20 @@ BlitImageHelper::BlitImageHelper(const Device& device_, Scheduler& scheduler_,
360 descriptor_pool.Allocator(*one_texture_set_layout, TEXTURE_DESCRIPTOR_BANK_INFO<1>)}, 413 descriptor_pool.Allocator(*one_texture_set_layout, TEXTURE_DESCRIPTOR_BANK_INFO<1>)},
361 two_textures_descriptor_allocator{ 414 two_textures_descriptor_allocator{
362 descriptor_pool.Allocator(*two_textures_set_layout, TEXTURE_DESCRIPTOR_BANK_INFO<2>)}, 415 descriptor_pool.Allocator(*two_textures_set_layout, TEXTURE_DESCRIPTOR_BANK_INFO<2>)},
363 one_texture_pipeline_layout(device.GetLogical().CreatePipelineLayout( 416 one_texture_pipeline_layout(device.GetLogical().CreatePipelineLayout(PipelineLayoutCreateInfo(
364 PipelineLayoutCreateInfo(one_texture_set_layout.address()))), 417 one_texture_set_layout.address(),
365 two_textures_pipeline_layout(device.GetLogical().CreatePipelineLayout( 418 PUSH_CONSTANT_RANGE<VK_SHADER_STAGE_VERTEX_BIT, sizeof(PushConstants)>))),
366 PipelineLayoutCreateInfo(two_textures_set_layout.address()))), 419 two_textures_pipeline_layout(
420 device.GetLogical().CreatePipelineLayout(PipelineLayoutCreateInfo(
421 two_textures_set_layout.address(),
422 PUSH_CONSTANT_RANGE<VK_SHADER_STAGE_VERTEX_BIT, sizeof(PushConstants)>))),
423 clear_color_pipeline_layout(device.GetLogical().CreatePipelineLayout(PipelineLayoutCreateInfo(
424 nullptr, PUSH_CONSTANT_RANGE<VK_SHADER_STAGE_FRAGMENT_BIT, sizeof(float) * 4>))),
367 full_screen_vert(BuildShader(device, FULL_SCREEN_TRIANGLE_VERT_SPV)), 425 full_screen_vert(BuildShader(device, FULL_SCREEN_TRIANGLE_VERT_SPV)),
368 blit_color_to_color_frag(BuildShader(device, VULKAN_BLIT_COLOR_FLOAT_FRAG_SPV)), 426 blit_color_to_color_frag(BuildShader(device, BLIT_COLOR_FLOAT_FRAG_SPV)),
369 blit_depth_stencil_frag(BuildShader(device, VULKAN_BLIT_DEPTH_STENCIL_FRAG_SPV)), 427 blit_depth_stencil_frag(BuildShader(device, VULKAN_BLIT_DEPTH_STENCIL_FRAG_SPV)),
428 clear_color_vert(BuildShader(device, VULKAN_COLOR_CLEAR_VERT_SPV)),
429 clear_color_frag(BuildShader(device, VULKAN_COLOR_CLEAR_FRAG_SPV)),
370 convert_depth_to_float_frag(BuildShader(device, CONVERT_DEPTH_TO_FLOAT_FRAG_SPV)), 430 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)), 431 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)), 432 convert_abgr8_to_d24s8_frag(BuildShader(device, CONVERT_ABGR8_TO_D24S8_FRAG_SPV)),
@@ -404,6 +464,32 @@ void BlitImageHelper::BlitColor(const Framebuffer* dst_framebuffer, VkImageView
404 scheduler.InvalidateState(); 464 scheduler.InvalidateState();
405} 465}
406 466
467void BlitImageHelper::BlitColor(const Framebuffer* dst_framebuffer, VkImageView src_image_view,
468 VkImage src_image, VkSampler src_sampler,
469 const Region2D& dst_region, const Region2D& src_region,
470 const Extent3D& src_size) {
471 const BlitImagePipelineKey key{
472 .renderpass = dst_framebuffer->RenderPass(),
473 .operation = Tegra::Engines::Fermi2D::Operation::SrcCopy,
474 };
475 const VkPipelineLayout layout = *one_texture_pipeline_layout;
476 const VkPipeline pipeline = FindOrEmplaceColorPipeline(key);
477 scheduler.RequestOutsideRenderPassOperationContext();
478 scheduler.Record([this, dst_framebuffer, src_image_view, src_image, src_sampler, dst_region,
479 src_region, src_size, pipeline, layout](vk::CommandBuffer cmdbuf) {
480 TransitionImageLayout(cmdbuf, src_image, VK_IMAGE_LAYOUT_READ_ONLY_OPTIMAL);
481 BeginRenderPass(cmdbuf, dst_framebuffer);
482 const VkDescriptorSet descriptor_set = one_texture_descriptor_allocator.Commit();
483 UpdateOneTextureDescriptorSet(device, descriptor_set, src_sampler, src_image_view);
484 cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline);
485 cmdbuf.BindDescriptorSets(VK_PIPELINE_BIND_POINT_GRAPHICS, layout, 0, descriptor_set,
486 nullptr);
487 BindBlitState(cmdbuf, layout, dst_region, src_region, src_size);
488 cmdbuf.Draw(3, 1, 0, 0);
489 cmdbuf.EndRenderPass();
490 });
491}
492
407void BlitImageHelper::BlitDepthStencil(const Framebuffer* dst_framebuffer, 493void BlitImageHelper::BlitDepthStencil(const Framebuffer* dst_framebuffer,
408 VkImageView src_depth_view, VkImageView src_stencil_view, 494 VkImageView src_depth_view, VkImageView src_stencil_view,
409 const Region2D& dst_region, const Region2D& src_region, 495 const Region2D& dst_region, const Region2D& src_region,
@@ -479,6 +565,30 @@ void BlitImageHelper::ConvertS8D24ToABGR8(const Framebuffer* dst_framebuffer,
479 ConvertDepthStencil(*convert_s8d24_to_abgr8_pipeline, dst_framebuffer, src_image_view); 565 ConvertDepthStencil(*convert_s8d24_to_abgr8_pipeline, dst_framebuffer, src_image_view);
480} 566}
481 567
568void BlitImageHelper::ClearColor(const Framebuffer* dst_framebuffer, u8 color_mask,
569 const std::array<f32, 4>& clear_color,
570 const Region2D& dst_region) {
571 const BlitImagePipelineKey key{
572 .renderpass = dst_framebuffer->RenderPass(),
573 .operation = Tegra::Engines::Fermi2D::Operation::BlendPremult,
574 };
575 const VkPipeline pipeline = FindOrEmplaceClearColorPipeline(key);
576 const VkPipelineLayout layout = *clear_color_pipeline_layout;
577 scheduler.RequestRenderpass(dst_framebuffer);
578 scheduler.Record(
579 [pipeline, layout, color_mask, clear_color, dst_region](vk::CommandBuffer cmdbuf) {
580 cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline);
581 const std::array blend_color = {
582 (color_mask & 0x1) ? 1.0f : 0.0f, (color_mask & 0x2) ? 1.0f : 0.0f,
583 (color_mask & 0x4) ? 1.0f : 0.0f, (color_mask & 0x8) ? 1.0f : 0.0f};
584 cmdbuf.SetBlendConstants(blend_color.data());
585 BindBlitState(cmdbuf, dst_region);
586 cmdbuf.PushConstants(layout, VK_SHADER_STAGE_FRAGMENT_BIT, clear_color);
587 cmdbuf.Draw(3, 1, 0, 0);
588 });
589 scheduler.InvalidateState();
590}
591
482void BlitImageHelper::Convert(VkPipeline pipeline, const Framebuffer* dst_framebuffer, 592void BlitImageHelper::Convert(VkPipeline pipeline, const Framebuffer* dst_framebuffer,
483 const ImageView& src_image_view) { 593 const ImageView& src_image_view) {
484 const VkPipelineLayout layout = *one_texture_pipeline_layout; 594 const VkPipelineLayout layout = *one_texture_pipeline_layout;
@@ -654,6 +764,58 @@ VkPipeline BlitImageHelper::FindOrEmplaceDepthStencilPipeline(const BlitImagePip
654 return *blit_depth_stencil_pipelines.back(); 764 return *blit_depth_stencil_pipelines.back();
655} 765}
656 766
767VkPipeline BlitImageHelper::FindOrEmplaceClearColorPipeline(const BlitImagePipelineKey& key) {
768 const auto it = std::ranges::find(clear_color_keys, key);
769 if (it != clear_color_keys.end()) {
770 return *clear_color_pipelines[std::distance(clear_color_keys.begin(), it)];
771 }
772 clear_color_keys.push_back(key);
773 const std::array stages = MakeStages(*clear_color_vert, *clear_color_frag);
774 const VkPipelineColorBlendAttachmentState color_blend_attachment_state{
775 .blendEnable = VK_TRUE,
776 .srcColorBlendFactor = VK_BLEND_FACTOR_CONSTANT_COLOR,
777 .dstColorBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_CONSTANT_COLOR,
778 .colorBlendOp = VK_BLEND_OP_ADD,
779 .srcAlphaBlendFactor = VK_BLEND_FACTOR_CONSTANT_ALPHA,
780 .dstAlphaBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_CONSTANT_ALPHA,
781 .alphaBlendOp = VK_BLEND_OP_ADD,
782 .colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT |
783 VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT,
784 };
785 const VkPipelineColorBlendStateCreateInfo color_blend_state_generic_create_info{
786 .sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO,
787 .pNext = nullptr,
788 .flags = 0,
789 .logicOpEnable = VK_FALSE,
790 .logicOp = VK_LOGIC_OP_CLEAR,
791 .attachmentCount = 1,
792 .pAttachments = &color_blend_attachment_state,
793 .blendConstants = {0.0f, 0.0f, 0.0f, 0.0f},
794 };
795 clear_color_pipelines.push_back(device.GetLogical().CreateGraphicsPipeline({
796 .sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO,
797 .pNext = nullptr,
798 .flags = 0,
799 .stageCount = static_cast<u32>(stages.size()),
800 .pStages = stages.data(),
801 .pVertexInputState = &PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO,
802 .pInputAssemblyState = &PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO,
803 .pTessellationState = nullptr,
804 .pViewportState = &PIPELINE_VIEWPORT_STATE_CREATE_INFO,
805 .pRasterizationState = &PIPELINE_RASTERIZATION_STATE_CREATE_INFO,
806 .pMultisampleState = &PIPELINE_MULTISAMPLE_STATE_CREATE_INFO,
807 .pDepthStencilState = &PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO,
808 .pColorBlendState = &color_blend_state_generic_create_info,
809 .pDynamicState = &PIPELINE_DYNAMIC_STATE_CREATE_INFO,
810 .layout = *clear_color_pipeline_layout,
811 .renderPass = key.renderpass,
812 .subpass = 0,
813 .basePipelineHandle = VK_NULL_HANDLE,
814 .basePipelineIndex = 0,
815 }));
816 return *clear_color_pipelines.back();
817}
818
657void BlitImageHelper::ConvertPipeline(vk::Pipeline& pipeline, VkRenderPass renderpass, 819void BlitImageHelper::ConvertPipeline(vk::Pipeline& pipeline, VkRenderPass renderpass,
658 bool is_target_depth) { 820 bool is_target_depth) {
659 if (pipeline) { 821 if (pipeline) {
diff --git a/src/video_core/renderer_vulkan/blit_image.h b/src/video_core/renderer_vulkan/blit_image.h
index 5df679fb4..2976a7d91 100644
--- a/src/video_core/renderer_vulkan/blit_image.h
+++ b/src/video_core/renderer_vulkan/blit_image.h
@@ -10,6 +10,8 @@
10 10
11namespace Vulkan { 11namespace Vulkan {
12 12
13using VideoCommon::Extent3D;
14using VideoCommon::Offset2D;
13using VideoCommon::Region2D; 15using VideoCommon::Region2D;
14 16
15class Device; 17class Device;
@@ -36,6 +38,10 @@ public:
36 Tegra::Engines::Fermi2D::Filter filter, 38 Tegra::Engines::Fermi2D::Filter filter,
37 Tegra::Engines::Fermi2D::Operation operation); 39 Tegra::Engines::Fermi2D::Operation operation);
38 40
41 void BlitColor(const Framebuffer* dst_framebuffer, VkImageView src_image_view,
42 VkImage src_image, VkSampler src_sampler, const Region2D& dst_region,
43 const Region2D& src_region, const Extent3D& src_size);
44
39 void BlitDepthStencil(const Framebuffer* dst_framebuffer, VkImageView src_depth_view, 45 void BlitDepthStencil(const Framebuffer* dst_framebuffer, VkImageView src_depth_view,
40 VkImageView src_stencil_view, const Region2D& dst_region, 46 VkImageView src_stencil_view, const Region2D& dst_region,
41 const Region2D& src_region, Tegra::Engines::Fermi2D::Filter filter, 47 const Region2D& src_region, Tegra::Engines::Fermi2D::Filter filter,
@@ -55,6 +61,9 @@ public:
55 61
56 void ConvertS8D24ToABGR8(const Framebuffer* dst_framebuffer, ImageView& src_image_view); 62 void ConvertS8D24ToABGR8(const Framebuffer* dst_framebuffer, ImageView& src_image_view);
57 63
64 void ClearColor(const Framebuffer* dst_framebuffer, u8 color_mask,
65 const std::array<f32, 4>& clear_color, const Region2D& dst_region);
66
58private: 67private:
59 void Convert(VkPipeline pipeline, const Framebuffer* dst_framebuffer, 68 void Convert(VkPipeline pipeline, const Framebuffer* dst_framebuffer,
60 const ImageView& src_image_view); 69 const ImageView& src_image_view);
@@ -66,6 +75,8 @@ private:
66 75
67 [[nodiscard]] VkPipeline FindOrEmplaceDepthStencilPipeline(const BlitImagePipelineKey& key); 76 [[nodiscard]] VkPipeline FindOrEmplaceDepthStencilPipeline(const BlitImagePipelineKey& key);
68 77
78 [[nodiscard]] VkPipeline FindOrEmplaceClearColorPipeline(const BlitImagePipelineKey& key);
79
69 void ConvertPipeline(vk::Pipeline& pipeline, VkRenderPass renderpass, bool is_target_depth); 80 void ConvertPipeline(vk::Pipeline& pipeline, VkRenderPass renderpass, bool is_target_depth);
70 81
71 void ConvertDepthToColorPipeline(vk::Pipeline& pipeline, VkRenderPass renderpass); 82 void ConvertDepthToColorPipeline(vk::Pipeline& pipeline, VkRenderPass renderpass);
@@ -91,9 +102,12 @@ private:
91 DescriptorAllocator two_textures_descriptor_allocator; 102 DescriptorAllocator two_textures_descriptor_allocator;
92 vk::PipelineLayout one_texture_pipeline_layout; 103 vk::PipelineLayout one_texture_pipeline_layout;
93 vk::PipelineLayout two_textures_pipeline_layout; 104 vk::PipelineLayout two_textures_pipeline_layout;
105 vk::PipelineLayout clear_color_pipeline_layout;
94 vk::ShaderModule full_screen_vert; 106 vk::ShaderModule full_screen_vert;
95 vk::ShaderModule blit_color_to_color_frag; 107 vk::ShaderModule blit_color_to_color_frag;
96 vk::ShaderModule blit_depth_stencil_frag; 108 vk::ShaderModule blit_depth_stencil_frag;
109 vk::ShaderModule clear_color_vert;
110 vk::ShaderModule clear_color_frag;
97 vk::ShaderModule convert_depth_to_float_frag; 111 vk::ShaderModule convert_depth_to_float_frag;
98 vk::ShaderModule convert_float_to_depth_frag; 112 vk::ShaderModule convert_float_to_depth_frag;
99 vk::ShaderModule convert_abgr8_to_d24s8_frag; 113 vk::ShaderModule convert_abgr8_to_d24s8_frag;
@@ -106,6 +120,8 @@ private:
106 std::vector<vk::Pipeline> blit_color_pipelines; 120 std::vector<vk::Pipeline> blit_color_pipelines;
107 std::vector<BlitImagePipelineKey> blit_depth_stencil_keys; 121 std::vector<BlitImagePipelineKey> blit_depth_stencil_keys;
108 std::vector<vk::Pipeline> blit_depth_stencil_pipelines; 122 std::vector<vk::Pipeline> blit_depth_stencil_pipelines;
123 std::vector<BlitImagePipelineKey> clear_color_keys;
124 std::vector<vk::Pipeline> clear_color_pipelines;
109 vk::Pipeline convert_d32_to_r32_pipeline; 125 vk::Pipeline convert_d32_to_r32_pipeline;
110 vk::Pipeline convert_r32_to_d32_pipeline; 126 vk::Pipeline convert_r32_to_d32_pipeline;
111 vk::Pipeline convert_d16_to_r16_pipeline; 127 vk::Pipeline convert_d16_to_r16_pipeline;
diff --git a/src/video_core/renderer_vulkan/vk_fsr.cpp b/src/video_core/renderer_vulkan/vk_fsr.cpp
index 33daa8c1c..df972cd54 100644
--- a/src/video_core/renderer_vulkan/vk_fsr.cpp
+++ b/src/video_core/renderer_vulkan/vk_fsr.cpp
@@ -1,12 +1,11 @@
1// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project 1// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later 2// SPDX-License-Identifier: GPL-2.0-or-later
3 3
4#include <cmath>
5#include "common/bit_cast.h"
6#include "common/common_types.h" 4#include "common/common_types.h"
7#include "common/div_ceil.h" 5#include "common/div_ceil.h"
8#include "common/settings.h" 6#include "common/settings.h"
9 7
8#include "video_core/fsr.h"
10#include "video_core/host_shaders/vulkan_fidelityfx_fsr_easu_fp16_comp_spv.h" 9#include "video_core/host_shaders/vulkan_fidelityfx_fsr_easu_fp16_comp_spv.h"
11#include "video_core/host_shaders/vulkan_fidelityfx_fsr_easu_fp32_comp_spv.h" 10#include "video_core/host_shaders/vulkan_fidelityfx_fsr_easu_fp32_comp_spv.h"
12#include "video_core/host_shaders/vulkan_fidelityfx_fsr_rcas_fp16_comp_spv.h" 11#include "video_core/host_shaders/vulkan_fidelityfx_fsr_rcas_fp16_comp_spv.h"
@@ -17,146 +16,7 @@
17#include "video_core/vulkan_common/vulkan_device.h" 16#include "video_core/vulkan_common/vulkan_device.h"
18 17
19namespace Vulkan { 18namespace Vulkan {
20namespace { 19using namespace FSR;
21// Reimplementations of the constant generating functions in ffx_fsr1.h
22// GCC generated a lot of warnings when using the official header.
23u32 AU1_AH1_AF1(f32 f) {
24 static constexpr u32 base[512]{
25 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
26 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
27 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
28 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
29 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
30 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
31 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
32 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
33 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
34 0x0000, 0x0000, 0x0000, 0x0000, 0x0001, 0x0002, 0x0004, 0x0008, 0x0010, 0x0020, 0x0040,
35 0x0080, 0x0100, 0x0200, 0x0400, 0x0800, 0x0c00, 0x1000, 0x1400, 0x1800, 0x1c00, 0x2000,
36 0x2400, 0x2800, 0x2c00, 0x3000, 0x3400, 0x3800, 0x3c00, 0x4000, 0x4400, 0x4800, 0x4c00,
37 0x5000, 0x5400, 0x5800, 0x5c00, 0x6000, 0x6400, 0x6800, 0x6c00, 0x7000, 0x7400, 0x7800,
38 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff,
39 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff,
40 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff,
41 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff,
42 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff,
43 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff,
44 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff,
45 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff,
46 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff,
47 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff,
48 0x7bff, 0x7bff, 0x7bff, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000,
49 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000,
50 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000,
51 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000,
52 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000,
53 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000,
54 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000,
55 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000,
56 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000,
57 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8001, 0x8002, 0x8004, 0x8008,
58 0x8010, 0x8020, 0x8040, 0x8080, 0x8100, 0x8200, 0x8400, 0x8800, 0x8c00, 0x9000, 0x9400,
59 0x9800, 0x9c00, 0xa000, 0xa400, 0xa800, 0xac00, 0xb000, 0xb400, 0xb800, 0xbc00, 0xc000,
60 0xc400, 0xc800, 0xcc00, 0xd000, 0xd400, 0xd800, 0xdc00, 0xe000, 0xe400, 0xe800, 0xec00,
61 0xf000, 0xf400, 0xf800, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff,
62 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff,
63 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff,
64 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff,
65 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff,
66 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff,
67 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff,
68 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff,
69 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff,
70 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff,
71 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff,
72 };
73 static constexpr s8 shift[512]{
74 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
75 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
76 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
77 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
78 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
79 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
80 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x17, 0x16,
81 0x15, 0x14, 0x13, 0x12, 0x11, 0x10, 0x0f, 0x0e, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d,
82 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d,
83 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
84 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
85 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
86 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
87 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
88 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
89 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
90 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
91 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
92 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
93 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
94 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
95 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
96 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
97 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x17,
98 0x16, 0x15, 0x14, 0x13, 0x12, 0x11, 0x10, 0x0f, 0x0e, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d,
99 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d,
100 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
101 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
102 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
103 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
104 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
105 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
106 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
107 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
108 0x18, 0x18,
109 };
110 const u32 u = Common::BitCast<u32>(f);
111 const u32 i = u >> 23;
112 return base[i] + ((u & 0x7fffff) >> shift[i]);
113}
114
115u32 AU1_AH2_AF2(f32 a[2]) {
116 return AU1_AH1_AF1(a[0]) + (AU1_AH1_AF1(a[1]) << 16);
117}
118
119void FsrEasuCon(u32 con0[4], u32 con1[4], u32 con2[4], u32 con3[4], f32 inputViewportInPixelsX,
120 f32 inputViewportInPixelsY, f32 inputSizeInPixelsX, f32 inputSizeInPixelsY,
121 f32 outputSizeInPixelsX, f32 outputSizeInPixelsY) {
122 con0[0] = Common::BitCast<u32>(inputViewportInPixelsX / outputSizeInPixelsX);
123 con0[1] = Common::BitCast<u32>(inputViewportInPixelsY / outputSizeInPixelsY);
124 con0[2] = Common::BitCast<u32>(0.5f * inputViewportInPixelsX / outputSizeInPixelsX - 0.5f);
125 con0[3] = Common::BitCast<u32>(0.5f * inputViewportInPixelsY / outputSizeInPixelsY - 0.5f);
126 con1[0] = Common::BitCast<u32>(1.0f / inputSizeInPixelsX);
127 con1[1] = Common::BitCast<u32>(1.0f / inputSizeInPixelsY);
128 con1[2] = Common::BitCast<u32>(1.0f / inputSizeInPixelsX);
129 con1[3] = Common::BitCast<u32>(-1.0f / inputSizeInPixelsY);
130 con2[0] = Common::BitCast<u32>(-1.0f / inputSizeInPixelsX);
131 con2[1] = Common::BitCast<u32>(2.0f / inputSizeInPixelsY);
132 con2[2] = Common::BitCast<u32>(1.0f / inputSizeInPixelsX);
133 con2[3] = Common::BitCast<u32>(2.0f / inputSizeInPixelsY);
134 con3[0] = Common::BitCast<u32>(0.0f / inputSizeInPixelsX);
135 con3[1] = Common::BitCast<u32>(4.0f / inputSizeInPixelsY);
136 con3[2] = con3[3] = 0;
137}
138
139void FsrEasuConOffset(u32 con0[4], u32 con1[4], u32 con2[4], u32 con3[4],
140 f32 inputViewportInPixelsX, f32 inputViewportInPixelsY,
141 f32 inputSizeInPixelsX, f32 inputSizeInPixelsY, f32 outputSizeInPixelsX,
142 f32 outputSizeInPixelsY, f32 inputOffsetInPixelsX, f32 inputOffsetInPixelsY) {
143 FsrEasuCon(con0, con1, con2, con3, inputViewportInPixelsX, inputViewportInPixelsY,
144 inputSizeInPixelsX, inputSizeInPixelsY, outputSizeInPixelsX, outputSizeInPixelsY);
145 con0[2] = Common::BitCast<u32>(0.5f * inputViewportInPixelsX / outputSizeInPixelsX - 0.5f +
146 inputOffsetInPixelsX);
147 con0[3] = Common::BitCast<u32>(0.5f * inputViewportInPixelsY / outputSizeInPixelsY - 0.5f +
148 inputOffsetInPixelsY);
149}
150
151void FsrRcasCon(u32* con, f32 sharpness) {
152 sharpness = std::exp2f(-sharpness);
153 f32 hSharp[2]{sharpness, sharpness};
154 con[0] = Common::BitCast<u32>(sharpness);
155 con[1] = AU1_AH2_AF2(hSharp);
156 con[2] = 0;
157 con[3] = 0;
158}
159} // Anonymous namespace
160 20
161FSR::FSR(const Device& device_, MemoryAllocator& memory_allocator_, size_t image_count_, 21FSR::FSR(const Device& device_, MemoryAllocator& memory_allocator_, size_t image_count_,
162 VkExtent2D output_size_) 22 VkExtent2D output_size_)
diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.cpp b/src/video_core/renderer_vulkan/vk_rasterizer.cpp
index b75b8eec6..719edbcfb 100644
--- a/src/video_core/renderer_vulkan/vk_rasterizer.cpp
+++ b/src/video_core/renderer_vulkan/vk_rasterizer.cpp
@@ -266,6 +266,35 @@ void RasterizerVulkan::DrawIndirect() {
266 buffer_cache.SetDrawIndirect(nullptr); 266 buffer_cache.SetDrawIndirect(nullptr);
267} 267}
268 268
269void RasterizerVulkan::DrawTexture() {
270 MICROPROFILE_SCOPE(Vulkan_Drawing);
271
272 SCOPE_EXIT({ gpu.TickWork(); });
273 FlushWork();
274
275 query_cache.UpdateCounters();
276
277 texture_cache.SynchronizeGraphicsDescriptors();
278 texture_cache.UpdateRenderTargets(false);
279
280 UpdateDynamicStates();
281
282 const auto& draw_texture_state = maxwell3d->draw_manager->GetDrawTextureState();
283 const auto& sampler = texture_cache.GetGraphicsSampler(draw_texture_state.src_sampler);
284 const auto& texture = texture_cache.GetImageView(draw_texture_state.src_texture);
285 Region2D dst_region = {Offset2D{.x = static_cast<s32>(draw_texture_state.dst_x0),
286 .y = static_cast<s32>(draw_texture_state.dst_y0)},
287 Offset2D{.x = static_cast<s32>(draw_texture_state.dst_x1),
288 .y = static_cast<s32>(draw_texture_state.dst_y1)}};
289 Region2D src_region = {Offset2D{.x = static_cast<s32>(draw_texture_state.src_x0),
290 .y = static_cast<s32>(draw_texture_state.src_y0)},
291 Offset2D{.x = static_cast<s32>(draw_texture_state.src_x1),
292 .y = static_cast<s32>(draw_texture_state.src_y1)}};
293 blit_image.BlitColor(texture_cache.GetFramebuffer(), texture.RenderTarget(),
294 texture.ImageHandle(), sampler->Handle(), dst_region, src_region,
295 texture.size);
296}
297
269void RasterizerVulkan::Clear(u32 layer_count) { 298void RasterizerVulkan::Clear(u32 layer_count) {
270 MICROPROFILE_SCOPE(Vulkan_Clearing); 299 MICROPROFILE_SCOPE(Vulkan_Clearing);
271 300
@@ -365,7 +394,15 @@ void RasterizerVulkan::Clear(u32 layer_count) {
365 cmdbuf.ClearAttachments(attachment, clear_rect); 394 cmdbuf.ClearAttachments(attachment, clear_rect);
366 }); 395 });
367 } else { 396 } else {
368 UNIMPLEMENTED_MSG("Unimplemented Clear only the specified channel"); 397 u8 color_mask = static_cast<u8>(regs.clear_surface.R | regs.clear_surface.G << 1 |
398 regs.clear_surface.B << 2 | regs.clear_surface.A << 3);
399 Region2D dst_region = {
400 Offset2D{.x = clear_rect.rect.offset.x, .y = clear_rect.rect.offset.y},
401 Offset2D{.x = clear_rect.rect.offset.x +
402 static_cast<s32>(clear_rect.rect.extent.width),
403 .y = clear_rect.rect.offset.y +
404 static_cast<s32>(clear_rect.rect.extent.height)}};
405 blit_image.ClearColor(framebuffer, color_mask, regs.clear_color, dst_region);
369 } 406 }
370 } 407 }
371 408
diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.h b/src/video_core/renderer_vulkan/vk_rasterizer.h
index 472cc64d9..a0508b57c 100644
--- a/src/video_core/renderer_vulkan/vk_rasterizer.h
+++ b/src/video_core/renderer_vulkan/vk_rasterizer.h
@@ -66,6 +66,7 @@ public:
66 66
67 void Draw(bool is_indexed, u32 instance_count) override; 67 void Draw(bool is_indexed, u32 instance_count) override;
68 void DrawIndirect() override; 68 void DrawIndirect() override;
69 void DrawTexture() override;
69 void Clear(u32 layer_count) override; 70 void Clear(u32 layer_count) override;
70 void DispatchCompute() override; 71 void DispatchCompute() override;
71 void ResetCounter(VideoCore::QueryType type) override; 72 void ResetCounter(VideoCore::QueryType type) override;
diff --git a/src/video_core/texture_cache/descriptor_table.h b/src/video_core/texture_cache/descriptor_table.h
index ee4240288..1bad83fb4 100644
--- a/src/video_core/texture_cache/descriptor_table.h
+++ b/src/video_core/texture_cache/descriptor_table.h
@@ -19,9 +19,7 @@ public:
19 explicit DescriptorTable(Tegra::MemoryManager& gpu_memory_) : gpu_memory{gpu_memory_} {} 19 explicit DescriptorTable(Tegra::MemoryManager& gpu_memory_) : gpu_memory{gpu_memory_} {}
20 20
21 [[nodiscard]] bool Synchronize(GPUVAddr gpu_addr, u32 limit) { 21 [[nodiscard]] bool Synchronize(GPUVAddr gpu_addr, u32 limit) {
22 [[likely]] if (current_gpu_addr == gpu_addr && current_limit == limit) { 22 [[likely]] if (current_gpu_addr == gpu_addr && current_limit == limit) { return false; }
23 return false;
24 }
25 Refresh(gpu_addr, limit); 23 Refresh(gpu_addr, limit);
26 return true; 24 return true;
27 } 25 }
diff --git a/src/video_core/texture_cache/image_info.cpp b/src/video_core/texture_cache/image_info.cpp
index 852ec2519..e9100091e 100644
--- a/src/video_core/texture_cache/image_info.cpp
+++ b/src/video_core/texture_cache/image_info.cpp
@@ -100,6 +100,10 @@ ImageInfo::ImageInfo(const TICEntry& config) noexcept {
100 ASSERT_MSG(false, "Invalid texture_type={}", static_cast<int>(config.texture_type.Value())); 100 ASSERT_MSG(false, "Invalid texture_type={}", static_cast<int>(config.texture_type.Value()));
101 break; 101 break;
102 } 102 }
103 if (num_samples > 1) {
104 size.width *= NumSamplesX(config.msaa_mode);
105 size.height *= NumSamplesY(config.msaa_mode);
106 }
103 if (type != ImageType::Linear) { 107 if (type != ImageType::Linear) {
104 // FIXME: Call this without passing *this 108 // FIXME: Call this without passing *this
105 layer_stride = CalculateLayerStride(*this); 109 layer_stride = CalculateLayerStride(*this);
diff --git a/src/video_core/texture_cache/samples_helper.h b/src/video_core/texture_cache/samples_helper.h
index d552bccf0..203ac1b11 100644
--- a/src/video_core/texture_cache/samples_helper.h
+++ b/src/video_core/texture_cache/samples_helper.h
@@ -51,4 +51,48 @@ namespace VideoCommon {
51 return 1; 51 return 1;
52} 52}
53 53
54[[nodiscard]] inline int NumSamplesX(Tegra::Texture::MsaaMode msaa_mode) {
55 using Tegra::Texture::MsaaMode;
56 switch (msaa_mode) {
57 case MsaaMode::Msaa1x1:
58 return 1;
59 case MsaaMode::Msaa2x1:
60 case MsaaMode::Msaa2x1_D3D:
61 case MsaaMode::Msaa2x2:
62 case MsaaMode::Msaa2x2_VC4:
63 case MsaaMode::Msaa2x2_VC12:
64 return 2;
65 case MsaaMode::Msaa4x2:
66 case MsaaMode::Msaa4x2_D3D:
67 case MsaaMode::Msaa4x2_VC8:
68 case MsaaMode::Msaa4x2_VC24:
69 case MsaaMode::Msaa4x4:
70 return 4;
71 }
72 ASSERT_MSG(false, "Invalid MSAA mode={}", static_cast<int>(msaa_mode));
73 return 1;
74}
75
76[[nodiscard]] inline int NumSamplesY(Tegra::Texture::MsaaMode msaa_mode) {
77 using Tegra::Texture::MsaaMode;
78 switch (msaa_mode) {
79 case MsaaMode::Msaa1x1:
80 case MsaaMode::Msaa2x1:
81 case MsaaMode::Msaa2x1_D3D:
82 return 1;
83 case MsaaMode::Msaa2x2:
84 case MsaaMode::Msaa2x2_VC4:
85 case MsaaMode::Msaa2x2_VC12:
86 case MsaaMode::Msaa4x2:
87 case MsaaMode::Msaa4x2_D3D:
88 case MsaaMode::Msaa4x2_VC8:
89 case MsaaMode::Msaa4x2_VC24:
90 return 2;
91 case MsaaMode::Msaa4x4:
92 return 4;
93 }
94 ASSERT_MSG(false, "Invalid MSAA mode={}", static_cast<int>(msaa_mode));
95 return 1;
96}
97
54} // namespace VideoCommon 98} // namespace VideoCommon
diff --git a/src/video_core/texture_cache/slot_vector.h b/src/video_core/texture_cache/slot_vector.h
index 1e2aad76a..9df6a2903 100644
--- a/src/video_core/texture_cache/slot_vector.h
+++ b/src/video_core/texture_cache/slot_vector.h
@@ -29,7 +29,7 @@ struct SlotId {
29}; 29};
30 30
31template <class T> 31template <class T>
32requires std::is_nothrow_move_assignable_v<T> && std::is_nothrow_move_constructible_v<T> 32 requires std::is_nothrow_move_assignable_v<T> && std::is_nothrow_move_constructible_v<T>
33class SlotVector { 33class SlotVector {
34public: 34public:
35 class Iterator { 35 class Iterator {
diff --git a/src/video_core/texture_cache/texture_cache.h b/src/video_core/texture_cache/texture_cache.h
index 87152c8e9..1b01990a4 100644
--- a/src/video_core/texture_cache/texture_cache.h
+++ b/src/video_core/texture_cache/texture_cache.h
@@ -149,6 +149,13 @@ typename P::ImageView& TextureCache<P>::GetImageView(ImageViewId id) noexcept {
149} 149}
150 150
151template <class P> 151template <class P>
152typename P::ImageView& TextureCache<P>::GetImageView(u32 index) noexcept {
153 const auto image_view_id = VisitImageView(channel_state->graphics_image_table,
154 channel_state->graphics_image_view_ids, index);
155 return slot_image_views[image_view_id];
156}
157
158template <class P>
152void TextureCache<P>::MarkModification(ImageId id) noexcept { 159void TextureCache<P>::MarkModification(ImageId id) noexcept {
153 MarkModification(slot_images[id]); 160 MarkModification(slot_images[id]);
154} 161}
diff --git a/src/video_core/texture_cache/texture_cache_base.h b/src/video_core/texture_cache/texture_cache_base.h
index 4eea1f609..485eaabaa 100644
--- a/src/video_core/texture_cache/texture_cache_base.h
+++ b/src/video_core/texture_cache/texture_cache_base.h
@@ -129,6 +129,9 @@ public:
129 /// Return a reference to the given image view id 129 /// Return a reference to the given image view id
130 [[nodiscard]] ImageView& GetImageView(ImageViewId id) noexcept; 130 [[nodiscard]] ImageView& GetImageView(ImageViewId id) noexcept;
131 131
132 /// Get the imageview from the graphics descriptor table in the specified index
133 [[nodiscard]] ImageView& GetImageView(u32 index) noexcept;
134
132 /// Mark an image as modified from the GPU 135 /// Mark an image as modified from the GPU
133 void MarkModification(ImageId id) noexcept; 136 void MarkModification(ImageId id) noexcept;
134 137
diff --git a/src/video_core/vulkan_common/nsight_aftermath_tracker.cpp b/src/video_core/vulkan_common/nsight_aftermath_tracker.cpp
index 85f1d13e0..5fa0d9620 100644
--- a/src/video_core/vulkan_common/nsight_aftermath_tracker.cpp
+++ b/src/video_core/vulkan_common/nsight_aftermath_tracker.cpp
@@ -57,7 +57,7 @@ NsightAftermathTracker::NsightAftermathTracker() {
57 if (!GFSDK_Aftermath_SUCCEED(GFSDK_Aftermath_EnableGpuCrashDumps( 57 if (!GFSDK_Aftermath_SUCCEED(GFSDK_Aftermath_EnableGpuCrashDumps(
58 GFSDK_Aftermath_Version_API, GFSDK_Aftermath_GpuCrashDumpWatchedApiFlags_Vulkan, 58 GFSDK_Aftermath_Version_API, GFSDK_Aftermath_GpuCrashDumpWatchedApiFlags_Vulkan,
59 GFSDK_Aftermath_GpuCrashDumpFeatureFlags_Default, GpuCrashDumpCallback, 59 GFSDK_Aftermath_GpuCrashDumpFeatureFlags_Default, GpuCrashDumpCallback,
60 ShaderDebugInfoCallback, CrashDumpDescriptionCallback, this))) { 60 ShaderDebugInfoCallback, CrashDumpDescriptionCallback, nullptr, this))) {
61 LOG_ERROR(Render_Vulkan, "GFSDK_Aftermath_EnableGpuCrashDumps failed"); 61 LOG_ERROR(Render_Vulkan, "GFSDK_Aftermath_EnableGpuCrashDumps failed");
62 return; 62 return;
63 } 63 }
@@ -83,7 +83,7 @@ void NsightAftermathTracker::SaveShader(std::span<const u32> spirv) const {
83 83
84 std::scoped_lock lock{mutex}; 84 std::scoped_lock lock{mutex};
85 85
86 GFSDK_Aftermath_ShaderHash hash; 86 GFSDK_Aftermath_ShaderBinaryHash hash;
87 if (!GFSDK_Aftermath_SUCCEED( 87 if (!GFSDK_Aftermath_SUCCEED(
88 GFSDK_Aftermath_GetShaderHashSpirv(GFSDK_Aftermath_Version_API, &shader, &hash))) { 88 GFSDK_Aftermath_GetShaderHashSpirv(GFSDK_Aftermath_Version_API, &shader, &hash))) {
89 LOG_ERROR(Render_Vulkan, "Failed to hash SPIR-V module"); 89 LOG_ERROR(Render_Vulkan, "Failed to hash SPIR-V module");
@@ -121,8 +121,8 @@ void NsightAftermathTracker::OnGpuCrashDumpCallback(const void* gpu_crash_dump,
121 u32 json_size = 0; 121 u32 json_size = 0;
122 if (!GFSDK_Aftermath_SUCCEED(GFSDK_Aftermath_GpuCrashDump_GenerateJSON( 122 if (!GFSDK_Aftermath_SUCCEED(GFSDK_Aftermath_GpuCrashDump_GenerateJSON(
123 decoder, GFSDK_Aftermath_GpuCrashDumpDecoderFlags_ALL_INFO, 123 decoder, GFSDK_Aftermath_GpuCrashDumpDecoderFlags_ALL_INFO,
124 GFSDK_Aftermath_GpuCrashDumpFormatterFlags_NONE, nullptr, nullptr, nullptr, nullptr, 124 GFSDK_Aftermath_GpuCrashDumpFormatterFlags_NONE, nullptr, nullptr, nullptr, this,
125 this, &json_size))) { 125 &json_size))) {
126 LOG_ERROR(Render_Vulkan, "Failed to generate JSON"); 126 LOG_ERROR(Render_Vulkan, "Failed to generate JSON");
127 return; 127 return;
128 } 128 }
diff --git a/src/yuzu/CMakeLists.txt b/src/yuzu/CMakeLists.txt
index dfc675cc8..06d982d9b 100644
--- a/src/yuzu/CMakeLists.txt
+++ b/src/yuzu/CMakeLists.txt
@@ -353,7 +353,7 @@ if (USE_DISCORD_PRESENCE)
353 discord_impl.cpp 353 discord_impl.cpp
354 discord_impl.h 354 discord_impl.h
355 ) 355 )
356 target_link_libraries(yuzu PRIVATE DiscordRPC::discord-rpc) 356 target_link_libraries(yuzu PRIVATE DiscordRPC::discord-rpc httplib::httplib)
357 target_compile_definitions(yuzu PRIVATE -DUSE_DISCORD_PRESENCE) 357 target_compile_definitions(yuzu PRIVATE -DUSE_DISCORD_PRESENCE)
358endif() 358endif()
359 359
diff --git a/src/yuzu/configuration/config.cpp b/src/yuzu/configuration/config.cpp
index 0db62baa3..35fef506a 100644
--- a/src/yuzu/configuration/config.cpp
+++ b/src/yuzu/configuration/config.cpp
@@ -70,28 +70,28 @@ const std::array<int, 2> Config::default_ringcon_analogs{{
70// UISetting::values.shortcuts, which is alphabetically ordered. 70// UISetting::values.shortcuts, which is alphabetically ordered.
71// clang-format off 71// clang-format off
72const std::array<UISettings::Shortcut, 22> Config::default_hotkeys{{ 72const std::array<UISettings::Shortcut, 22> Config::default_hotkeys{{
73 {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Audio Mute/Unmute")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("Ctrl+M"), QStringLiteral("Home+Dpad_Right"), Qt::WindowShortcut}}, 73 {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Audio Mute/Unmute")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("Ctrl+M"), QStringLiteral("Home+Dpad_Right"), Qt::WindowShortcut, false}},
74 {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Audio Volume Down")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("-"), QStringLiteral("Home+Dpad_Down"), Qt::ApplicationShortcut}}, 74 {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Audio Volume Down")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("-"), QStringLiteral("Home+Dpad_Down"), Qt::ApplicationShortcut, true}},
75 {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Audio Volume Up")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("="), QStringLiteral("Home+Dpad_Up"), Qt::ApplicationShortcut}}, 75 {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Audio Volume Up")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("="), QStringLiteral("Home+Dpad_Up"), Qt::ApplicationShortcut, true}},
76 {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Capture Screenshot")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("Ctrl+P"), QStringLiteral("Screenshot"), Qt::WidgetWithChildrenShortcut}}, 76 {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Capture Screenshot")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("Ctrl+P"), QStringLiteral("Screenshot"), Qt::WidgetWithChildrenShortcut, false}},
77 {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Change Adapting Filter")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("F8"), QStringLiteral("Home+L"), Qt::ApplicationShortcut}}, 77 {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Change Adapting Filter")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("F8"), QStringLiteral("Home+L"), Qt::ApplicationShortcut, false}},
78 {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Change Docked Mode")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("F10"), QStringLiteral("Home+X"), Qt::ApplicationShortcut}}, 78 {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Change Docked Mode")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("F10"), QStringLiteral("Home+X"), Qt::ApplicationShortcut, false}},
79 {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Change GPU Accuracy")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("F9"), QStringLiteral("Home+R"), Qt::ApplicationShortcut}}, 79 {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Change GPU Accuracy")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("F9"), QStringLiteral("Home+R"), Qt::ApplicationShortcut, false}},
80 {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Continue/Pause Emulation")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("F4"), QStringLiteral("Home+Plus"), Qt::WindowShortcut}}, 80 {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Continue/Pause Emulation")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("F4"), QStringLiteral("Home+Plus"), Qt::WindowShortcut, false}},
81 {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Exit Fullscreen")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("Esc"), QStringLiteral(""), Qt::WindowShortcut}}, 81 {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Exit Fullscreen")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("Esc"), QStringLiteral(""), Qt::WindowShortcut, false}},
82 {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Exit yuzu")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("Ctrl+Q"), QStringLiteral("Home+Minus"), Qt::WindowShortcut}}, 82 {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Exit yuzu")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("Ctrl+Q"), QStringLiteral("Home+Minus"), Qt::WindowShortcut, false}},
83 {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Fullscreen")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("F11"), QStringLiteral("Home+B"), Qt::WindowShortcut}}, 83 {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Fullscreen")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("F11"), QStringLiteral("Home+B"), Qt::WindowShortcut, false}},
84 {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Load File")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("Ctrl+O"), QStringLiteral(""), Qt::WidgetWithChildrenShortcut}}, 84 {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Load File")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("Ctrl+O"), QStringLiteral(""), Qt::WidgetWithChildrenShortcut, false}},
85 {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Load/Remove Amiibo")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("F2"), QStringLiteral("Home+A"), Qt::WidgetWithChildrenShortcut}}, 85 {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Load/Remove Amiibo")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("F2"), QStringLiteral("Home+A"), Qt::WidgetWithChildrenShortcut, false}},
86 {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Restart Emulation")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("F6"), QStringLiteral(""), Qt::WindowShortcut}}, 86 {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Restart Emulation")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("F6"), QStringLiteral(""), Qt::WindowShortcut, false}},
87 {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Stop Emulation")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("F5"), QStringLiteral(""), Qt::WindowShortcut}}, 87 {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Stop Emulation")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("F5"), QStringLiteral(""), Qt::WindowShortcut, false}},
88 {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "TAS Record")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("Ctrl+F7"), QStringLiteral(""), Qt::ApplicationShortcut}}, 88 {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "TAS Record")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("Ctrl+F7"), QStringLiteral(""), Qt::ApplicationShortcut, false}},
89 {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "TAS Reset")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("Ctrl+F6"), QStringLiteral(""), Qt::ApplicationShortcut}}, 89 {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "TAS Reset")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("Ctrl+F6"), QStringLiteral(""), Qt::ApplicationShortcut, false}},
90 {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "TAS Start/Stop")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("Ctrl+F5"), QStringLiteral(""), Qt::ApplicationShortcut}}, 90 {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "TAS Start/Stop")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("Ctrl+F5"), QStringLiteral(""), Qt::ApplicationShortcut, false}},
91 {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Toggle Filter Bar")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("Ctrl+F"), QStringLiteral(""), Qt::WindowShortcut}}, 91 {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Toggle Filter Bar")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("Ctrl+F"), QStringLiteral(""), Qt::WindowShortcut, false}},
92 {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Toggle Framerate Limit")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("Ctrl+U"), QStringLiteral("Home+Y"), Qt::ApplicationShortcut}}, 92 {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Toggle Framerate Limit")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("Ctrl+U"), QStringLiteral("Home+Y"), Qt::ApplicationShortcut, false}},
93 {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Toggle Mouse Panning")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("Ctrl+F9"), QStringLiteral(""), Qt::ApplicationShortcut}}, 93 {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Toggle Mouse Panning")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("Ctrl+F9"), QStringLiteral(""), Qt::ApplicationShortcut, false}},
94 {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Toggle Status Bar")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("Ctrl+S"), QStringLiteral(""), Qt::WindowShortcut}}, 94 {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Toggle Status Bar")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("Ctrl+S"), QStringLiteral(""), Qt::WindowShortcut, false}},
95}}; 95}};
96// clang-format on 96// clang-format on
97 97
@@ -440,6 +440,7 @@ void Config::ReadControlValues() {
440 ReadBasicSetting(Settings::values.emulate_analog_keyboard); 440 ReadBasicSetting(Settings::values.emulate_analog_keyboard);
441 Settings::values.mouse_panning = false; 441 Settings::values.mouse_panning = false;
442 ReadBasicSetting(Settings::values.mouse_panning_sensitivity); 442 ReadBasicSetting(Settings::values.mouse_panning_sensitivity);
443 ReadBasicSetting(Settings::values.enable_joycon_driver);
443 444
444 ReadBasicSetting(Settings::values.tas_enable); 445 ReadBasicSetting(Settings::values.tas_enable);
445 ReadBasicSetting(Settings::values.tas_loop); 446 ReadBasicSetting(Settings::values.tas_loop);
@@ -747,7 +748,7 @@ void Config::ReadShortcutValues() {
747 for (const auto& [name, group, shortcut] : default_hotkeys) { 748 for (const auto& [name, group, shortcut] : default_hotkeys) {
748 qt_config->beginGroup(group); 749 qt_config->beginGroup(group);
749 qt_config->beginGroup(name); 750 qt_config->beginGroup(name);
750 // No longer using ReadSetting for shortcut.second as it innacurately returns a value of 1 751 // No longer using ReadSetting for shortcut.second as it inaccurately returns a value of 1
751 // for WidgetWithChildrenShortcut which is a value of 3. Needed to fix shortcuts the open 752 // for WidgetWithChildrenShortcut which is a value of 3. Needed to fix shortcuts the open
752 // a file dialog in windowed mode 753 // a file dialog in windowed mode
753 UISettings::values.shortcuts.push_back( 754 UISettings::values.shortcuts.push_back(
@@ -756,7 +757,7 @@ void Config::ReadShortcutValues() {
756 {ReadSetting(QStringLiteral("KeySeq"), shortcut.keyseq).toString(), 757 {ReadSetting(QStringLiteral("KeySeq"), shortcut.keyseq).toString(),
757 ReadSetting(QStringLiteral("Controller_KeySeq"), shortcut.controller_keyseq) 758 ReadSetting(QStringLiteral("Controller_KeySeq"), shortcut.controller_keyseq)
758 .toString(), 759 .toString(),
759 shortcut.context}}); 760 shortcut.context, ReadSetting(QStringLiteral("Repeat"), shortcut.repeat).toBool()}});
760 qt_config->endGroup(); 761 qt_config->endGroup();
761 qt_config->endGroup(); 762 qt_config->endGroup();
762 } 763 }
@@ -1139,6 +1140,7 @@ void Config::SaveControlValues() {
1139 WriteGlobalSetting(Settings::values.enable_accurate_vibrations); 1140 WriteGlobalSetting(Settings::values.enable_accurate_vibrations);
1140 WriteGlobalSetting(Settings::values.motion_enabled); 1141 WriteGlobalSetting(Settings::values.motion_enabled);
1141 WriteBasicSetting(Settings::values.enable_raw_input); 1142 WriteBasicSetting(Settings::values.enable_raw_input);
1143 WriteBasicSetting(Settings::values.enable_joycon_driver);
1142 WriteBasicSetting(Settings::values.keyboard_enabled); 1144 WriteBasicSetting(Settings::values.keyboard_enabled);
1143 WriteBasicSetting(Settings::values.emulate_analog_keyboard); 1145 WriteBasicSetting(Settings::values.emulate_analog_keyboard);
1144 WriteBasicSetting(Settings::values.mouse_panning_sensitivity); 1146 WriteBasicSetting(Settings::values.mouse_panning_sensitivity);
@@ -1393,6 +1395,7 @@ void Config::SaveShortcutValues() {
1393 WriteSetting(QStringLiteral("Controller_KeySeq"), shortcut.controller_keyseq, 1395 WriteSetting(QStringLiteral("Controller_KeySeq"), shortcut.controller_keyseq,
1394 default_hotkey.controller_keyseq); 1396 default_hotkey.controller_keyseq);
1395 WriteSetting(QStringLiteral("Context"), shortcut.context, default_hotkey.context); 1397 WriteSetting(QStringLiteral("Context"), shortcut.context, default_hotkey.context);
1398 WriteSetting(QStringLiteral("Repeat"), shortcut.repeat, default_hotkey.repeat);
1396 qt_config->endGroup(); 1399 qt_config->endGroup();
1397 qt_config->endGroup(); 1400 qt_config->endGroup();
1398 } 1401 }
diff --git a/src/yuzu/configuration/configuration_shared.cpp b/src/yuzu/configuration/configuration_shared.cpp
index 97fb664bf..ac42cc7fc 100644
--- a/src/yuzu/configuration/configuration_shared.cpp
+++ b/src/yuzu/configuration/configuration_shared.cpp
@@ -92,3 +92,13 @@ void ConfigurationShared::InsertGlobalItem(QComboBox* combobox, int global_index
92 combobox->insertItem(ConfigurationShared::USE_GLOBAL_INDEX, use_global_text); 92 combobox->insertItem(ConfigurationShared::USE_GLOBAL_INDEX, use_global_text);
93 combobox->insertSeparator(ConfigurationShared::USE_GLOBAL_SEPARATOR_INDEX); 93 combobox->insertSeparator(ConfigurationShared::USE_GLOBAL_SEPARATOR_INDEX);
94} 94}
95
96int ConfigurationShared::GetComboboxIndex(int global_setting_index, const QComboBox* combobox) {
97 if (Settings::IsConfiguringGlobal()) {
98 return combobox->currentIndex();
99 }
100 if (combobox->currentIndex() == ConfigurationShared::USE_GLOBAL_INDEX) {
101 return global_setting_index;
102 }
103 return combobox->currentIndex() - ConfigurationShared::USE_GLOBAL_OFFSET;
104}
diff --git a/src/yuzu/configuration/configuration_shared.h b/src/yuzu/configuration/configuration_shared.h
index e597dcdb5..04c88758c 100644
--- a/src/yuzu/configuration/configuration_shared.h
+++ b/src/yuzu/configuration/configuration_shared.h
@@ -69,4 +69,7 @@ void SetColoredComboBox(QComboBox* combobox, QWidget* target, int global);
69// Adds the "Use Global Configuration" selection and separator to the beginning of a QComboBox 69// Adds the "Use Global Configuration" selection and separator to the beginning of a QComboBox
70void InsertGlobalItem(QComboBox* combobox, int global_index); 70void InsertGlobalItem(QComboBox* combobox, int global_index);
71 71
72// Returns the correct index of a QComboBox taking into account global configuration
73int GetComboboxIndex(int global_setting_index, const QComboBox* combobox);
74
72} // namespace ConfigurationShared 75} // namespace ConfigurationShared
diff --git a/src/yuzu/configuration/configure_dialog.cpp b/src/yuzu/configuration/configure_dialog.cpp
index 4301313cf..2aaefcc05 100644
--- a/src/yuzu/configuration/configure_dialog.cpp
+++ b/src/yuzu/configuration/configure_dialog.cpp
@@ -66,7 +66,6 @@ ConfigureDialog::ConfigureDialog(QWidget* parent, HotkeyRegistry& registry_,
66 66
67 web_tab->SetWebServiceConfigEnabled(enable_web_config); 67 web_tab->SetWebServiceConfigEnabled(enable_web_config);
68 hotkeys_tab->Populate(registry); 68 hotkeys_tab->Populate(registry);
69 setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);
70 69
71 input_tab->Initialize(input_subsystem); 70 input_tab->Initialize(input_subsystem);
72 71
diff --git a/src/yuzu/configuration/configure_graphics.ui b/src/yuzu/configuration/configure_graphics.ui
index bb9910a53..a45ec69ec 100644
--- a/src/yuzu/configuration/configure_graphics.ui
+++ b/src/yuzu/configuration/configure_graphics.ui
@@ -460,7 +460,7 @@
460 </item> 460 </item>
461 <item> 461 <item>
462 <property name="text"> 462 <property name="text">
463 <string>AMD FidelityFX™️ Super Resolution (Vulkan Only)</string> 463 <string>AMD FidelityFX™️ Super Resolution</string>
464 </property> 464 </property>
465 </item> 465 </item>
466 </widget> 466 </widget>
diff --git a/src/yuzu/configuration/configure_input_advanced.cpp b/src/yuzu/configuration/configure_input_advanced.cpp
index 235b813d9..77b976e74 100644
--- a/src/yuzu/configuration/configure_input_advanced.cpp
+++ b/src/yuzu/configuration/configure_input_advanced.cpp
@@ -138,6 +138,7 @@ void ConfigureInputAdvanced::ApplyConfiguration() {
138 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(); 139 Settings::values.enable_ring_controller = ui->enable_ring_controller->isChecked();
140 Settings::values.enable_ir_sensor = ui->enable_ir_sensor->isChecked(); 140 Settings::values.enable_ir_sensor = ui->enable_ir_sensor->isChecked();
141 Settings::values.enable_joycon_driver = ui->enable_joycon_driver->isChecked();
141} 142}
142 143
143void ConfigureInputAdvanced::LoadConfiguration() { 144void ConfigureInputAdvanced::LoadConfiguration() {
@@ -172,6 +173,7 @@ void ConfigureInputAdvanced::LoadConfiguration() {
172 ui->controller_navigation->setChecked(Settings::values.controller_navigation.GetValue()); 173 ui->controller_navigation->setChecked(Settings::values.controller_navigation.GetValue());
173 ui->enable_ring_controller->setChecked(Settings::values.enable_ring_controller.GetValue()); 174 ui->enable_ring_controller->setChecked(Settings::values.enable_ring_controller.GetValue());
174 ui->enable_ir_sensor->setChecked(Settings::values.enable_ir_sensor.GetValue()); 175 ui->enable_ir_sensor->setChecked(Settings::values.enable_ir_sensor.GetValue());
176 ui->enable_joycon_driver->setChecked(Settings::values.enable_joycon_driver.GetValue());
175 177
176 UpdateUIEnabled(); 178 UpdateUIEnabled();
177} 179}
diff --git a/src/yuzu/configuration/configure_input_advanced.ui b/src/yuzu/configuration/configure_input_advanced.ui
index fac8cf827..75d96d3ab 100644
--- a/src/yuzu/configuration/configure_input_advanced.ui
+++ b/src/yuzu/configuration/configure_input_advanced.ui
@@ -2696,6 +2696,22 @@
2696 </widget> 2696 </widget>
2697 </item> 2697 </item>
2698 <item row="5" column="0"> 2698 <item row="5" column="0">
2699 <widget class="QCheckBox" name="enable_joycon_driver">
2700 <property name="toolTip">
2701 <string>Requires restarting yuzu</string>
2702 </property>
2703 <property name="minimumSize">
2704 <size>
2705 <width>0</width>
2706 <height>23</height>
2707 </size>
2708 </property>
2709 <property name="text">
2710 <string>Enable direct JoyCon driver</string>
2711 </property>
2712 </widget>
2713 </item>
2714 <item row="6" column="0">
2699 <widget class="QCheckBox" name="mouse_panning"> 2715 <widget class="QCheckBox" name="mouse_panning">
2700 <property name="minimumSize"> 2716 <property name="minimumSize">
2701 <size> 2717 <size>
@@ -2708,7 +2724,7 @@
2708 </property> 2724 </property>
2709 </widget> 2725 </widget>
2710 </item> 2726 </item>
2711 <item row="5" column="2"> 2727 <item row="6" column="2">
2712 <widget class="QSpinBox" name="mouse_panning_sensitivity"> 2728 <widget class="QSpinBox" name="mouse_panning_sensitivity">
2713 <property name="toolTip"> 2729 <property name="toolTip">
2714 <string>Mouse sensitivity</string> 2730 <string>Mouse sensitivity</string>
@@ -2730,14 +2746,14 @@
2730 </property> 2746 </property>
2731 </widget> 2747 </widget>
2732 </item> 2748 </item>
2733 <item row="6" column="0"> 2749 <item row="7" column="0">
2734 <widget class="QLabel" name="motion_touch"> 2750 <widget class="QLabel" name="motion_touch">
2735 <property name="text"> 2751 <property name="text">
2736 <string>Motion / Touch</string> 2752 <string>Motion / Touch</string>
2737 </property> 2753 </property>
2738 </widget> 2754 </widget>
2739 </item> 2755 </item>
2740 <item row="6" column="2"> 2756 <item row="7" column="2">
2741 <widget class="QPushButton" name="buttonMotionTouch"> 2757 <widget class="QPushButton" name="buttonMotionTouch">
2742 <property name="text"> 2758 <property name="text">
2743 <string>Configure</string> 2759 <string>Configure</string>
diff --git a/src/yuzu/configuration/configure_input_player.cpp b/src/yuzu/configuration/configure_input_player.cpp
index c40d980c9..723690e71 100644
--- a/src/yuzu/configuration/configure_input_player.cpp
+++ b/src/yuzu/configuration/configure_input_player.cpp
@@ -66,6 +66,18 @@ QString GetButtonName(Common::Input::ButtonNames button_name) {
66 return QObject::tr("R"); 66 return QObject::tr("R");
67 case Common::Input::ButtonNames::TriggerL: 67 case Common::Input::ButtonNames::TriggerL:
68 return QObject::tr("L"); 68 return QObject::tr("L");
69 case Common::Input::ButtonNames::TriggerZR:
70 return QObject::tr("ZR");
71 case Common::Input::ButtonNames::TriggerZL:
72 return QObject::tr("ZL");
73 case Common::Input::ButtonNames::TriggerSR:
74 return QObject::tr("SR");
75 case Common::Input::ButtonNames::TriggerSL:
76 return QObject::tr("SL");
77 case Common::Input::ButtonNames::ButtonStickL:
78 return QObject::tr("Stick L");
79 case Common::Input::ButtonNames::ButtonStickR:
80 return QObject::tr("Stick R");
69 case Common::Input::ButtonNames::ButtonA: 81 case Common::Input::ButtonNames::ButtonA:
70 return QObject::tr("A"); 82 return QObject::tr("A");
71 case Common::Input::ButtonNames::ButtonB: 83 case Common::Input::ButtonNames::ButtonB:
@@ -76,6 +88,14 @@ QString GetButtonName(Common::Input::ButtonNames button_name) {
76 return QObject::tr("Y"); 88 return QObject::tr("Y");
77 case Common::Input::ButtonNames::ButtonStart: 89 case Common::Input::ButtonNames::ButtonStart:
78 return QObject::tr("Start"); 90 return QObject::tr("Start");
91 case Common::Input::ButtonNames::ButtonPlus:
92 return QObject::tr("Plus");
93 case Common::Input::ButtonNames::ButtonMinus:
94 return QObject::tr("Minus");
95 case Common::Input::ButtonNames::ButtonHome:
96 return QObject::tr("Home");
97 case Common::Input::ButtonNames::ButtonCapture:
98 return QObject::tr("Capture");
79 case Common::Input::ButtonNames::L1: 99 case Common::Input::ButtonNames::L1:
80 return QObject::tr("L1"); 100 return QObject::tr("L1");
81 case Common::Input::ButtonNames::L2: 101 case Common::Input::ButtonNames::L2:
@@ -162,12 +182,13 @@ QString ConfigureInputPlayer::ButtonToText(const Common::ParamPackage& param) {
162 const QString toggle = QString::fromStdString(param.Get("toggle", false) ? "~" : ""); 182 const QString toggle = QString::fromStdString(param.Get("toggle", false) ? "~" : "");
163 const QString inverted = QString::fromStdString(param.Get("inverted", false) ? "!" : ""); 183 const QString inverted = QString::fromStdString(param.Get("inverted", false) ? "!" : "");
164 const QString invert = QString::fromStdString(param.Get("invert", "+") == "-" ? "-" : ""); 184 const QString invert = QString::fromStdString(param.Get("invert", "+") == "-" ? "-" : "");
185 const QString turbo = QString::fromStdString(param.Get("turbo", false) ? "$" : "");
165 const auto common_button_name = input_subsystem->GetButtonName(param); 186 const auto common_button_name = input_subsystem->GetButtonName(param);
166 187
167 // Retrieve the names from Qt 188 // Retrieve the names from Qt
168 if (param.Get("engine", "") == "keyboard") { 189 if (param.Get("engine", "") == "keyboard") {
169 const QString button_str = GetKeyName(param.Get("code", 0)); 190 const QString button_str = GetKeyName(param.Get("code", 0));
170 return QObject::tr("%1%2%3").arg(toggle, inverted, button_str); 191 return QObject::tr("%1%2%3%4").arg(turbo, toggle, inverted, button_str);
171 } 192 }
172 193
173 if (common_button_name == Common::Input::ButtonNames::Invalid) { 194 if (common_button_name == Common::Input::ButtonNames::Invalid) {
@@ -181,7 +202,7 @@ QString ConfigureInputPlayer::ButtonToText(const Common::ParamPackage& param) {
181 if (common_button_name == Common::Input::ButtonNames::Value) { 202 if (common_button_name == Common::Input::ButtonNames::Value) {
182 if (param.Has("hat")) { 203 if (param.Has("hat")) {
183 const QString hat = GetDirectionName(param.Get("direction", "")); 204 const QString hat = GetDirectionName(param.Get("direction", ""));
184 return QObject::tr("%1%2Hat %3").arg(toggle, inverted, hat); 205 return QObject::tr("%1%2%3Hat %4").arg(turbo, toggle, inverted, hat);
185 } 206 }
186 if (param.Has("axis")) { 207 if (param.Has("axis")) {
187 const QString axis = QString::fromStdString(param.Get("axis", "")); 208 const QString axis = QString::fromStdString(param.Get("axis", ""));
@@ -199,13 +220,13 @@ QString ConfigureInputPlayer::ButtonToText(const Common::ParamPackage& param) {
199 } 220 }
200 if (param.Has("button")) { 221 if (param.Has("button")) {
201 const QString button = QString::fromStdString(param.Get("button", "")); 222 const QString button = QString::fromStdString(param.Get("button", ""));
202 return QObject::tr("%1%2Button %3").arg(toggle, inverted, button); 223 return QObject::tr("%1%2%3Button %4").arg(turbo, toggle, inverted, button);
203 } 224 }
204 } 225 }
205 226
206 QString button_name = GetButtonName(common_button_name); 227 QString button_name = GetButtonName(common_button_name);
207 if (param.Has("hat")) { 228 if (param.Has("hat")) {
208 return QObject::tr("%1%2Hat %3").arg(toggle, inverted, button_name); 229 return QObject::tr("%1%2%3Hat %4").arg(turbo, toggle, inverted, button_name);
209 } 230 }
210 if (param.Has("axis")) { 231 if (param.Has("axis")) {
211 return QObject::tr("%1%2Axis %3").arg(toggle, inverted, button_name); 232 return QObject::tr("%1%2Axis %3").arg(toggle, inverted, button_name);
@@ -214,7 +235,7 @@ QString ConfigureInputPlayer::ButtonToText(const Common::ParamPackage& param) {
214 return QObject::tr("%1%2Axis %3").arg(toggle, inverted, button_name); 235 return QObject::tr("%1%2Axis %3").arg(toggle, inverted, button_name);
215 } 236 }
216 if (param.Has("button")) { 237 if (param.Has("button")) {
217 return QObject::tr("%1%2Button %3").arg(toggle, inverted, button_name); 238 return QObject::tr("%1%2%3Button %4").arg(turbo, toggle, inverted, button_name);
218 } 239 }
219 240
220 return QObject::tr("[unknown]"); 241 return QObject::tr("[unknown]");
@@ -375,6 +396,12 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_i
375 button_map[button_id]->setText(ButtonToText(param)); 396 button_map[button_id]->setText(ButtonToText(param));
376 emulated_controller->SetButtonParam(button_id, param); 397 emulated_controller->SetButtonParam(button_id, param);
377 }); 398 });
399 context_menu.addAction(tr("Turbo button"), [&] {
400 const bool turbo_value = !param.Get("turbo", false);
401 param.Set("turbo", turbo_value);
402 button_map[button_id]->setText(ButtonToText(param));
403 emulated_controller->SetButtonParam(button_id, param);
404 });
378 } 405 }
379 if (param.Has("axis")) { 406 if (param.Has("axis")) {
380 context_menu.addAction(tr("Invert axis"), [&] { 407 context_menu.addAction(tr("Invert axis"), [&] {
diff --git a/src/yuzu/configuration/configure_input_player_widget.cpp b/src/yuzu/configuration/configure_input_player_widget.cpp
index 11390fec0..c287220fc 100644
--- a/src/yuzu/configuration/configure_input_player_widget.cpp
+++ b/src/yuzu/configuration/configure_input_player_widget.cpp
@@ -81,7 +81,6 @@ void PlayerControlPreview::UpdateColors() {
81 colors.outline = QColor(0, 0, 0); 81 colors.outline = QColor(0, 0, 0);
82 colors.primary = QColor(225, 225, 225); 82 colors.primary = QColor(225, 225, 225);
83 colors.button = QColor(109, 111, 114); 83 colors.button = QColor(109, 111, 114);
84 colors.button2 = QColor(109, 111, 114);
85 colors.button2 = QColor(77, 80, 84); 84 colors.button2 = QColor(77, 80, 84);
86 colors.slider_arrow = QColor(65, 68, 73); 85 colors.slider_arrow = QColor(65, 68, 73);
87 colors.font2 = QColor(0, 0, 0); 86 colors.font2 = QColor(0, 0, 0);
@@ -100,12 +99,17 @@ void PlayerControlPreview::UpdateColors() {
100 colors.led_off = QColor(170, 238, 255); 99 colors.led_off = QColor(170, 238, 255);
101 colors.indicator2 = QColor(59, 165, 93); 100 colors.indicator2 = QColor(59, 165, 93);
102 colors.charging = QColor(250, 168, 26); 101 colors.charging = QColor(250, 168, 26);
102 colors.button_turbo = QColor(217, 158, 4);
103 103
104 colors.left = colors.primary; 104 colors.left = colors.primary;
105 colors.right = colors.primary; 105 colors.right = colors.primary;
106 // Possible alternative to set colors from settings 106
107 // colors.left = QColor(controller->GetColors().left.body); 107 const auto color_left = controller->GetColorsValues()[0].body;
108 // colors.right = QColor(controller->GetColors().right.body); 108 const auto color_right = controller->GetColorsValues()[1].body;
109 if (color_left != 0 && color_right != 0) {
110 colors.left = QColor(color_left);
111 colors.right = QColor(color_right);
112 }
109} 113}
110 114
111void PlayerControlPreview::ResetInputs() { 115void PlayerControlPreview::ResetInputs() {
@@ -2465,7 +2469,6 @@ void PlayerControlPreview::DrawJoystickDot(QPainter& p, const QPointF center,
2465void PlayerControlPreview::DrawRoundButton(QPainter& p, QPointF center, 2469void PlayerControlPreview::DrawRoundButton(QPainter& p, QPointF center,
2466 const Common::Input::ButtonStatus& pressed, float width, 2470 const Common::Input::ButtonStatus& pressed, float width,
2467 float height, Direction direction, float radius) { 2471 float height, Direction direction, float radius) {
2468 p.setBrush(button_color);
2469 if (pressed.value) { 2472 if (pressed.value) {
2470 switch (direction) { 2473 switch (direction) {
2471 case Direction::Left: 2474 case Direction::Left:
@@ -2483,16 +2486,16 @@ void PlayerControlPreview::DrawRoundButton(QPainter& p, QPointF center,
2483 case Direction::None: 2486 case Direction::None:
2484 break; 2487 break;
2485 } 2488 }
2486 p.setBrush(colors.highlight);
2487 } 2489 }
2488 QRectF rect = {center.x() - width, center.y() - height, width * 2.0f, height * 2.0f}; 2490 QRectF rect = {center.x() - width, center.y() - height, width * 2.0f, height * 2.0f};
2491 p.setBrush(GetButtonColor(button_color, pressed.value, pressed.turbo));
2489 p.drawRoundedRect(rect, radius, radius); 2492 p.drawRoundedRect(rect, radius, radius);
2490} 2493}
2491void PlayerControlPreview::DrawMinusButton(QPainter& p, const QPointF center, 2494void PlayerControlPreview::DrawMinusButton(QPainter& p, const QPointF center,
2492 const Common::Input::ButtonStatus& pressed, 2495 const Common::Input::ButtonStatus& pressed,
2493 int button_size) { 2496 int button_size) {
2494 p.setPen(colors.outline); 2497 p.setPen(colors.outline);
2495 p.setBrush(pressed.value ? colors.highlight : colors.button); 2498 p.setBrush(GetButtonColor(colors.button, pressed.value, pressed.turbo));
2496 DrawRectangle(p, center, button_size, button_size / 3.0f); 2499 DrawRectangle(p, center, button_size, button_size / 3.0f);
2497} 2500}
2498void PlayerControlPreview::DrawPlusButton(QPainter& p, const QPointF center, 2501void PlayerControlPreview::DrawPlusButton(QPainter& p, const QPointF center,
@@ -2500,7 +2503,7 @@ void PlayerControlPreview::DrawPlusButton(QPainter& p, const QPointF center,
2500 int button_size) { 2503 int button_size) {
2501 // Draw outer line 2504 // Draw outer line
2502 p.setPen(colors.outline); 2505 p.setPen(colors.outline);
2503 p.setBrush(pressed.value ? colors.highlight : colors.button); 2506 p.setBrush(GetButtonColor(colors.button, pressed.value, pressed.turbo));
2504 DrawRectangle(p, center, button_size, button_size / 3.0f); 2507 DrawRectangle(p, center, button_size, button_size / 3.0f);
2505 DrawRectangle(p, center, button_size / 3.0f, button_size); 2508 DrawRectangle(p, center, button_size / 3.0f, button_size);
2506 2509
@@ -2522,7 +2525,7 @@ void PlayerControlPreview::DrawGCButtonX(QPainter& p, const QPointF center,
2522 } 2525 }
2523 2526
2524 p.setPen(colors.outline); 2527 p.setPen(colors.outline);
2525 p.setBrush(pressed.value ? colors.highlight : colors.button); 2528 p.setBrush(GetButtonColor(colors.button, pressed.value, pressed.turbo));
2526 DrawPolygon(p, button_x); 2529 DrawPolygon(p, button_x);
2527} 2530}
2528 2531
@@ -2535,7 +2538,7 @@ void PlayerControlPreview::DrawGCButtonY(QPainter& p, const QPointF center,
2535 } 2538 }
2536 2539
2537 p.setPen(colors.outline); 2540 p.setPen(colors.outline);
2538 p.setBrush(pressed.value ? colors.highlight : colors.button); 2541 p.setBrush(GetButtonColor(colors.button, pressed.value, pressed.turbo));
2539 DrawPolygon(p, button_x); 2542 DrawPolygon(p, button_x);
2540} 2543}
2541 2544
@@ -2549,17 +2552,15 @@ void PlayerControlPreview::DrawGCButtonZ(QPainter& p, const QPointF center,
2549 } 2552 }
2550 2553
2551 p.setPen(colors.outline); 2554 p.setPen(colors.outline);
2552 p.setBrush(pressed.value ? colors.highlight : colors.button2); 2555 p.setBrush(GetButtonColor(colors.button2, pressed.value, pressed.turbo));
2553 DrawPolygon(p, button_x); 2556 DrawPolygon(p, button_x);
2554} 2557}
2555 2558
2556void PlayerControlPreview::DrawCircleButton(QPainter& p, const QPointF center, 2559void PlayerControlPreview::DrawCircleButton(QPainter& p, const QPointF center,
2557 const Common::Input::ButtonStatus& pressed, 2560 const Common::Input::ButtonStatus& pressed,
2558 float button_size) { 2561 float button_size) {
2559 p.setBrush(button_color); 2562
2560 if (pressed.value) { 2563 p.setBrush(GetButtonColor(button_color, pressed.value, pressed.turbo));
2561 p.setBrush(colors.highlight);
2562 }
2563 p.drawEllipse(center, button_size, button_size); 2564 p.drawEllipse(center, button_size, button_size);
2564} 2565}
2565 2566
@@ -2616,7 +2617,7 @@ void PlayerControlPreview::DrawArrowButton(QPainter& p, const QPointF center,
2616 2617
2617 // Draw arrow button 2618 // Draw arrow button
2618 p.setPen(pressed.value ? colors.highlight : colors.button); 2619 p.setPen(pressed.value ? colors.highlight : colors.button);
2619 p.setBrush(pressed.value ? colors.highlight : colors.button); 2620 p.setBrush(GetButtonColor(colors.button, pressed.value, pressed.turbo));
2620 DrawPolygon(p, arrow_button); 2621 DrawPolygon(p, arrow_button);
2621 2622
2622 switch (direction) { 2623 switch (direction) {
@@ -2668,10 +2669,20 @@ void PlayerControlPreview::DrawTriggerButton(QPainter& p, const QPointF center,
2668 2669
2669 // Draw arrow button 2670 // Draw arrow button
2670 p.setPen(colors.outline); 2671 p.setPen(colors.outline);
2671 p.setBrush(pressed.value ? colors.highlight : colors.button); 2672 p.setBrush(GetButtonColor(colors.button, pressed.value, pressed.turbo));
2672 DrawPolygon(p, qtrigger_button); 2673 DrawPolygon(p, qtrigger_button);
2673} 2674}
2674 2675
2676QColor PlayerControlPreview::GetButtonColor(QColor default_color, bool is_pressed, bool turbo) {
2677 if (is_pressed && turbo) {
2678 return colors.button_turbo;
2679 }
2680 if (is_pressed) {
2681 return colors.highlight;
2682 }
2683 return default_color;
2684}
2685
2675void PlayerControlPreview::DrawBattery(QPainter& p, QPointF center, 2686void PlayerControlPreview::DrawBattery(QPainter& p, QPointF center,
2676 Common::Input::BatteryLevel battery) { 2687 Common::Input::BatteryLevel battery) {
2677 if (battery == Common::Input::BatteryLevel::None) { 2688 if (battery == Common::Input::BatteryLevel::None) {
diff --git a/src/yuzu/configuration/configure_input_player_widget.h b/src/yuzu/configuration/configure_input_player_widget.h
index b258c6d77..0e9e95e85 100644
--- a/src/yuzu/configuration/configure_input_player_widget.h
+++ b/src/yuzu/configuration/configure_input_player_widget.h
@@ -81,6 +81,7 @@ private:
81 QColor right{}; 81 QColor right{};
82 QColor button{}; 82 QColor button{};
83 QColor button2{}; 83 QColor button2{};
84 QColor button_turbo{};
84 QColor font{}; 85 QColor font{};
85 QColor font2{}; 86 QColor font2{};
86 QColor highlight{}; 87 QColor highlight{};
@@ -183,6 +184,7 @@ private:
183 const Common::Input::ButtonStatus& pressed, float size = 1.0f); 184 const Common::Input::ButtonStatus& pressed, float size = 1.0f);
184 void DrawTriggerButton(QPainter& p, QPointF center, Direction direction, 185 void DrawTriggerButton(QPainter& p, QPointF center, Direction direction,
185 const Common::Input::ButtonStatus& pressed); 186 const Common::Input::ButtonStatus& pressed);
187 QColor GetButtonColor(QColor default_color, bool is_pressed, bool turbo);
186 188
187 // Draw battery functions 189 // Draw battery functions
188 void DrawBattery(QPainter& p, QPointF center, Common::Input::BatteryLevel battery); 190 void DrawBattery(QPainter& p, QPointF center, Common::Input::BatteryLevel battery);
diff --git a/src/yuzu/configuration/configure_motion_touch.cpp b/src/yuzu/configuration/configure_motion_touch.cpp
index d1b870c72..fb1292f07 100644
--- a/src/yuzu/configuration/configure_motion_touch.cpp
+++ b/src/yuzu/configuration/configure_motion_touch.cpp
@@ -89,7 +89,6 @@ ConfigureMotionTouch::ConfigureMotionTouch(QWidget* parent,
89 "using-a-controller-or-android-phone-for-motion-or-touch-input'><span " 89 "using-a-controller-or-android-phone-for-motion-or-touch-input'><span "
90 "style=\"text-decoration: underline; color:#039be5;\">Learn More</span></a>")); 90 "style=\"text-decoration: underline; color:#039be5;\">Learn More</span></a>"));
91 91
92 setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);
93 SetConfiguration(); 92 SetConfiguration();
94 UpdateUiDisplay(); 93 UpdateUiDisplay();
95 ConnectEvents(); 94 ConnectEvents();
diff --git a/src/yuzu/configuration/configure_per_game.cpp b/src/yuzu/configuration/configure_per_game.cpp
index 93db47cfd..7e757eafd 100644
--- a/src/yuzu/configuration/configure_per_game.cpp
+++ b/src/yuzu/configuration/configure_per_game.cpp
@@ -66,8 +66,6 @@ ConfigurePerGame::ConfigurePerGame(QWidget* parent, u64 title_id_, const std::st
66 66
67 setFocusPolicy(Qt::ClickFocus); 67 setFocusPolicy(Qt::ClickFocus);
68 setWindowTitle(tr("Properties")); 68 setWindowTitle(tr("Properties"));
69 // remove Help question mark button from the title bar
70 setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);
71 69
72 addons_tab->SetTitleId(title_id); 70 addons_tab->SetTitleId(title_id);
73 71
diff --git a/src/yuzu/configuration/configure_ringcon.cpp b/src/yuzu/configuration/configure_ringcon.cpp
index 688c2dd38..1275f10c8 100644
--- a/src/yuzu/configuration/configure_ringcon.cpp
+++ b/src/yuzu/configuration/configure_ringcon.cpp
@@ -4,9 +4,11 @@
4#include <memory> 4#include <memory>
5#include <QKeyEvent> 5#include <QKeyEvent>
6#include <QMenu> 6#include <QMenu>
7#include <QMessageBox>
7#include <QTimer> 8#include <QTimer>
9#include <fmt/format.h>
8 10
9#include "core/hid/emulated_devices.h" 11#include "core/hid/emulated_controller.h"
10#include "core/hid/hid_core.h" 12#include "core/hid/hid_core.h"
11#include "input_common/drivers/keyboard.h" 13#include "input_common/drivers/keyboard.h"
12#include "input_common/drivers/mouse.h" 14#include "input_common/drivers/mouse.h"
@@ -126,9 +128,16 @@ ConfigureRingController::ConfigureRingController(QWidget* parent,
126 ui->buttonRingAnalogPush, 128 ui->buttonRingAnalogPush,
127 }; 129 };
128 130
129 emulated_device = hid_core_.GetEmulatedDevices(); 131 emulated_controller = hid_core_.GetEmulatedController(Core::HID::NpadIdType::Player1);
130 emulated_device->SaveCurrentConfig(); 132 emulated_controller->SaveCurrentConfig();
131 emulated_device->EnableConfiguration(); 133 emulated_controller->EnableConfiguration();
134
135 Core::HID::ControllerUpdateCallback engine_callback{
136 .on_change = [this](Core::HID::ControllerTriggerType type) { ControllerUpdate(type); },
137 .is_npad_service = false,
138 };
139 callback_key = emulated_controller->SetCallback(engine_callback);
140 is_controller_set = true;
132 141
133 LoadConfiguration(); 142 LoadConfiguration();
134 143
@@ -143,9 +152,9 @@ ConfigureRingController::ConfigureRingController(QWidget* parent,
143 HandleClick( 152 HandleClick(
144 analog_map_buttons[sub_button_id], 153 analog_map_buttons[sub_button_id],
145 [=, this](const Common::ParamPackage& params) { 154 [=, this](const Common::ParamPackage& params) {
146 Common::ParamPackage param = emulated_device->GetRingParam(); 155 Common::ParamPackage param = emulated_controller->GetRingParam();
147 SetAnalogParam(params, param, analog_sub_buttons[sub_button_id]); 156 SetAnalogParam(params, param, analog_sub_buttons[sub_button_id]);
148 emulated_device->SetRingParam(param); 157 emulated_controller->SetRingParam(param);
149 }, 158 },
150 InputCommon::Polling::InputType::Stick); 159 InputCommon::Polling::InputType::Stick);
151 }); 160 });
@@ -155,16 +164,16 @@ ConfigureRingController::ConfigureRingController(QWidget* parent,
155 connect(analog_button, &QPushButton::customContextMenuRequested, 164 connect(analog_button, &QPushButton::customContextMenuRequested,
156 [=, this](const QPoint& menu_location) { 165 [=, this](const QPoint& menu_location) {
157 QMenu context_menu; 166 QMenu context_menu;
158 Common::ParamPackage param = emulated_device->GetRingParam(); 167 Common::ParamPackage param = emulated_controller->GetRingParam();
159 context_menu.addAction(tr("Clear"), [&] { 168 context_menu.addAction(tr("Clear"), [&] {
160 emulated_device->SetRingParam({}); 169 emulated_controller->SetRingParam(param);
161 analog_map_buttons[sub_button_id]->setText(tr("[not set]")); 170 analog_map_buttons[sub_button_id]->setText(tr("[not set]"));
162 }); 171 });
163 context_menu.addAction(tr("Invert axis"), [&] { 172 context_menu.addAction(tr("Invert axis"), [&] {
164 const bool invert_value = param.Get("invert_x", "+") == "-"; 173 const bool invert_value = param.Get("invert_x", "+") == "-";
165 const std::string invert_str = invert_value ? "+" : "-"; 174 const std::string invert_str = invert_value ? "+" : "-";
166 param.Set("invert_x", invert_str); 175 param.Set("invert_x", invert_str);
167 emulated_device->SetRingParam(param); 176 emulated_controller->SetRingParam(param);
168 for (int sub_button_id2 = 0; sub_button_id2 < ANALOG_SUB_BUTTONS_NUM; 177 for (int sub_button_id2 = 0; sub_button_id2 < ANALOG_SUB_BUTTONS_NUM;
169 ++sub_button_id2) { 178 ++sub_button_id2) {
170 analog_map_buttons[sub_button_id2]->setText( 179 analog_map_buttons[sub_button_id2]->setText(
@@ -177,16 +186,19 @@ ConfigureRingController::ConfigureRingController(QWidget* parent,
177 } 186 }
178 187
179 connect(ui->sliderRingAnalogDeadzone, &QSlider::valueChanged, [=, this] { 188 connect(ui->sliderRingAnalogDeadzone, &QSlider::valueChanged, [=, this] {
180 Common::ParamPackage param = emulated_device->GetRingParam(); 189 Common::ParamPackage param = emulated_controller->GetRingParam();
181 const auto slider_value = ui->sliderRingAnalogDeadzone->value(); 190 const auto slider_value = ui->sliderRingAnalogDeadzone->value();
182 ui->labelRingAnalogDeadzone->setText(tr("Deadzone: %1%").arg(slider_value)); 191 ui->labelRingAnalogDeadzone->setText(tr("Deadzone: %1%").arg(slider_value));
183 param.Set("deadzone", slider_value / 100.0f); 192 param.Set("deadzone", slider_value / 100.0f);
184 emulated_device->SetRingParam(param); 193 emulated_controller->SetRingParam(param);
185 }); 194 });
186 195
187 connect(ui->restore_defaults_button, &QPushButton::clicked, this, 196 connect(ui->restore_defaults_button, &QPushButton::clicked, this,
188 &ConfigureRingController::RestoreDefaults); 197 &ConfigureRingController::RestoreDefaults);
189 198
199 connect(ui->enable_ring_controller_button, &QPushButton::clicked, this,
200 &ConfigureRingController::EnableRingController);
201
190 timeout_timer->setSingleShot(true); 202 timeout_timer->setSingleShot(true);
191 connect(timeout_timer.get(), &QTimer::timeout, [this] { SetPollingResult({}, true); }); 203 connect(timeout_timer.get(), &QTimer::timeout, [this] { SetPollingResult({}, true); });
192 204
@@ -202,7 +214,14 @@ ConfigureRingController::ConfigureRingController(QWidget* parent,
202} 214}
203 215
204ConfigureRingController::~ConfigureRingController() { 216ConfigureRingController::~ConfigureRingController() {
205 emulated_device->DisableConfiguration(); 217 emulated_controller->SetPollingMode(Core::HID::EmulatedDeviceIndex::RightIndex,
218 Common::Input::PollingMode::Active);
219 emulated_controller->DisableConfiguration();
220
221 if (is_controller_set) {
222 emulated_controller->DeleteCallback(callback_key);
223 is_controller_set = false;
224 }
206}; 225};
207 226
208void ConfigureRingController::changeEvent(QEvent* event) { 227void ConfigureRingController::changeEvent(QEvent* event) {
@@ -219,7 +238,7 @@ void ConfigureRingController::RetranslateUI() {
219 238
220void ConfigureRingController::UpdateUI() { 239void ConfigureRingController::UpdateUI() {
221 RetranslateUI(); 240 RetranslateUI();
222 const Common::ParamPackage param = emulated_device->GetRingParam(); 241 const Common::ParamPackage param = emulated_controller->GetRingParam();
223 242
224 for (int sub_button_id = 0; sub_button_id < ANALOG_SUB_BUTTONS_NUM; ++sub_button_id) { 243 for (int sub_button_id = 0; sub_button_id < ANALOG_SUB_BUTTONS_NUM; ++sub_button_id) {
225 auto* const analog_button = analog_map_buttons[sub_button_id]; 244 auto* const analog_button = analog_map_buttons[sub_button_id];
@@ -240,9 +259,9 @@ void ConfigureRingController::UpdateUI() {
240} 259}
241 260
242void ConfigureRingController::ApplyConfiguration() { 261void ConfigureRingController::ApplyConfiguration() {
243 emulated_device->DisableConfiguration(); 262 emulated_controller->DisableConfiguration();
244 emulated_device->SaveCurrentConfig(); 263 emulated_controller->SaveCurrentConfig();
245 emulated_device->EnableConfiguration(); 264 emulated_controller->EnableConfiguration();
246} 265}
247 266
248void ConfigureRingController::LoadConfiguration() { 267void ConfigureRingController::LoadConfiguration() {
@@ -252,10 +271,62 @@ void ConfigureRingController::LoadConfiguration() {
252void ConfigureRingController::RestoreDefaults() { 271void ConfigureRingController::RestoreDefaults() {
253 const std::string default_ring_string = InputCommon::GenerateAnalogParamFromKeys( 272 const std::string default_ring_string = InputCommon::GenerateAnalogParamFromKeys(
254 0, 0, Config::default_ringcon_analogs[0], Config::default_ringcon_analogs[1], 0, 0.05f); 273 0, 0, Config::default_ringcon_analogs[0], Config::default_ringcon_analogs[1], 0, 0.05f);
255 emulated_device->SetRingParam(Common::ParamPackage(default_ring_string)); 274 emulated_controller->SetRingParam(Common::ParamPackage(default_ring_string));
256 UpdateUI(); 275 UpdateUI();
257} 276}
258 277
278void ConfigureRingController::EnableRingController() {
279 const auto dialog_title = tr("Error enabling ring input");
280
281 is_ring_enabled = false;
282 ui->ring_controller_sensor_value->setText(tr("Not connected"));
283
284 if (!Settings::values.enable_joycon_driver) {
285 QMessageBox::warning(this, dialog_title, tr("Direct Joycon driver is not enabled"));
286 return;
287 }
288
289 ui->enable_ring_controller_button->setEnabled(false);
290 ui->enable_ring_controller_button->setText(tr("Configuring"));
291 // SetPollingMode is blocking. Allow to update the button status before calling the command
292 repaint();
293
294 const auto result = emulated_controller->SetPollingMode(
295 Core::HID::EmulatedDeviceIndex::RightIndex, Common::Input::PollingMode::Ring);
296 switch (result) {
297 case Common::Input::DriverResult::Success:
298 is_ring_enabled = true;
299 break;
300 case Common::Input::DriverResult::NotSupported:
301 QMessageBox::warning(this, dialog_title,
302 tr("The current mapped device doesn't support the ring controller"));
303 break;
304 case Common::Input::DriverResult::NoDeviceDetected:
305 QMessageBox::warning(this, dialog_title,
306 tr("The current mapped device doesn't have a ring attached"));
307 break;
308 default:
309 QMessageBox::warning(this, dialog_title,
310 tr("Unexpected driver result %1").arg(static_cast<int>(result)));
311 break;
312 }
313 ui->enable_ring_controller_button->setEnabled(true);
314 ui->enable_ring_controller_button->setText(tr("Enable"));
315}
316
317void ConfigureRingController::ControllerUpdate(Core::HID::ControllerTriggerType type) {
318 if (!is_ring_enabled) {
319 return;
320 }
321 if (type != Core::HID::ControllerTriggerType::RingController) {
322 return;
323 }
324
325 const auto value = emulated_controller->GetRingSensorValues();
326 const auto tex_value = QString::fromStdString(fmt::format("{:.3f}", value.raw_value));
327 ui->ring_controller_sensor_value->setText(tex_value);
328}
329
259void ConfigureRingController::HandleClick( 330void ConfigureRingController::HandleClick(
260 QPushButton* button, std::function<void(const Common::ParamPackage&)> new_input_setter, 331 QPushButton* button, std::function<void(const Common::ParamPackage&)> new_input_setter,
261 InputCommon::Polling::InputType type) { 332 InputCommon::Polling::InputType type) {
diff --git a/src/yuzu/configuration/configure_ringcon.h b/src/yuzu/configuration/configure_ringcon.h
index 38a9cb716..b23c27906 100644
--- a/src/yuzu/configuration/configure_ringcon.h
+++ b/src/yuzu/configuration/configure_ringcon.h
@@ -13,7 +13,7 @@ class InputSubsystem;
13 13
14namespace Core::HID { 14namespace Core::HID {
15class HIDCore; 15class HIDCore;
16class EmulatedDevices; 16class EmulatedController;
17} // namespace Core::HID 17} // namespace Core::HID
18 18
19namespace Ui { 19namespace Ui {
@@ -42,6 +42,12 @@ private:
42 /// Restore all buttons to their default values. 42 /// Restore all buttons to their default values.
43 void RestoreDefaults(); 43 void RestoreDefaults();
44 44
45 /// Sets current polling mode to ring input
46 void EnableRingController();
47
48 // Handles emulated controller events
49 void ControllerUpdate(Core::HID::ControllerTriggerType type);
50
45 /// Called when the button was pressed. 51 /// Called when the button was pressed.
46 void HandleClick(QPushButton* button, 52 void HandleClick(QPushButton* button,
47 std::function<void(const Common::ParamPackage&)> new_input_setter, 53 std::function<void(const Common::ParamPackage&)> new_input_setter,
@@ -78,7 +84,11 @@ private:
78 std::optional<std::function<void(const Common::ParamPackage&)>> input_setter; 84 std::optional<std::function<void(const Common::ParamPackage&)>> input_setter;
79 85
80 InputCommon::InputSubsystem* input_subsystem; 86 InputCommon::InputSubsystem* input_subsystem;
81 Core::HID::EmulatedDevices* emulated_device; 87 Core::HID::EmulatedController* emulated_controller;
88
89 bool is_ring_enabled{};
90 bool is_controller_set{};
91 int callback_key;
82 92
83 std::unique_ptr<Ui::ConfigureRingController> ui; 93 std::unique_ptr<Ui::ConfigureRingController> ui;
84}; 94};
diff --git a/src/yuzu/configuration/configure_ringcon.ui b/src/yuzu/configuration/configure_ringcon.ui
index 9ec634dd4..514dff372 100644
--- a/src/yuzu/configuration/configure_ringcon.ui
+++ b/src/yuzu/configuration/configure_ringcon.ui
@@ -6,8 +6,8 @@
6 <rect> 6 <rect>
7 <x>0</x> 7 <x>0</x>
8 <y>0</y> 8 <y>0</y>
9 <width>298</width> 9 <width>315</width>
10 <height>339</height> 10 <height>400</height>
11 </rect> 11 </rect>
12 </property> 12 </property>
13 <property name="windowTitle"> 13 <property name="windowTitle">
@@ -46,187 +46,283 @@
46 </property> 46 </property>
47 </spacer> 47 </spacer>
48 </item> 48 </item>
49 <item> 49 <item>
50 <widget class="QGroupBox" name="RingAnalog"> 50 <widget class="QGroupBox" name="RingAnalog">
51 <property name="title"> 51 <property name="title">
52 <string>Ring Sensor Parameters</string> 52 <string>Virtual Ring Sensor Parameters</string>
53 </property> 53 </property>
54 <property name="alignment"> 54 <property name="alignment">
55 <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set> 55 <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
56 </property> 56 </property>
57 <layout class="QVBoxLayout" name="verticalLayout_3"> 57 <layout class="QVBoxLayout" name="verticalLayout_1">
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"> 58 <property name="spacing">
79 <number>3</number> 59 <number>0</number>
80 </property> 60 </property>
81 <item alignment="Qt::AlignHCenter"> 61 <property name="sizeConstraint">
82 <widget class="QGroupBox" name="buttonRingAnalogPullGroup"> 62 <enum>QLayout::SetDefaultConstraint</enum>
83 <property name="title"> 63 </property>
84 <string>Pull</string> 64 <property name="leftMargin">
85 </property> 65 <number>3</number>
86 <property name="alignment"> 66 </property>
87 <set>Qt::AlignCenter</set> 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>
88 </property> 80 </property>
89 <layout class="QVBoxLayout" name="buttonRingAnalogPullVerticalLayout"> 81 <item alignment="Qt::AlignHCenter">
90 <property name="spacing"> 82 <widget class="QGroupBox" name="buttonRingAnalogPullGroup">
91 <number>3</number> 83 <property name="title">
84 <string>Pull</string>
92 </property> 85 </property>
93 <property name="leftMargin"> 86 <property name="alignment">
94 <number>3</number> 87 <set>Qt::AlignCenter</set>
95 </property>
96 <property name="topMargin">
97 <number>3</number>
98 </property> 88 </property>
99 <property name="rightMargin"> 89 <layout class="QVBoxLayout" name="buttonRingAnalogPullVerticalLayout">
100 <number>3</number> 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>70</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>
101 </property> 134 </property>
102 <property name="bottomMargin"> 135 <property name="alignment">
103 <number>3</number> 136 <set>Qt::AlignCenter</set>
104 </property> 137 </property>
105 <item> 138 <layout class="QVBoxLayout" name="buttonRingAnalogPushVerticalLayout">
106 <widget class="QPushButton" name="buttonRingAnalogPull"> 139 <property name="spacing">
107 <property name="minimumSize"> 140 <number>3</number>
108 <size> 141 </property>
109 <width>68</width> 142 <property name="leftMargin">
110 <height>0</height> 143 <number>3</number>
111 </size> 144 </property>
112 </property> 145 <property name="topMargin">
113 <property name="maximumSize"> 146 <number>3</number>
114 <size> 147 </property>
115 <width>68</width> 148 <property name="rightMargin">
116 <height>16777215</height> 149 <number>3</number>
117 </size> 150 </property>
118 </property> 151 <property name="bottomMargin">
119 <property name="styleSheet"> 152 <number>3</number>
120 <string notr="true">min-width: 68px;</string> 153 </property>
121 </property> 154 <item>
122 <property name="text"> 155 <widget class="QPushButton" name="buttonRingAnalogPush">
123 <string>Pull</string> 156 <property name="minimumSize">
124 </property> 157 <size>
125 </widget> 158 <width>70</width>
126 </item> 159 <height>0</height>
127 </layout> 160 </size>
128 </widget> 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>
129 </item> 180 </item>
130 <item alignment="Qt::AlignHCenter"> 181 <item>
131 <widget class="QGroupBox" name="buttonRingAnalogPushGroup"> 182 <layout class="QVBoxLayout" name="sliderRingAnalogDeadzoneVerticalLayout">
132 <property name="title"> 183 <property name="spacing">
133 <string>Push</string> 184 <number>3</number>
134 </property> 185 </property>
135 <property name="alignment"> 186 <property name="sizeConstraint">
136 <set>Qt::AlignCenter</set> 187 <enum>QLayout::SetDefaultConstraint</enum>
137 </property> 188 </property>
138 <layout class="QVBoxLayout" name="buttonRingAnalogPushVerticalLayout"> 189 <property name="leftMargin">
139 <property name="spacing"> 190 <number>0</number>
140 <number>3</number> 191 </property>
141 </property> 192 <property name="topMargin">
142 <property name="leftMargin"> 193 <number>10</number>
143 <number>3</number> 194 </property>
144 </property> 195 <property name="rightMargin">
145 <property name="topMargin"> 196 <number>0</number>
146 <number>3</number> 197 </property>
147 </property> 198 <property name="bottomMargin">
148 <property name="rightMargin"> 199 <number>3</number>
149 <number>3</number> 200 </property>
150 </property> 201 <item>
151 <property name="bottomMargin"> 202 <layout class="QHBoxLayout" name="sliderRingAnalogDeadzoneHorizontalLayout">
152 <number>3</number>
153 </property>
154 <item> 203 <item>
155 <widget class="QPushButton" name="buttonRingAnalogPush"> 204 <widget class="QLabel" name="labelRingAnalogDeadzone">
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"> 205 <property name="text">
172 <string>Push</string> 206 <string>Deadzone: 0%</string>
207 </property>
208 <property name="alignment">
209 <set>Qt::AlignHCenter</set>
173 </property> 210 </property>
174 </widget> 211 </widget>
175 </item> 212 </item>
176 </layout> 213 </layout>
177 </widget> 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>
178 </item> 226 </item>
179 </layout> 227 </layout>
180 </item> 228 </widget>
181 <item> 229 </item>
182 <layout class="QVBoxLayout" name="sliderRingAnalogDeadzoneVerticalLayout"> 230 <item>
231 <widget class="QGroupBox" name="RingDriver">
232 <property name="title">
233 <string>Direct Joycon Driver</string>
234 </property>
235 <property name="alignment">
236 <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
237 </property>
238 <layout class="QVBoxLayout" name="verticalLayout_2">
183 <property name="spacing"> 239 <property name="spacing">
184 <number>3</number> 240 <number>0</number>
185 </property> 241 </property>
186 <property name="sizeConstraint"> 242 <property name="sizeConstraint">
187 <enum>QLayout::SetDefaultConstraint</enum> 243 <enum>QLayout::SetDefaultConstraint</enum>
188 </property> 244 </property>
189 <property name="leftMargin"> 245 <property name="leftMargin">
190 <number>0</number> 246 <number>3</number>
191 </property> 247 </property>
192 <property name="topMargin"> 248 <property name="topMargin">
193 <number>10</number> 249 <number>6</number>
194 </property> 250 </property>
195 <property name="rightMargin"> 251 <property name="rightMargin">
196 <number>0</number> 252 <number>3</number>
197 </property> 253 </property>
198 <property name="bottomMargin"> 254 <property name="bottomMargin">
199 <number>3</number> 255 <number>10</number>
200 </property> 256 </property>
201 <item> 257 <item>
202 <layout class="QHBoxLayout" name="sliderRingAnalogDeadzoneHorizontalLayout"> 258 <layout class="QGridLayout" name="gridLayout">
203 <item> 259 <property name="leftMargin">
204 <widget class="QLabel" name="labelRingAnalogDeadzone"> 260 <number>10</number>
261 </property>
262 <property name="topMargin">
263 <number>6</number>
264 </property>
265 <property name="rightMargin">
266 <number>10</number>
267 </property>
268 <property name="bottomMargin">
269 <number>10</number>
270 </property>
271 <property name="verticalSpacing">
272 <number>10</number>
273 </property>
274 <item row="0" column="1">
275 <spacer name="horizontalSpacer">
276 <property name="orientation">
277 <enum>Qt::Horizontal</enum>
278 </property>
279 <property name="sizeType">
280 <enum>QSizePolicy::Fixed</enum>
281 </property>
282 <property name="sizeHint" stdset="0">
283 <size>
284 <width>76</width>
285 <height>20</height>
286 </size>
287 </property>
288 </spacer>
289 </item>
290 <item row="0" column="0">
291 <widget class="QLabel" name="enable_ring_controller_label">
292 <property name="text">
293 <string>Enable Ring Input</string>
294 </property>
295 </widget>
296 </item>
297 <item row="0" column="2">
298 <widget class="QPushButton" name="enable_ring_controller_button">
205 <property name="text"> 299 <property name="text">
206 <string>Deadzone: 0%</string> 300 <string>Enable</string>
301 </property>
302 </widget>
303 </item>
304 <item row="1" column="0">
305 <widget class="QLabel" name="ring_controller_sensor_label">
306 <property name="text">
307 <string>Ring Sensor Value</string>
308 </property>
309 </widget>
310 </item>
311 <item row="1" column="2">
312 <widget class="QLabel" name="ring_controller_sensor_value">
313 <property name="text">
314 <string>Not connected</string>
207 </property> 315 </property>
208 <property name="alignment"> 316 <property name="alignment">
209 <set>Qt::AlignHCenter</set> 317 <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
210 </property> 318 </property>
211 </widget> 319 </widget>
212 </item> 320 </item>
213 </layout> 321 </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> 322 </item>
225 </layout> 323 </layout>
226 </item> 324 </widget>
227 </layout> 325 </item>
228 </widget>
229 </item>
230 <item> 326 <item>
231 <spacer name="verticalSpacer"> 327 <spacer name="verticalSpacer">
232 <property name="orientation"> 328 <property name="orientation">
@@ -273,6 +369,6 @@
273 <signal>rejected()</signal> 369 <signal>rejected()</signal>
274 <receiver>ConfigureRingController</receiver> 370 <receiver>ConfigureRingController</receiver>
275 <slot>reject()</slot> 371 <slot>reject()</slot>
276 </connection> 372 </connection>
277 </connections> 373 </connections>
278</ui> 374</ui>
diff --git a/src/yuzu/configuration/configure_system.cpp b/src/yuzu/configuration/configure_system.cpp
index 94049f2f4..9ea4c02da 100644
--- a/src/yuzu/configuration/configure_system.cpp
+++ b/src/yuzu/configuration/configure_system.cpp
@@ -31,6 +31,9 @@ constexpr std::array<u32, 7> LOCALE_BLOCKLIST{
31}; 31};
32 32
33static bool IsValidLocale(u32 region_index, u32 language_index) { 33static bool IsValidLocale(u32 region_index, u32 language_index) {
34 if (region_index >= LOCALE_BLOCKLIST.size()) {
35 return false;
36 }
34 return ((LOCALE_BLOCKLIST.at(region_index) >> language_index) & 1) == 0; 37 return ((LOCALE_BLOCKLIST.at(region_index) >> language_index) & 1) == 0;
35} 38}
36 39
@@ -55,8 +58,11 @@ ConfigureSystem::ConfigureSystem(Core::System& system_, QWidget* parent)
55 }); 58 });
56 59
57 const auto locale_check = [this](int index) { 60 const auto locale_check = [this](int index) {
58 const bool valid_locale = 61 const auto region_index = ConfigurationShared::GetComboboxIndex(
59 IsValidLocale(ui->combo_region->currentIndex(), ui->combo_language->currentIndex()); 62 Settings::values.region_index.GetValue(true), ui->combo_region);
63 const auto language_index = ConfigurationShared::GetComboboxIndex(
64 Settings::values.language_index.GetValue(true), ui->combo_language);
65 const bool valid_locale = IsValidLocale(region_index, language_index);
60 ui->label_warn_invalid_locale->setVisible(!valid_locale); 66 ui->label_warn_invalid_locale->setVisible(!valid_locale);
61 if (!valid_locale) { 67 if (!valid_locale) {
62 ui->label_warn_invalid_locale->setText( 68 ui->label_warn_invalid_locale->setText(
diff --git a/src/yuzu/configuration/configure_system.h b/src/yuzu/configuration/configure_system.h
index 8f02880a7..a7f086258 100644
--- a/src/yuzu/configuration/configure_system.h
+++ b/src/yuzu/configuration/configure_system.h
@@ -42,13 +42,7 @@ private:
42 std::unique_ptr<Ui::ConfigureSystem> ui; 42 std::unique_ptr<Ui::ConfigureSystem> ui;
43 bool enabled = false; 43 bool enabled = false;
44 44
45 int language_index = 0;
46 int region_index = 0;
47 int time_zone_index = 0;
48 int sound_index = 0;
49
50 ConfigurationShared::CheckState use_rng_seed; 45 ConfigurationShared::CheckState use_rng_seed;
51 ConfigurationShared::CheckState use_custom_rtc;
52 46
53 Core::System& system; 47 Core::System& system;
54}; 48};
diff --git a/src/yuzu/configuration/configure_tas.cpp b/src/yuzu/configuration/configure_tas.cpp
index 1edc5f1f3..5a545aa70 100644
--- a/src/yuzu/configuration/configure_tas.cpp
+++ b/src/yuzu/configuration/configure_tas.cpp
@@ -17,7 +17,6 @@ ConfigureTasDialog::ConfigureTasDialog(QWidget* parent)
17 17
18 setFocusPolicy(Qt::ClickFocus); 18 setFocusPolicy(Qt::ClickFocus);
19 setWindowTitle(tr("TAS Configuration")); 19 setWindowTitle(tr("TAS Configuration"));
20 setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);
21 20
22 connect(ui->tas_path_button, &QToolButton::pressed, this, 21 connect(ui->tas_path_button, &QToolButton::pressed, this,
23 [this] { SetDirectory(DirectoryTarget::TAS, ui->tas_path_edit); }); 22 [this] { SetDirectory(DirectoryTarget::TAS, ui->tas_path_edit); });
diff --git a/src/yuzu/configuration/input_profiles.cpp b/src/yuzu/configuration/input_profiles.cpp
index 9bb69cab1..41ef4250a 100644
--- a/src/yuzu/configuration/input_profiles.cpp
+++ b/src/yuzu/configuration/input_profiles.cpp
@@ -58,13 +58,16 @@ std::vector<std::string> InputProfiles::GetInputProfileNames() {
58 std::vector<std::string> profile_names; 58 std::vector<std::string> profile_names;
59 profile_names.reserve(map_profiles.size()); 59 profile_names.reserve(map_profiles.size());
60 60
61 for (const auto& [profile_name, config] : map_profiles) { 61 auto it = map_profiles.cbegin();
62 while (it != map_profiles.cend()) {
63 const auto& [profile_name, config] = *it;
62 if (!ProfileExistsInFilesystem(profile_name)) { 64 if (!ProfileExistsInFilesystem(profile_name)) {
63 DeleteProfile(profile_name); 65 it = map_profiles.erase(it);
64 continue; 66 continue;
65 } 67 }
66 68
67 profile_names.push_back(profile_name); 69 profile_names.push_back(profile_name);
70 ++it;
68 } 71 }
69 72
70 std::stable_sort(profile_names.begin(), profile_names.end()); 73 std::stable_sort(profile_names.begin(), profile_names.end());
diff --git a/src/yuzu/debugger/controller.cpp b/src/yuzu/debugger/controller.cpp
index 19f3775a3..e2f55ebae 100644
--- a/src/yuzu/debugger/controller.cpp
+++ b/src/yuzu/debugger/controller.cpp
@@ -20,9 +20,8 @@ ControllerDialog::ControllerDialog(Core::HID::HIDCore& hid_core_,
20 setWindowTitle(tr("Controller P1")); 20 setWindowTitle(tr("Controller P1"));
21 resize(500, 350); 21 resize(500, 350);
22 setMinimumSize(500, 350); 22 setMinimumSize(500, 350);
23 // Remove the "?" button from the titlebar and enable the maximize button 23 // Enable the maximize button
24 setWindowFlags((windowFlags() & ~Qt::WindowContextHelpButtonHint) | 24 setWindowFlags(windowFlags() | Qt::WindowMaximizeButtonHint);
25 Qt::WindowMaximizeButtonHint);
26 25
27 widget = new PlayerControlPreview(this); 26 widget = new PlayerControlPreview(this);
28 refreshConfiguration(); 27 refreshConfiguration();
diff --git a/src/yuzu/debugger/profiler.cpp b/src/yuzu/debugger/profiler.cpp
index d3e2d3c12..493ee0b17 100644
--- a/src/yuzu/debugger/profiler.cpp
+++ b/src/yuzu/debugger/profiler.cpp
@@ -49,9 +49,8 @@ MicroProfileDialog::MicroProfileDialog(QWidget* parent) : QWidget(parent, Qt::Di
49 setObjectName(QStringLiteral("MicroProfile")); 49 setObjectName(QStringLiteral("MicroProfile"));
50 setWindowTitle(tr("&MicroProfile")); 50 setWindowTitle(tr("&MicroProfile"));
51 resize(1000, 600); 51 resize(1000, 600);
52 // Remove the "?" button from the titlebar and enable the maximize button 52 // Enable the maximize button
53 setWindowFlags((windowFlags() & ~Qt::WindowContextHelpButtonHint) | 53 setWindowFlags(windowFlags() | Qt::WindowMaximizeButtonHint);
54 Qt::WindowMaximizeButtonHint);
55 54
56#if MICROPROFILE_ENABLED 55#if MICROPROFILE_ENABLED
57 56
diff --git a/src/yuzu/discord_impl.cpp b/src/yuzu/discord_impl.cpp
index c351e9b83..de0c307d4 100644
--- a/src/yuzu/discord_impl.cpp
+++ b/src/yuzu/discord_impl.cpp
@@ -4,7 +4,10 @@
4#include <chrono> 4#include <chrono>
5#include <string> 5#include <string>
6#include <discord_rpc.h> 6#include <discord_rpc.h>
7#include <fmt/format.h>
8#include <httplib.h>
7#include "common/common_types.h" 9#include "common/common_types.h"
10#include "common/string_util.h"
8#include "core/core.h" 11#include "core/core.h"
9#include "core/loader/loader.h" 12#include "core/loader/loader.h"
10#include "yuzu/discord_impl.h" 13#include "yuzu/discord_impl.h"
@@ -14,7 +17,6 @@ namespace DiscordRPC {
14 17
15DiscordImpl::DiscordImpl(Core::System& system_) : system{system_} { 18DiscordImpl::DiscordImpl(Core::System& system_) : system{system_} {
16 DiscordEventHandlers handlers{}; 19 DiscordEventHandlers handlers{};
17
18 // The number is the client ID for yuzu, it's used for images and the 20 // The number is the client ID for yuzu, it's used for images and the
19 // application name 21 // application name
20 Discord_Initialize("712465656758665259", &handlers, 1, nullptr); 22 Discord_Initialize("712465656758665259", &handlers, 1, nullptr);
@@ -29,23 +31,74 @@ void DiscordImpl::Pause() {
29 Discord_ClearPresence(); 31 Discord_ClearPresence();
30} 32}
31 33
34static std::string GetGameString(const std::string& title) {
35 // Convert to lowercase
36 std::string icon_name = Common::ToLower(title);
37
38 // Replace spaces with dashes
39 std::replace(icon_name.begin(), icon_name.end(), ' ', '-');
40
41 // Remove non-alphanumeric characters but keep dashes
42 std::erase_if(icon_name, [](char c) { return !std::isalnum(c) && c != '-'; });
43
44 // Remove dashes from the start and end of the string
45 icon_name.erase(icon_name.begin(), std::find_if(icon_name.begin(), icon_name.end(),
46 [](int ch) { return ch != '-'; }));
47 icon_name.erase(
48 std::find_if(icon_name.rbegin(), icon_name.rend(), [](int ch) { return ch != '-'; }).base(),
49 icon_name.end());
50
51 // Remove double dashes
52 icon_name.erase(std::unique(icon_name.begin(), icon_name.end(),
53 [](char a, char b) { return a == '-' && b == '-'; }),
54 icon_name.end());
55
56 return icon_name;
57}
58
32void DiscordImpl::Update() { 59void DiscordImpl::Update() {
33 s64 start_time = std::chrono::duration_cast<std::chrono::seconds>( 60 s64 start_time = std::chrono::duration_cast<std::chrono::seconds>(
34 std::chrono::system_clock::now().time_since_epoch()) 61 std::chrono::system_clock::now().time_since_epoch())
35 .count(); 62 .count();
63 const std::string default_text = "yuzu is an emulator for the Nintendo Switch";
64 const std::string default_image = "yuzu_logo";
65 std::string game_cover_url = "https://yuzu-emu.org";
36 std::string title; 66 std::string title;
37 if (system.IsPoweredOn()) { 67
38 system.GetAppLoader().ReadTitle(title);
39 }
40 DiscordRichPresence presence{}; 68 DiscordRichPresence presence{};
41 presence.largeImageKey = "yuzu_logo"; 69
42 presence.largeImageText = "yuzu is an emulator for the Nintendo Switch";
43 if (system.IsPoweredOn()) { 70 if (system.IsPoweredOn()) {
71 system.GetAppLoader().ReadTitle(title);
72
73 // Used to format Icon URL for yuzu website game compatibility page
74 std::string icon_name = GetGameString(title);
75
76 // New Check for game cover
77 httplib::Client cli(game_cover_url);
78
79 if (auto res = cli.Head(fmt::format("/images/game/boxart/{}.png", icon_name).c_str())) {
80 if (res->status == 200) {
81 game_cover_url += fmt::format("/images/game/boxart/{}.png", icon_name);
82 } else {
83 game_cover_url = "yuzu_logo";
84 }
85 } else {
86 game_cover_url = "yuzu_logo";
87 }
88
89 presence.largeImageKey = game_cover_url.c_str();
90 presence.largeImageText = title.c_str();
91
92 presence.smallImageKey = default_image.c_str();
93 presence.smallImageText = default_text.c_str();
44 presence.state = title.c_str(); 94 presence.state = title.c_str();
45 presence.details = "Currently in game"; 95 presence.details = "Currently in game";
46 } else { 96 } else {
47 presence.details = "Not in game"; 97 presence.largeImageKey = default_image.c_str();
98 presence.largeImageText = default_text.c_str();
99 presence.details = "Currently not in game";
48 } 100 }
101
49 presence.startTimestamp = start_time; 102 presence.startTimestamp = start_time;
50 Discord_UpdatePresence(&presence); 103 Discord_UpdatePresence(&presence);
51} 104}
diff --git a/src/yuzu/hotkeys.cpp b/src/yuzu/hotkeys.cpp
index 13723f6e5..6530186c1 100644
--- a/src/yuzu/hotkeys.cpp
+++ b/src/yuzu/hotkeys.cpp
@@ -21,7 +21,7 @@ void HotkeyRegistry::SaveHotkeys() {
21 {hotkey.first, group.first, 21 {hotkey.first, group.first,
22 UISettings::ContextualShortcut({hotkey.second.keyseq.toString(), 22 UISettings::ContextualShortcut({hotkey.second.keyseq.toString(),
23 hotkey.second.controller_keyseq, 23 hotkey.second.controller_keyseq,
24 hotkey.second.context})}); 24 hotkey.second.context, hotkey.second.repeat})});
25 } 25 }
26 } 26 }
27} 27}
@@ -47,6 +47,7 @@ void HotkeyRegistry::LoadHotkeys() {
47 hk.controller_shortcut->disconnect(); 47 hk.controller_shortcut->disconnect();
48 hk.controller_shortcut->SetKey(hk.controller_keyseq); 48 hk.controller_shortcut->SetKey(hk.controller_keyseq);
49 } 49 }
50 hk.repeat = shortcut.shortcut.repeat;
50 } 51 }
51} 52}
52 53
@@ -57,8 +58,7 @@ QShortcut* HotkeyRegistry::GetHotkey(const QString& group, const QString& action
57 hk.shortcut = new QShortcut(hk.keyseq, widget, nullptr, nullptr, hk.context); 58 hk.shortcut = new QShortcut(hk.keyseq, widget, nullptr, nullptr, hk.context);
58 } 59 }
59 60
60 hk.shortcut->setAutoRepeat(false); 61 hk.shortcut->setAutoRepeat(hk.repeat);
61
62 return hk.shortcut; 62 return hk.shortcut;
63} 63}
64 64
diff --git a/src/yuzu/hotkeys.h b/src/yuzu/hotkeys.h
index dc5b7f628..848239c35 100644
--- a/src/yuzu/hotkeys.h
+++ b/src/yuzu/hotkeys.h
@@ -115,6 +115,7 @@ private:
115 QShortcut* shortcut = nullptr; 115 QShortcut* shortcut = nullptr;
116 ControllerShortcut* controller_shortcut = nullptr; 116 ControllerShortcut* controller_shortcut = nullptr;
117 Qt::ShortcutContext context = Qt::WindowShortcut; 117 Qt::ShortcutContext context = Qt::WindowShortcut;
118 bool repeat;
118 }; 119 };
119 120
120 using HotkeyMap = std::map<QString, Hotkey>; 121 using HotkeyMap = std::map<QString, Hotkey>;
diff --git a/src/yuzu/install_dialog.cpp b/src/yuzu/install_dialog.cpp
index 84ec4fe13..673bbaa83 100644
--- a/src/yuzu/install_dialog.cpp
+++ b/src/yuzu/install_dialog.cpp
@@ -46,7 +46,6 @@ InstallDialog::InstallDialog(QWidget* parent, const QStringList& files) : QDialo
46 vbox_layout->addLayout(hbox_layout); 46 vbox_layout->addLayout(hbox_layout);
47 47
48 setLayout(vbox_layout); 48 setLayout(vbox_layout);
49 setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);
50 setWindowTitle(tr("Install Files to NAND")); 49 setWindowTitle(tr("Install Files to NAND"));
51} 50}
52 51
diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp
index c278620ab..f5e6e0f58 100644
--- a/src/yuzu/main.cpp
+++ b/src/yuzu/main.cpp
@@ -1015,11 +1015,6 @@ void GMainWindow::InitializeWidgets() {
1015 filter_status_button->setFocusPolicy(Qt::NoFocus); 1015 filter_status_button->setFocusPolicy(Qt::NoFocus);
1016 connect(filter_status_button, &QPushButton::clicked, this, 1016 connect(filter_status_button, &QPushButton::clicked, this,
1017 &GMainWindow::OnToggleAdaptingFilter); 1017 &GMainWindow::OnToggleAdaptingFilter);
1018 auto filter = Settings::values.scaling_filter.GetValue();
1019 if (Settings::values.renderer_backend.GetValue() == Settings::RendererBackend::OpenGL &&
1020 filter == Settings::ScalingFilter::Fsr) {
1021 Settings::values.scaling_filter.SetValue(Settings::ScalingFilter::NearestNeighbor);
1022 }
1023 UpdateFilterText(); 1018 UpdateFilterText();
1024 filter_status_button->setCheckable(true); 1019 filter_status_button->setCheckable(true);
1025 filter_status_button->setChecked(true); 1020 filter_status_button->setChecked(true);
@@ -2769,8 +2764,7 @@ void GMainWindow::OnMenuInstallToNAND() {
2769 ui->action_Install_File_NAND->setEnabled(false); 2764 ui->action_Install_File_NAND->setEnabled(false);
2770 2765
2771 install_progress = new QProgressDialog(QString{}, tr("Cancel"), 0, total_size, this); 2766 install_progress = new QProgressDialog(QString{}, tr("Cancel"), 0, total_size, this);
2772 install_progress->setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint & 2767 install_progress->setWindowFlags(windowFlags() & ~Qt::WindowMaximizeButtonHint);
2773 ~Qt::WindowMaximizeButtonHint);
2774 install_progress->setAttribute(Qt::WA_DeleteOnClose, true); 2768 install_progress->setAttribute(Qt::WA_DeleteOnClose, true);
2775 install_progress->setFixedWidth(installDialog.GetMinimumWidth() + 40); 2769 install_progress->setFixedWidth(installDialog.GetMinimumWidth() + 40);
2776 install_progress->show(); 2770 install_progress->show();
@@ -3513,10 +3507,6 @@ void GMainWindow::OnToggleAdaptingFilter() {
3513 } else { 3507 } else {
3514 filter = static_cast<Settings::ScalingFilter>(static_cast<u32>(filter) + 1); 3508 filter = static_cast<Settings::ScalingFilter>(static_cast<u32>(filter) + 1);
3515 } 3509 }
3516 if (Settings::values.renderer_backend.GetValue() == Settings::RendererBackend::OpenGL &&
3517 filter == Settings::ScalingFilter::Fsr) {
3518 filter = Settings::ScalingFilter::NearestNeighbor;
3519 }
3520 Settings::values.scaling_filter.SetValue(filter); 3510 Settings::values.scaling_filter.SetValue(filter);
3521 filter_status_button->setChecked(true); 3511 filter_status_button->setChecked(true);
3522 UpdateFilterText(); 3512 UpdateFilterText();
@@ -4513,6 +4503,11 @@ int main(int argc, char* argv[]) {
4513 } 4503 }
4514#endif 4504#endif
4515 4505
4506#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
4507 // Disables the "?" button on all dialogs. Disabled by default on Qt6.
4508 QCoreApplication::setAttribute(Qt::AA_DisableWindowContextHelpButton);
4509#endif
4510
4516 // Enables the core to make the qt created contexts current on std::threads 4511 // Enables the core to make the qt created contexts current on std::threads
4517 QCoreApplication::setAttribute(Qt::AA_DontCheckOpenGLContextThreadAffinity); 4512 QCoreApplication::setAttribute(Qt::AA_DontCheckOpenGLContextThreadAffinity);
4518 QApplication app(argc, argv); 4513 QApplication app(argc, argv);
diff --git a/src/yuzu/multiplayer/direct_connect.cpp b/src/yuzu/multiplayer/direct_connect.cpp
index cbd52da85..d71cc23a7 100644
--- a/src/yuzu/multiplayer/direct_connect.cpp
+++ b/src/yuzu/multiplayer/direct_connect.cpp
@@ -81,20 +81,13 @@ void DirectConnectWindow::Connect() {
81 } 81 }
82 } 82 }
83 } 83 }
84 switch (static_cast<ConnectionType>(ui->connection_type->currentIndex())) { 84 if (!ui->ip->hasAcceptableInput()) {
85 case ConnectionType::TraversalServer: 85 NetworkMessage::ErrorManager::ShowError(NetworkMessage::ErrorManager::IP_ADDRESS_NOT_VALID);
86 break; 86 return;
87 case ConnectionType::IP: 87 }
88 if (!ui->ip->hasAcceptableInput()) { 88 if (!ui->port->hasAcceptableInput()) {
89 NetworkMessage::ErrorManager::ShowError( 89 NetworkMessage::ErrorManager::ShowError(NetworkMessage::ErrorManager::PORT_NOT_VALID);
90 NetworkMessage::ErrorManager::IP_ADDRESS_NOT_VALID); 90 return;
91 return;
92 }
93 if (!ui->port->hasAcceptableInput()) {
94 NetworkMessage::ErrorManager::ShowError(NetworkMessage::ErrorManager::PORT_NOT_VALID);
95 return;
96 }
97 break;
98 } 91 }
99 92
100 // Store settings 93 // Store settings
diff --git a/src/yuzu/multiplayer/direct_connect.ui b/src/yuzu/multiplayer/direct_connect.ui
index 57d6ec25a..0dd4e6829 100644
--- a/src/yuzu/multiplayer/direct_connect.ui
+++ b/src/yuzu/multiplayer/direct_connect.ui
@@ -27,19 +27,10 @@
27 <number>0</number> 27 <number>0</number>
28 </property> 28 </property>
29 <item> 29 <item>
30 <widget class="QComboBox" name="connection_type">
31 <item>
32 <property name="text">
33 <string>IP Address</string>
34 </property>
35 </item>
36 </widget>
37 </item>
38 <item>
39 <widget class="QWidget" name="ip_container" native="true"> 30 <widget class="QWidget" name="ip_container" native="true">
40 <layout class="QHBoxLayout" name="ip_layout"> 31 <layout class="QHBoxLayout" name="ip_layout">
41 <property name="leftMargin"> 32 <property name="leftMargin">
42 <number>5</number> 33 <number>0</number>
43 </property> 34 </property>
44 <property name="topMargin"> 35 <property name="topMargin">
45 <number>0</number> 36 <number>0</number>
@@ -53,17 +44,17 @@
53 <item> 44 <item>
54 <widget class="QLabel" name="label_2"> 45 <widget class="QLabel" name="label_2">
55 <property name="text"> 46 <property name="text">
56 <string>IP</string> 47 <string>Server Address</string>
57 </property> 48 </property>
58 </widget> 49 </widget>
59 </item> 50 </item>
60 <item> 51 <item>
61 <widget class="QLineEdit" name="ip"> 52 <widget class="QLineEdit" name="ip">
62 <property name="toolTip"> 53 <property name="toolTip">
63 <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;IPv4 address of the host&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string> 54 <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Server address of the host&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
64 </property> 55 </property>
65 <property name="maxLength"> 56 <property name="maxLength">
66 <number>16</number> 57 <number>253</number>
67 </property> 58 </property>
68 </widget> 59 </widget>
69 </item> 60 </item>
@@ -85,6 +76,12 @@
85 <property name="placeholderText"> 76 <property name="placeholderText">
86 <string notr="true" extracomment="placeholder string that tells user default port">24872</string> 77 <string notr="true" extracomment="placeholder string that tells user default port">24872</string>
87 </property> 78 </property>
79 <property name="maximumSize">
80 <size>
81 <width>65</width>
82 <height>50</height>
83 </size>
84 </property>
88 </widget> 85 </widget>
89 </item> 86 </item>
90 </layout> 87 </layout>
diff --git a/src/yuzu/multiplayer/lobby.cpp b/src/yuzu/multiplayer/lobby.cpp
index 08c275696..6c93e3511 100644
--- a/src/yuzu/multiplayer/lobby.cpp
+++ b/src/yuzu/multiplayer/lobby.cpp
@@ -77,6 +77,7 @@ Lobby::Lobby(QWidget* parent, QStandardItemModel* list,
77 // UI Buttons 77 // UI Buttons
78 connect(ui->refresh_list, &QPushButton::clicked, this, &Lobby::RefreshLobby); 78 connect(ui->refresh_list, &QPushButton::clicked, this, &Lobby::RefreshLobby);
79 connect(ui->games_owned, &QCheckBox::toggled, proxy, &LobbyFilterProxyModel::SetFilterOwned); 79 connect(ui->games_owned, &QCheckBox::toggled, proxy, &LobbyFilterProxyModel::SetFilterOwned);
80 connect(ui->hide_empty, &QCheckBox::toggled, proxy, &LobbyFilterProxyModel::SetFilterEmpty);
80 connect(ui->hide_full, &QCheckBox::toggled, proxy, &LobbyFilterProxyModel::SetFilterFull); 81 connect(ui->hide_full, &QCheckBox::toggled, proxy, &LobbyFilterProxyModel::SetFilterFull);
81 connect(ui->search, &QLineEdit::textChanged, proxy, &LobbyFilterProxyModel::SetFilterSearch); 82 connect(ui->search, &QLineEdit::textChanged, proxy, &LobbyFilterProxyModel::SetFilterSearch);
82 connect(ui->room_list, &QTreeView::doubleClicked, this, &Lobby::OnJoinRoom); 83 connect(ui->room_list, &QTreeView::doubleClicked, this, &Lobby::OnJoinRoom);
@@ -329,6 +330,16 @@ bool LobbyFilterProxyModel::filterAcceptsRow(int sourceRow, const QModelIndex& s
329 return true; 330 return true;
330 } 331 }
331 332
333 // filter by empty rooms
334 if (filter_empty) {
335 QModelIndex member_list = sourceModel()->index(sourceRow, Column::MEMBER, sourceParent);
336 int player_count =
337 sourceModel()->data(member_list, LobbyItemMemberList::MemberListRole).toList().size();
338 if (player_count == 0) {
339 return false;
340 }
341 }
342
332 // filter by filled rooms 343 // filter by filled rooms
333 if (filter_full) { 344 if (filter_full) {
334 QModelIndex member_list = sourceModel()->index(sourceRow, Column::MEMBER, sourceParent); 345 QModelIndex member_list = sourceModel()->index(sourceRow, Column::MEMBER, sourceParent);
@@ -399,6 +410,11 @@ void LobbyFilterProxyModel::SetFilterOwned(bool filter) {
399 invalidate(); 410 invalidate();
400} 411}
401 412
413void LobbyFilterProxyModel::SetFilterEmpty(bool filter) {
414 filter_empty = filter;
415 invalidate();
416}
417
402void LobbyFilterProxyModel::SetFilterFull(bool filter) { 418void LobbyFilterProxyModel::SetFilterFull(bool filter) {
403 filter_full = filter; 419 filter_full = filter;
404 invalidate(); 420 invalidate();
diff --git a/src/yuzu/multiplayer/lobby.h b/src/yuzu/multiplayer/lobby.h
index 300dad13e..2674ae7c3 100644
--- a/src/yuzu/multiplayer/lobby.h
+++ b/src/yuzu/multiplayer/lobby.h
@@ -130,12 +130,14 @@ public:
130 130
131public slots: 131public slots:
132 void SetFilterOwned(bool); 132 void SetFilterOwned(bool);
133 void SetFilterEmpty(bool);
133 void SetFilterFull(bool); 134 void SetFilterFull(bool);
134 void SetFilterSearch(const QString&); 135 void SetFilterSearch(const QString&);
135 136
136private: 137private:
137 QStandardItemModel* game_list; 138 QStandardItemModel* game_list;
138 bool filter_owned = false; 139 bool filter_owned = false;
140 bool filter_empty = false;
139 bool filter_full = false; 141 bool filter_full = false;
140 QString filter_search; 142 QString filter_search;
141}; 143};
diff --git a/src/yuzu/multiplayer/lobby.ui b/src/yuzu/multiplayer/lobby.ui
index 4c9901c9a..0ef0ef762 100644
--- a/src/yuzu/multiplayer/lobby.ui
+++ b/src/yuzu/multiplayer/lobby.ui
@@ -78,6 +78,13 @@
78 </widget> 78 </widget>
79 </item> 79 </item>
80 <item> 80 <item>
81 <widget class="QCheckBox" name="hide_empty">
82 <property name="text">
83 <string>Hide Empty Rooms</string>
84 </property>
85 </widget>
86 </item>
87 <item>
81 <widget class="QCheckBox" name="hide_full"> 88 <widget class="QCheckBox" name="hide_full">
82 <property name="text"> 89 <property name="text">
83 <string>Hide Full Rooms</string> 90 <string>Hide Full Rooms</string>
diff --git a/src/yuzu/multiplayer/validation.h b/src/yuzu/multiplayer/validation.h
index dd25af280..cbbe6757b 100644
--- a/src/yuzu/multiplayer/validation.h
+++ b/src/yuzu/multiplayer/validation.h
@@ -38,11 +38,28 @@ private:
38 QRegularExpression(QStringLiteral("^[a-zA-Z0-9._ -]{4,20}")); 38 QRegularExpression(QStringLiteral("^[a-zA-Z0-9._ -]{4,20}"));
39 QRegularExpressionValidator nickname; 39 QRegularExpressionValidator nickname;
40 40
41 /// ipv4 address only 41 /// ipv4 / ipv6 / hostnames
42 // TODO remove this when we support hostnames in direct connect
43 QRegularExpression ip_regex = QRegularExpression(QStringLiteral( 42 QRegularExpression ip_regex = QRegularExpression(QStringLiteral(
44 "(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|" 43 // IPv4 regex
45 "2[0-4][0-9]|25[0-5])")); 44 "^((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.){3}(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$|"
45 // IPv6 regex
46 "^((([0-9A-Fa-f]{1,4}:){7}([0-9A-Fa-f]{1,4}|:))|"
47 "(([0-9A-Fa-f]{1,4}:){6}(:[0-9A-Fa-f]{1,4}|((25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)(\\.(25[0-"
48 "5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)){3})|:))|"
49 "(([0-9A-Fa-f]{1,4}:){5}(((:[0-9A-Fa-f]{1,4}){1,2})|:((25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)"
50 "(\\.(25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)){3})|:))|"
51 "(([0-9A-Fa-f]{1,4}:){4}(((:[0-9A-Fa-f]{1,4}){1,3})|((:[0-9A-Fa-f]{1,4})?:((25[0-5]|2[0-4]"
52 "\\d|1\\d\\d|[1-9]?\\d)(\\.(25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)){3}))|:))|"
53 "(([0-9A-Fa-f]{1,4}:){3}(((:[0-9A-Fa-f]{1,4}){1,4})|((:[0-9A-Fa-f]{1,4}){0,2}:((25[0-5]|2["
54 "0-4]\\d|1\\d\\d|[1-9]?\\d)(\\.(25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)){3}))|:))|"
55 "(([0-9A-Fa-f]{1,4}:){2}(((:[0-9A-Fa-f]{1,4}){1,5})|((:[0-9A-Fa-f]{1,4}){0,3}:((25[0-5]|2["
56 "0-4]\\d|1\\d\\d|[1-9]?\\d)(\\.(25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)){3}))|:))|"
57 "(([0-9A-Fa-f]{1,4}:){1}(((:[0-9A-Fa-f]{1,4}){1,6})|((:[0-9A-Fa-f]{1,4}){0,4}:((25[0-5]|2["
58 "0-4]\\d|1\\d\\d|[1-9]?\\d)(\\.(25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)){3}))|:))|"
59 "(:(((:[0-9A-Fa-f]{1,4}){1,7})|((:[0-9A-Fa-f]{1,4}){0,5}:((25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?"
60 "\\d)(\\.(25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)){3}))|:)))(%.+)?$|"
61 // Hostname regex
62 "^([a-zA-Z0-9]+(-[a-zA-Z0-9]+)*\\.)+[a-zA-Z]{2,}$"));
46 QRegularExpressionValidator ip; 63 QRegularExpressionValidator ip;
47 64
48 /// port must be between 0 and 65535 65 /// port must be between 0 and 65535
diff --git a/src/yuzu/uisettings.h b/src/yuzu/uisettings.h
index 2006b883e..db43b7033 100644
--- a/src/yuzu/uisettings.h
+++ b/src/yuzu/uisettings.h
@@ -22,6 +22,7 @@ struct ContextualShortcut {
22 QString keyseq; 22 QString keyseq;
23 QString controller_keyseq; 23 QString controller_keyseq;
24 int context; 24 int context;
25 bool repeat;
25}; 26};
26 27
27struct Shortcut { 28struct Shortcut {
diff --git a/src/yuzu/util/limitable_input_dialog.cpp b/src/yuzu/util/limitable_input_dialog.cpp
index bbb370595..5f6a9c193 100644
--- a/src/yuzu/util/limitable_input_dialog.cpp
+++ b/src/yuzu/util/limitable_input_dialog.cpp
@@ -16,8 +16,6 @@ LimitableInputDialog::LimitableInputDialog(QWidget* parent) : QDialog{parent} {
16LimitableInputDialog::~LimitableInputDialog() = default; 16LimitableInputDialog::~LimitableInputDialog() = default;
17 17
18void LimitableInputDialog::CreateUI() { 18void LimitableInputDialog::CreateUI() {
19 setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);
20
21 text_label = new QLabel(this); 19 text_label = new QLabel(this);
22 text_entry = new QLineEdit(this); 20 text_entry = new QLineEdit(this);
23 text_label_invalid = new QLabel(this); 21 text_label_invalid = new QLabel(this);
diff --git a/src/yuzu/util/sequence_dialog/sequence_dialog.cpp b/src/yuzu/util/sequence_dialog/sequence_dialog.cpp
index 4b10fa517..1670aa596 100644
--- a/src/yuzu/util/sequence_dialog/sequence_dialog.cpp
+++ b/src/yuzu/util/sequence_dialog/sequence_dialog.cpp
@@ -8,7 +8,6 @@
8 8
9SequenceDialog::SequenceDialog(QWidget* parent) : QDialog(parent) { 9SequenceDialog::SequenceDialog(QWidget* parent) : QDialog(parent) {
10 setWindowTitle(tr("Enter a hotkey")); 10 setWindowTitle(tr("Enter a hotkey"));
11 setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);
12 11
13 key_sequence = new QKeySequenceEdit; 12 key_sequence = new QKeySequenceEdit;
14 13
diff --git a/src/yuzu_cmd/config.cpp b/src/yuzu_cmd/config.cpp
index 527017282..9c34cdc6e 100644
--- a/src/yuzu_cmd/config.cpp
+++ b/src/yuzu_cmd/config.cpp
@@ -176,6 +176,9 @@ void Config::ReadValues() {
176 Settings::values.debug_pad_analogs[i] = default_param; 176 Settings::values.debug_pad_analogs[i] = default_param;
177 } 177 }
178 178
179 ReadSetting("ControlsGeneral", Settings::values.enable_raw_input);
180 ReadSetting("ControlsGeneral", Settings::values.enable_joycon_driver);
181 ReadSetting("ControlsGeneral", Settings::values.emulate_analog_keyboard);
179 ReadSetting("ControlsGeneral", Settings::values.vibration_enabled); 182 ReadSetting("ControlsGeneral", Settings::values.vibration_enabled);
180 ReadSetting("ControlsGeneral", Settings::values.enable_accurate_vibrations); 183 ReadSetting("ControlsGeneral", Settings::values.enable_accurate_vibrations);
181 ReadSetting("ControlsGeneral", Settings::values.motion_enabled); 184 ReadSetting("ControlsGeneral", Settings::values.motion_enabled);
diff --git a/src/yuzu_cmd/default_ini.h b/src/yuzu_cmd/default_ini.h
index 6fcf04e1b..cf3cc4c4e 100644
--- a/src/yuzu_cmd/default_ini.h
+++ b/src/yuzu_cmd/default_ini.h
@@ -5,8 +5,8 @@
5 5
6namespace DefaultINI { 6namespace DefaultINI {
7 7
8const char* sdl2_config_file = R"( 8const char* sdl2_config_file =
9 9 R"(
10[ControlsP0] 10[ControlsP0]
11# The input devices and parameters for each Switch native input 11# The input devices and parameters for each Switch native input
12# The config section determines the player number where the config will be applied on. For example "ControlsP0", "ControlsP1", ... 12# The config section determines the player number where the config will be applied on. For example "ControlsP0", "ControlsP1", ...
@@ -14,6 +14,7 @@ const char* sdl2_config_file = R"(
14# Escape characters $0 (for ':'), $1 (for ',') and $2 (for '$') can be used in values 14# Escape characters $0 (for ':'), $1 (for ',') and $2 (for '$') can be used in values
15 15
16# Indicates if this player should be connected at boot 16# Indicates if this player should be connected at boot
17# 0 (default): Disabled, 1: Enabled
17connected= 18connected=
18 19
19# for button input, the following devices are available: 20# for button input, the following devices are available:
@@ -94,6 +95,18 @@ motionright=
94# 0 (default): Disabled, 1: Enabled 95# 0 (default): Disabled, 1: Enabled
95debug_pad_enabled = 96debug_pad_enabled =
96 97
98# Enable sdl raw input. Allows to configure up to 8 xinput controllers.
99# 0 (default): Disabled, 1: Enabled
100enable_raw_input =
101
102# Enable yuzu joycon driver instead of SDL drive.
103# 0: Disabled, 1 (default): Enabled
104enable_joycon_driver =
105
106# Emulates an analog input from buttons. Allowing to dial any angle.
107# 0 (default): Disabled, 1: Enabled
108emulate_analog_keyboard =
109
97# Whether to enable or disable vibration 110# Whether to enable or disable vibration
98# 0: Disabled, 1 (default): Enabled 111# 0: Disabled, 1 (default): Enabled
99vibration_enabled= 112vibration_enabled=
@@ -143,6 +156,8 @@ mouse_enabled =
143# 0 (default): Disabled, 1: Enabled 156# 0 (default): Disabled, 1: Enabled
144keyboard_enabled = 157keyboard_enabled =
145 158
159)"
160 R"(
146[Core] 161[Core]
147# Whether to use multi-core for CPU emulation 162# Whether to use multi-core for CPU emulation
148# 0: Disabled, 1 (default): Enabled 163# 0: Disabled, 1 (default): Enabled
@@ -242,6 +257,8 @@ cpuopt_unsafe_fastmem_check =
242# 0: Disabled, 1 (default): Enabled 257# 0: Disabled, 1 (default): Enabled
243cpuopt_unsafe_ignore_global_monitor = 258cpuopt_unsafe_ignore_global_monitor =
244 259
260)"
261 R"(
245[Renderer] 262[Renderer]
246# Which backend API to use. 263# Which backend API to use.
247# 0: OpenGL, 1 (default): Vulkan 264# 0: OpenGL, 1 (default): Vulkan
@@ -269,11 +286,14 @@ vulkan_device =
269# 0: 0.5x (360p/540p) [EXPERIMENTAL] 286# 0: 0.5x (360p/540p) [EXPERIMENTAL]
270# 1: 0.75x (540p/810p) [EXPERIMENTAL] 287# 1: 0.75x (540p/810p) [EXPERIMENTAL]
271# 2 (default): 1x (720p/1080p) 288# 2 (default): 1x (720p/1080p)
272# 3: 2x (1440p/2160p) 289# 3: 1.5x (1080p/1620p) [EXPERIMENTAL]
273# 4: 3x (2160p/3240p) 290# 4: 2x (1440p/2160p)
274# 5: 4x (2880p/4320p) 291# 5: 3x (2160p/3240p)
275# 6: 5x (3600p/5400p) 292# 6: 4x (2880p/4320p)
276# 7: 6x (4320p/6480p) 293# 7: 5x (3600p/5400p)
294# 8: 6x (4320p/6480p)
295# 9: 7x (5040p/7560p)
296# 10: 8x (5760/8640p)
277resolution_setup = 297resolution_setup =
278 298
279# Pixel filter to use when up- or down-sampling rendered frames. 299# Pixel filter to use when up- or down-sampling rendered frames.
@@ -282,11 +302,11 @@ resolution_setup =
282# 2: Bicubic 302# 2: Bicubic
283# 3: Gaussian 303# 3: Gaussian
284# 4: ScaleForce 304# 4: ScaleForce
285# 5: AMD FidelityFX™️ Super Resolution [Vulkan Only] 305# 5: AMD FidelityFX™️ Super Resolution
286scaling_filter = 306scaling_filter =
287 307
288# Anti-Aliasing (AA) 308# Anti-Aliasing (AA)
289# 0 (default): None, 1: FXAA 309# 0 (default): None, 1: FXAA, 2: SMAA
290anti_aliasing = 310anti_aliasing =
291 311
292# Whether to use fullscreen or borderless window mode 312# Whether to use fullscreen or borderless window mode
@@ -360,6 +380,8 @@ bg_red =
360bg_blue = 380bg_blue =
361bg_green = 381bg_green =
362 382
383)"
384 R"(
363[Audio] 385[Audio]
364# Which audio output engine to use. 386# Which audio output engine to use.
365# auto (default): Auto-select 387# auto (default): Auto-select
diff --git a/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp b/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp
index 31f28a507..5450b8c38 100644
--- a/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp
+++ b/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp
@@ -18,11 +18,11 @@
18 18
19EmuWindow_SDL2::EmuWindow_SDL2(InputCommon::InputSubsystem* input_subsystem_, Core::System& system_) 19EmuWindow_SDL2::EmuWindow_SDL2(InputCommon::InputSubsystem* input_subsystem_, Core::System& system_)
20 : input_subsystem{input_subsystem_}, system{system_} { 20 : input_subsystem{input_subsystem_}, system{system_} {
21 if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_JOYSTICK) < 0) { 21 input_subsystem->Initialize();
22 if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_JOYSTICK | SDL_INIT_GAMECONTROLLER) < 0) {
22 LOG_CRITICAL(Frontend, "Failed to initialize SDL2! Exiting..."); 23 LOG_CRITICAL(Frontend, "Failed to initialize SDL2! Exiting...");
23 exit(1); 24 exit(1);
24 } 25 }
25 input_subsystem->Initialize();
26 SDL_SetMainReady(); 26 SDL_SetMainReady();
27} 27}
28 28
@@ -32,10 +32,6 @@ EmuWindow_SDL2::~EmuWindow_SDL2() {
32 SDL_Quit(); 32 SDL_Quit();
33} 33}
34 34
35void EmuWindow_SDL2::OnMouseMotion(s32 x, s32 y) {
36 input_subsystem->GetMouse()->MouseMove(x, y, 0, 0, 0, 0);
37}
38
39InputCommon::MouseButton EmuWindow_SDL2::SDLButtonToMouseButton(u32 button) const { 35InputCommon::MouseButton EmuWindow_SDL2::SDLButtonToMouseButton(u32 button) const {
40 switch (button) { 36 switch (button) {
41 case SDL_BUTTON_LEFT: 37 case SDL_BUTTON_LEFT:
@@ -53,44 +49,36 @@ InputCommon::MouseButton EmuWindow_SDL2::SDLButtonToMouseButton(u32 button) cons
53 } 49 }
54} 50}
55 51
52std::pair<float, float> EmuWindow_SDL2::MouseToTouchPos(s32 touch_x, s32 touch_y) const {
53 int w, h;
54 SDL_GetWindowSize(render_window, &w, &h);
55 const float fx = static_cast<float>(touch_x) / w;
56 const float fy = static_cast<float>(touch_y) / h;
57
58 return {std::clamp<float>(fx, 0.0f, 1.0f), std::clamp<float>(fy, 0.0f, 1.0f)};
59}
60
56void EmuWindow_SDL2::OnMouseButton(u32 button, u8 state, s32 x, s32 y) { 61void EmuWindow_SDL2::OnMouseButton(u32 button, u8 state, s32 x, s32 y) {
57 const auto mouse_button = SDLButtonToMouseButton(button); 62 const auto mouse_button = SDLButtonToMouseButton(button);
58 if (state == SDL_PRESSED) { 63 if (state == SDL_PRESSED) {
59 input_subsystem->GetMouse()->PressButton(x, y, 0, 0, mouse_button); 64 const auto [touch_x, touch_y] = MouseToTouchPos(x, y);
65 input_subsystem->GetMouse()->PressButton(x, y, touch_x, touch_y, mouse_button);
60 } else { 66 } else {
61 input_subsystem->GetMouse()->ReleaseButton(mouse_button); 67 input_subsystem->GetMouse()->ReleaseButton(mouse_button);
62 } 68 }
63} 69}
64 70
65std::pair<unsigned, unsigned> EmuWindow_SDL2::TouchToPixelPos(float touch_x, float touch_y) const { 71void EmuWindow_SDL2::OnMouseMotion(s32 x, s32 y) {
66 int w, h; 72 const auto [touch_x, touch_y] = MouseToTouchPos(x, y);
67 SDL_GetWindowSize(render_window, &w, &h); 73 input_subsystem->GetMouse()->MouseMove(x, y, touch_x, touch_y, 0, 0);
68
69 touch_x *= w;
70 touch_y *= h;
71
72 return {static_cast<unsigned>(std::max(std::round(touch_x), 0.0f)),
73 static_cast<unsigned>(std::max(std::round(touch_y), 0.0f))};
74} 74}
75 75
76void EmuWindow_SDL2::OnFingerDown(float x, float y, std::size_t id) { 76void EmuWindow_SDL2::OnFingerDown(float x, float y, std::size_t id) {
77 int width, height; 77 input_subsystem->GetTouchScreen()->TouchPressed(x, y, id);
78 SDL_GetWindowSize(render_window, &width, &height);
79 const auto [px, py] = TouchToPixelPos(x, y);
80 const float fx = px * 1.0f / width;
81 const float fy = py * 1.0f / height;
82
83 input_subsystem->GetTouchScreen()->TouchPressed(fx, fy, id);
84} 78}
85 79
86void EmuWindow_SDL2::OnFingerMotion(float x, float y, std::size_t id) { 80void EmuWindow_SDL2::OnFingerMotion(float x, float y, std::size_t id) {
87 int width, height; 81 input_subsystem->GetTouchScreen()->TouchMoved(x, y, id);
88 SDL_GetWindowSize(render_window, &width, &height);
89 const auto [px, py] = TouchToPixelPos(x, y);
90 const float fx = px * 1.0f / width;
91 const float fy = py * 1.0f / height;
92
93 input_subsystem->GetTouchScreen()->TouchMoved(fx, fy, id);
94} 82}
95 83
96void EmuWindow_SDL2::OnFingerUp() { 84void EmuWindow_SDL2::OnFingerUp() {
diff --git a/src/yuzu_cmd/emu_window/emu_window_sdl2.h b/src/yuzu_cmd/emu_window/emu_window_sdl2.h
index 25c23e2a5..d9b453dee 100644
--- a/src/yuzu_cmd/emu_window/emu_window_sdl2.h
+++ b/src/yuzu_cmd/emu_window/emu_window_sdl2.h
@@ -38,17 +38,17 @@ protected:
38 /// Called by WaitEvent when a key is pressed or released. 38 /// Called by WaitEvent when a key is pressed or released.
39 void OnKeyEvent(int key, u8 state); 39 void OnKeyEvent(int key, u8 state);
40 40
41 /// Called by WaitEvent when the mouse moves.
42 void OnMouseMotion(s32 x, s32 y);
43
44 /// Converts a SDL mouse button into MouseInput mouse button 41 /// Converts a SDL mouse button into MouseInput mouse button
45 InputCommon::MouseButton SDLButtonToMouseButton(u32 button) const; 42 InputCommon::MouseButton SDLButtonToMouseButton(u32 button) const;
46 43
44 /// Translates pixel position to float position
45 std::pair<float, float> MouseToTouchPos(s32 touch_x, s32 touch_y) const;
46
47 /// Called by WaitEvent when a mouse button is pressed or released 47 /// Called by WaitEvent when a mouse button is pressed or released
48 void OnMouseButton(u32 button, u8 state, s32 x, s32 y); 48 void OnMouseButton(u32 button, u8 state, s32 x, s32 y);
49 49
50 /// Translates pixel position (0..1) to pixel positions 50 /// Called by WaitEvent when the mouse moves.
51 std::pair<unsigned, unsigned> TouchToPixelPos(float touch_x, float touch_y) const; 51 void OnMouseMotion(s32 x, s32 y);
52 52
53 /// Called by WaitEvent when a finger starts touching the touchscreen 53 /// Called by WaitEvent when a finger starts touching the touchscreen
54 void OnFingerDown(float x, float y, std::size_t id); 54 void OnFingerDown(float x, float y, std::size_t id);
diff --git a/src/yuzu_cmd/yuzu.cpp b/src/yuzu_cmd/yuzu.cpp
index 91133569d..d1f7b1d49 100644
--- a/src/yuzu_cmd/yuzu.cpp
+++ b/src/yuzu_cmd/yuzu.cpp
@@ -62,13 +62,15 @@ __declspec(dllexport) int AmdPowerXpressRequestHighPerformance = 1;
62static void PrintHelp(const char* argv0) { 62static void PrintHelp(const char* argv0) {
63 std::cout << "Usage: " << argv0 63 std::cout << "Usage: " << argv0
64 << " [options] <filename>\n" 64 << " [options] <filename>\n"
65 "-m, --multiplayer=nick:password@address:port" 65 "-c, --config Load the specified configuration file\n"
66 " Nickname, password, address and port for multiplayer\n"
67 "-f, --fullscreen Start in fullscreen mode\n" 66 "-f, --fullscreen Start in fullscreen mode\n"
67 "-g, --game File path of the game to load\n"
68 "-h, --help Display this help and exit\n" 68 "-h, --help Display this help and exit\n"
69 "-v, --version Output version information and exit\n" 69 "-m, --multiplayer=nick:password@address:port"
70 " Nickname, password, address and port for multiplayer\n"
70 "-p, --program Pass following string as arguments to executable\n" 71 "-p, --program Pass following string as arguments to executable\n"
71 "-c, --config Load the specified configuration file\n"; 72 "-u, --user Select a specific user profile from 0 to 7\n"
73 "-v, --version Output version information and exit\n";
72} 74}
73 75
74static void PrintVersion() { 76static void PrintVersion() {
@@ -199,6 +201,7 @@ int main(int argc, char** argv) {
199 std::string filepath; 201 std::string filepath;
200 std::optional<std::string> config_path; 202 std::optional<std::string> config_path;
201 std::string program_args; 203 std::string program_args;
204 std::optional<int> selected_user;
202 205
203 bool use_multiplayer = false; 206 bool use_multiplayer = false;
204 bool fullscreen = false; 207 bool fullscreen = false;
@@ -209,12 +212,14 @@ int main(int argc, char** argv) {
209 212
210 static struct option long_options[] = { 213 static struct option long_options[] = {
211 // clang-format off 214 // clang-format off
212 {"multiplayer", required_argument, 0, 'm'}, 215 {"config", required_argument, 0, 'c'},
213 {"fullscreen", no_argument, 0, 'f'}, 216 {"fullscreen", no_argument, 0, 'f'},
214 {"help", no_argument, 0, 'h'}, 217 {"help", no_argument, 0, 'h'},
215 {"version", no_argument, 0, 'v'}, 218 {"game", required_argument, 0, 'g'},
219 {"multiplayer", required_argument, 0, 'm'},
216 {"program", optional_argument, 0, 'p'}, 220 {"program", optional_argument, 0, 'p'},
217 {"config", required_argument, 0, 'c'}, 221 {"user", required_argument, 0, 'u'},
222 {"version", no_argument, 0, 'v'},
218 {0, 0, 0, 0}, 223 {0, 0, 0, 0},
219 // clang-format on 224 // clang-format on
220 }; 225 };
@@ -223,6 +228,21 @@ int main(int argc, char** argv) {
223 int arg = getopt_long(argc, argv, "g:fhvp::c:", long_options, &option_index); 228 int arg = getopt_long(argc, argv, "g:fhvp::c:", long_options, &option_index);
224 if (arg != -1) { 229 if (arg != -1) {
225 switch (static_cast<char>(arg)) { 230 switch (static_cast<char>(arg)) {
231 case 'c':
232 config_path = optarg;
233 break;
234 case 'f':
235 fullscreen = true;
236 LOG_INFO(Frontend, "Starting in fullscreen mode...");
237 break;
238 case 'h':
239 PrintHelp(argv[0]);
240 return 0;
241 case 'g': {
242 const std::string str_arg(optarg);
243 filepath = str_arg;
244 break;
245 }
226 case 'm': { 246 case 'm': {
227 use_multiplayer = true; 247 use_multiplayer = true;
228 const std::string str_arg(optarg); 248 const std::string str_arg(optarg);
@@ -255,23 +275,16 @@ int main(int argc, char** argv) {
255 } 275 }
256 break; 276 break;
257 } 277 }
258 case 'f': 278 case 'p':
259 fullscreen = true; 279 program_args = argv[optind];
260 LOG_INFO(Frontend, "Starting in fullscreen mode..."); 280 ++optind;
261 break; 281 break;
262 case 'h': 282 case 'u':
263 PrintHelp(argv[0]); 283 selected_user = atoi(optarg);
264 return 0; 284 return 0;
265 case 'v': 285 case 'v':
266 PrintVersion(); 286 PrintVersion();
267 return 0; 287 return 0;
268 case 'p':
269 program_args = argv[optind];
270 ++optind;
271 break;
272 case 'c':
273 config_path = optarg;
274 break;
275 } 288 }
276 } else { 289 } else {
277#ifdef _WIN32 290#ifdef _WIN32
@@ -295,6 +308,10 @@ int main(int argc, char** argv) {
295 Settings::values.program_args = program_args; 308 Settings::values.program_args = program_args;
296 } 309 }
297 310
311 if (selected_user.has_value()) {
312 Settings::values.current_user = std::clamp(*selected_user, 0, 7);
313 }
314
298#ifdef _WIN32 315#ifdef _WIN32
299 LocalFree(argv_w); 316 LocalFree(argv_w);
300#endif 317#endif