summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/audio_core/renderer/adsp/audio_renderer.cpp2
-rw-r--r--src/audio_core/renderer/command/command_buffer.cpp4
-rw-r--r--src/audio_core/renderer/command/command_generator.cpp2
-rw-r--r--src/audio_core/renderer/command/effect/aux_.cpp130
-rw-r--r--src/audio_core/renderer/command/effect/biquad_filter.cpp38
-rw-r--r--src/audio_core/renderer/command/effect/i3dl2_reverb.cpp8
-rw-r--r--src/audio_core/renderer/command/effect/reverb.cpp8
-rw-r--r--src/audio_core/renderer/command/resample/upsample.cpp99
-rw-r--r--src/audio_core/renderer/system.cpp2
-rw-r--r--src/audio_core/renderer/system_manager.cpp2
-rw-r--r--src/audio_core/renderer/voice/voice_state.h8
-rw-r--r--src/audio_core/sink/cubeb_sink.cpp20
-rw-r--r--src/audio_core/sink/sink_stream.cpp8
-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.h73
-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.cpp16
-rw-r--r--src/common/settings.h29
-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.txt61
-rw-r--r--src/core/arm/arm_interface.cpp28
-rw-r--r--src/core/arm/arm_interface.h1
-rw-r--r--src/core/core.cpp42
-rw-r--r--src/core/core.h18
-rw-r--r--src/core/core_timing.cpp44
-rw-r--r--src/core/core_timing.h9
-rw-r--r--src/core/debugger/gdbstub.cpp54
-rw-r--r--src/core/debugger/gdbstub_arch.cpp14
-rw-r--r--src/core/debugger/gdbstub_arch.h6
-rw-r--r--src/core/file_sys/ips_layer.cpp8
-rw-r--r--src/core/file_sys/registered_cache.cpp2
-rw-r--r--src/core/file_sys/savedata_factory.cpp2
-rw-r--r--src/core/hardware_properties.h20
-rw-r--r--src/core/hid/emulated_console.cpp3
-rw-r--r--src/core/hid/emulated_controller.cpp405
-rw-r--r--src/core/hid/emulated_controller.h75
-rw-r--r--src/core/hid/emulated_devices.cpp49
-rw-r--r--src/core/hid/emulated_devices.h18
-rw-r--r--src/core/hid/hid_types.h7
-rw-r--r--src/core/hid/input_converter.cpp12
-rw-r--r--src/core/hid/input_converter.h10
-rw-r--r--src/core/hid/motion_input.cpp12
-rw-r--r--src/core/hid/motion_input.h15
-rw-r--r--src/core/hle/ipc_helpers.h2
-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.cpp4
-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_client_port.cpp3
-rw-r--r--src/core/hle/kernel/k_code_memory.cpp37
-rw-r--r--src/core/hle/kernel/k_code_memory.h6
-rw-r--r--src/core/hle/kernel/k_condition_variable.cpp10
-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_handle_table.h3
-rw-r--r--src/core/hle/kernel/k_hardware_timer.cpp6
-rw-r--r--src/core/hle/kernel/k_interrupt_manager.cpp2
-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_memory_manager.cpp8
-rw-r--r--src/core/hle/kernel/k_object_name.cpp102
-rw-r--r--src/core/hle/kernel/k_object_name.h86
-rw-r--r--src/core/hle/kernel/k_page_group.cpp121
-rw-r--r--src/core/hle/kernel/k_page_group.h163
-rw-r--r--src/core/hle/kernel/k_page_table.cpp640
-rw-r--r--src/core/hle/kernel/k_page_table.h93
-rw-r--r--src/core/hle/kernel/k_priority_queue.h54
-rw-r--r--src/core/hle/kernel/k_process.cpp42
-rw-r--r--src/core/hle/kernel/k_scheduler.cpp14
-rw-r--r--src/core/hle/kernel/k_scoped_lock.h11
-rw-r--r--src/core/hle/kernel/k_session.cpp3
-rw-r--r--src/core/hle/kernel/k_shared_memory.cpp25
-rw-r--r--src/core/hle/kernel/k_thread.cpp29
-rw-r--r--src/core/hle/kernel/k_thread.h34
-rw-r--r--src/core/hle/kernel/k_thread_local_page.h6
-rw-r--r--src/core/hle/kernel/k_transfer_memory.cpp2
-rw-r--r--src/core/hle/kernel/kernel.cpp99
-rw-r--r--src/core/hle/kernel/kernel.h42
-rw-r--r--src/core/hle/kernel/memory_types.h3
-rw-r--r--src/core/hle/kernel/physical_core.h1
-rw-r--r--src/core/hle/kernel/svc.cpp6406
-rw-r--r--src/core/hle/kernel/svc.h528
-rw-r--r--src/core/hle/kernel/svc/svc_activity.cpp66
-rw-r--r--src/core/hle/kernel/svc/svc_address_arbiter.cpp122
-rw-r--r--src/core/hle/kernel/svc/svc_address_translation.cpp50
-rw-r--r--src/core/hle/kernel/svc/svc_cache.cpp98
-rw-r--r--src/core/hle/kernel/svc/svc_code_memory.cpp171
-rw-r--r--src/core/hle/kernel/svc/svc_condition_variable.cpp77
-rw-r--r--src/core/hle/kernel/svc/svc_debug.cpp194
-rw-r--r--src/core/hle/kernel/svc/svc_debug_string.cpp29
-rw-r--r--src/core/hle/kernel/svc/svc_device_address_space.cpp258
-rw-r--r--src/core/hle/kernel/svc/svc_event.cpp124
-rw-r--r--src/core/hle/kernel/svc/svc_exception.cpp137
-rw-r--r--src/core/hle/kernel/svc/svc_info.cpp297
-rw-r--r--src/core/hle/kernel/svc/svc_insecure_memory.cpp35
-rw-r--r--src/core/hle/kernel/svc/svc_interrupt_event.cpp25
-rw-r--r--src/core/hle/kernel/svc/svc_io_pool.cpp71
-rw-r--r--src/core/hle/kernel/svc/svc_ipc.cpp172
-rw-r--r--src/core/hle/kernel/svc/svc_kernel_debug.cpp35
-rw-r--r--src/core/hle/kernel/svc/svc_light_ipc.cpp73
-rw-r--r--src/core/hle/kernel/svc/svc_lock.cpp66
-rw-r--r--src/core/hle/kernel/svc/svc_memory.cpp217
-rw-r--r--src/core/hle/kernel/svc/svc_physical_memory.cpp185
-rw-r--r--src/core/hle/kernel/svc/svc_port.cpp170
-rw-r--r--src/core/hle/kernel/svc/svc_power_management.cpp21
-rw-r--r--src/core/hle/kernel/svc/svc_process.cpp194
-rw-r--r--src/core/hle/kernel/svc/svc_process_memory.cpp324
-rw-r--r--src/core/hle/kernel/svc/svc_processor.cpp25
-rw-r--r--src/core/hle/kernel/svc/svc_query_memory.cpp65
-rw-r--r--src/core/hle/kernel/svc/svc_register.cpp27
-rw-r--r--src/core/hle/kernel/svc/svc_resource_limit.cpp149
-rw-r--r--src/core/hle/kernel/svc/svc_secure_monitor_call.cpp53
-rw-r--r--src/core/hle/kernel/svc/svc_session.cpp128
-rw-r--r--src/core/hle/kernel/svc/svc_shared_memory.cpp133
-rw-r--r--src/core/hle/kernel/svc/svc_synchronization.cpp163
-rw-r--r--src/core/hle/kernel/svc/svc_thread.cpp437
-rw-r--r--src/core/hle/kernel/svc/svc_thread_profiler.cpp60
-rw-r--r--src/core/hle/kernel/svc/svc_tick.cpp35
-rw-r--r--src/core/hle/kernel/svc/svc_transfer_memory.cpp117
-rw-r--r--src/core/hle/kernel/svc_generator.py716
-rw-r--r--src/core/hle/kernel/svc_results.h1
-rw-r--r--src/core/hle/kernel/svc_types.h30
-rw-r--r--src/core/hle/kernel/svc_version.h58
-rw-r--r--src/core/hle/kernel/svc_wrap.h733
-rw-r--r--src/core/hle/service/acc/acc.cpp4
-rw-r--r--src/core/hle/service/am/am.cpp28
-rw-r--r--src/core/hle/service/am/applets/applet_error.cpp2
-rw-r--r--src/core/hle/service/am/applets/applet_general_backend.cpp2
-rw-r--r--src/core/hle/service/am/applets/applet_web_browser.cpp2
-rw-r--r--src/core/hle/service/am/tcap.cpp22
-rw-r--r--src/core/hle/service/am/tcap.h20
-rw-r--r--src/core/hle/service/aoc/aoc_u.cpp6
-rw-r--r--src/core/hle/service/apm/apm.cpp2
-rw-r--r--src/core/hle/service/apm/apm_controller.cpp2
-rw-r--r--src/core/hle/service/audio/auddbg.cpp21
-rw-r--r--src/core/hle/service/audio/auddbg.h20
-rw-r--r--src/core/hle/service/audio/audin_a.cpp23
-rw-r--r--src/core/hle/service/audio/audin_a.h20
-rw-r--r--src/core/hle/service/audio/audio.cpp14
-rw-r--r--src/core/hle/service/audio/audout_a.cpp25
-rw-r--r--src/core/hle/service/audio/audout_a.h20
-rw-r--r--src/core/hle/service/audio/audren_a.cpp27
-rw-r--r--src/core/hle/service/audio/audren_a.h20
-rw-r--r--src/core/hle/service/audio/audren_u.cpp4
-rw-r--r--src/core/hle/service/audio/codecctl.cpp29
-rw-r--r--src/core/hle/service/audio/codecctl.h20
-rw-r--r--src/core/hle/service/audio/hwopus.cpp2
-rw-r--r--src/core/hle/service/bcat/bcat_module.cpp10
-rw-r--r--src/core/hle/service/es/es.cpp2
-rw-r--r--src/core/hle/service/fatal/fatal.cpp2
-rw-r--r--src/core/hle/service/filesystem/filesystem.cpp4
-rw-r--r--src/core/hle/service/filesystem/fsp_srv.cpp16
-rw-r--r--src/core/hle/service/glue/arp.cpp3
-rw-r--r--src/core/hle/service/hid/controllers/npad.cpp43
-rw-r--r--src/core/hle/service/hid/controllers/npad.h17
-rw-r--r--src/core/hle/service/hid/errors.h1
-rw-r--r--src/core/hle/service/hid/hid.cpp36
-rw-r--r--src/core/hle/service/hid/hidbus.cpp28
-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.cpp26
-rw-r--r--src/core/hle/service/jit/jit.cpp8
-rw-r--r--src/core/hle/service/ldn/ldn.cpp4
-rw-r--r--src/core/hle/service/ldr/ldr.cpp13
-rw-r--r--src/core/hle/service/nfc/nfc_device.cpp7
-rw-r--r--src/core/hle/service/nfp/nfp_device.cpp9
-rw-r--r--src/core/hle/service/nvdrv/core/syncpoint_manager.h2
-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.cpp25
-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.cpp24
-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/nvflinger.cpp8
-rw-r--r--src/core/hle/service/nvflinger/nvflinger.h2
-rw-r--r--src/core/hle/service/nvflinger/parcel.h87
-rw-r--r--src/core/hle/service/pctl/pctl_module.cpp2
-rw-r--r--src/core/hle/service/pcv/pcv.cpp28
-rw-r--r--src/core/hle/service/prepo/prepo.cpp12
-rw-r--r--src/core/hle/service/service.cpp2
-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/ethc.cpp42
-rw-r--r--src/core/hle/service/sockets/ethc.h26
-rw-r--r--src/core/hle/service/sockets/sfdnsres.cpp2
-rw-r--r--src/core/hle/service/sockets/sockets.cpp4
-rw-r--r--src/core/hle/service/ssl/ssl.cpp8
-rw-r--r--src/core/hle/service/time/time_zone_manager.cpp2
-rw-r--r--src/core/hle/service/vi/vi.cpp4
-rw-r--r--src/core/hle/service/wlan/wlan.cpp186
-rw-r--r--src/core/hle/service/wlan/wlan.h18
-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/loader/nso.cpp2
-rw-r--r--src/core/memory.cpp16
-rw-r--r--src/core/memory/cheat_engine.cpp6
-rw-r--r--src/core/reporter.cpp12
-rw-r--r--src/core/reporter.h4
-rw-r--r--src/core/telemetry_session.cpp2
-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.cpp724
-rw-r--r--src/input_common/drivers/joycon.h112
-rw-r--r--src/input_common/drivers/mouse.cpp67
-rw-r--r--src/input_common/drivers/mouse.h38
-rw-r--r--src/input_common/drivers/sdl_driver.cpp95
-rw-r--r--src/input_common/drivers/sdl_driver.h2
-rw-r--r--src/input_common/drivers/tas_input.cpp12
-rw-r--r--src/input_common/drivers/tas_input.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.cpp576
-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.cpp67
-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_mapping.cpp6
-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.cpp40
-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.cpp55
-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/frontend/maxwell/translate_program.cpp2
-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.cpp4
-rw-r--r--src/video_core/CMakeLists.txt6
-rw-r--r--src/video_core/buffer_cache/buffer_base.h14
-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.cpp9
-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/gpu.cpp2
-rw-r--r--src/video_core/gpu_thread.cpp6
-rw-r--r--src/video_core/gpu_thread.h8
-rw-r--r--src/video_core/host1x/codecs/codec.cpp2
-rw-r--r--src/video_core/host1x/vic.cpp14
-rw-r--r--src/video_core/host_shaders/CMakeLists.txt36
-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/convert_msaa_to_non_msaa.comp30
-rw-r--r--src/video_core/host_shaders/convert_non_msaa_to_msaa.comp29
-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.h5
-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.cpp48
-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/gl_texture_cache.cpp8
-rw-r--r--src/video_core/renderer_opengl/gl_texture_cache.h9
-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_opengl/util_shaders.cpp33
-rw-r--r--src/video_core/renderer_opengl/util_shaders.h5
-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/renderer_vulkan.cpp21
-rw-r--r--src/video_core/renderer_vulkan/vk_fsr.cpp144
-rw-r--r--src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp26
-rw-r--r--src/video_core/renderer_vulkan/vk_rasterizer.cpp41
-rw-r--r--src/video_core/renderer_vulkan/vk_rasterizer.h1
-rw-r--r--src/video_core/renderer_vulkan/vk_scheduler.cpp5
-rw-r--r--src/video_core/renderer_vulkan/vk_scheduler.h7
-rw-r--r--src/video_core/renderer_vulkan/vk_smaa.cpp14
-rw-r--r--src/video_core/renderer_vulkan/vk_texture_cache.cpp5
-rw-r--r--src/video_core/renderer_vulkan/vk_texture_cache.h9
-rw-r--r--src/video_core/renderer_vulkan/vk_turbo_mode.cpp21
-rw-r--r--src/video_core/renderer_vulkan/vk_turbo_mode.h9
-rw-r--r--src/video_core/texture_cache/descriptor_table.h4
-rw-r--r--src/video_core/texture_cache/formatter.cpp3
-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.h21
-rw-r--r--src/video_core/texture_cache/texture_cache_base.h3
-rw-r--r--src/video_core/texture_cache/util.cpp5
-rw-r--r--src/video_core/textures/decoders.cpp2
-rw-r--r--src/video_core/vulkan_common/nsight_aftermath_tracker.cpp8
-rw-r--r--src/video_core/vulkan_common/vulkan_device.cpp1390
-rw-r--r--src/video_core/vulkan_common/vulkan_device.h442
-rw-r--r--src/video_core/vulkan_common/vulkan_wrapper.cpp10
-rw-r--r--src/video_core/vulkan_common/vulkan_wrapper.h12
-rw-r--r--src/yuzu/CMakeLists.txt2
-rw-r--r--src/yuzu/Info.plist2
-rw-r--r--src/yuzu/applets/qt_software_keyboard.cpp2
-rw-r--r--src/yuzu/bootmanager.cpp51
-rw-r--r--src/yuzu/bootmanager.h6
-rw-r--r--src/yuzu/configuration/config.cpp54
-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.ui17
-rw-r--r--src/yuzu/configuration/configure_graphics_advanced.cpp5
-rw-r--r--src/yuzu/configuration/configure_input_advanced.cpp4
-rw-r--r--src/yuzu/configuration/configure_input_advanced.ui38
-rw-r--r--src/yuzu/configuration/configure_input_player.cpp45
-rw-r--r--src/yuzu/configuration/configure_input_player.h3
-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.cpp107
-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/game_list.cpp1
-rw-r--r--src/yuzu/game_list.h1
-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.cpp254
-rw-r--r--src/yuzu/main.h13
-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/overlay_dialog.cpp2
-rw-r--r--src/yuzu/util/sequence_dialog/sequence_dialog.cpp1
-rw-r--r--src/yuzu_cmd/config.cpp4
-rw-r--r--src/yuzu_cmd/default_ini.h40
-rw-r--r--src/yuzu_cmd/emu_window/emu_window_sdl2.cpp52
-rw-r--r--src/yuzu_cmd/emu_window/emu_window_sdl2.h10
-rw-r--r--src/yuzu_cmd/yuzu.cpp57
453 files changed, 22428 insertions, 7861 deletions
diff --git a/src/audio_core/renderer/adsp/audio_renderer.cpp b/src/audio_core/renderer/adsp/audio_renderer.cpp
index d982ef630..78c15629b 100644
--- a/src/audio_core/renderer/adsp/audio_renderer.cpp
+++ b/src/audio_core/renderer/adsp/audio_renderer.cpp
@@ -132,7 +132,7 @@ void AudioRenderer::CreateSinkStreams() {
}
void AudioRenderer::ThreadFunc() {
- constexpr char name[]{"AudioRenderer"};
+ static constexpr char name[]{"AudioRenderer"};
MicroProfileOnThreadCreate(name);
Common::SetCurrentThreadName(name);
Common::SetCurrentThreadPriority(Common::ThreadPriority::Critical);
diff --git a/src/audio_core/renderer/command/command_buffer.cpp b/src/audio_core/renderer/command/command_buffer.cpp
index 8c6fe97e7..0bd418306 100644
--- a/src/audio_core/renderer/command/command_buffer.cpp
+++ b/src/audio_core/renderer/command/command_buffer.cpp
@@ -251,8 +251,8 @@ void CommandBuffer::GenerateBiquadFilterCommand(const s32 node_id, EffectInfoBas
const auto& parameter{
*reinterpret_cast<BiquadFilterInfo::ParameterVersion1*>(effect_info.GetParameter())};
- const auto state{
- reinterpret_cast<VoiceState::BiquadFilterState*>(effect_info.GetStateBuffer())};
+ const auto state{reinterpret_cast<VoiceState::BiquadFilterState*>(
+ effect_info.GetStateBuffer() + channel * sizeof(VoiceState::BiquadFilterState))};
cmd.input = buffer_offset + parameter.inputs[channel];
cmd.output = buffer_offset + parameter.outputs[channel];
diff --git a/src/audio_core/renderer/command/command_generator.cpp b/src/audio_core/renderer/command/command_generator.cpp
index 2ea50d128..fba84c7bd 100644
--- a/src/audio_core/renderer/command/command_generator.cpp
+++ b/src/audio_core/renderer/command/command_generator.cpp
@@ -46,7 +46,7 @@ void CommandGenerator::GenerateDataSourceCommand(VoiceInfo& voice_info,
while (destination != nullptr) {
if (destination->IsConfigured()) {
auto mix_id{destination->GetMixId()};
- if (mix_id < mix_context.GetCount()) {
+ if (mix_id < mix_context.GetCount() && mix_id != UnusedSplitterId) {
auto mix_info{mix_context.GetInfo(mix_id)};
command_buffer.GenerateDepopPrepareCommand(
voice_info.node_id, voice_state, render_context.depop_buffer,
diff --git a/src/audio_core/renderer/command/effect/aux_.cpp b/src/audio_core/renderer/command/effect/aux_.cpp
index e76db893f..c5650effa 100644
--- a/src/audio_core/renderer/command/effect/aux_.cpp
+++ b/src/audio_core/renderer/command/effect/aux_.cpp
@@ -4,6 +4,7 @@
#include "audio_core/renderer/adsp/command_list_processor.h"
#include "audio_core/renderer/command/effect/aux_.h"
#include "audio_core/renderer/effect/aux_.h"
+#include "core/core.h"
#include "core/memory.h"
namespace AudioCore::AudioRenderer {
@@ -19,10 +20,24 @@ static void ResetAuxBufferDsp(Core::Memory::Memory& memory, const CpuAddr aux_in
return;
}
- auto info{reinterpret_cast<AuxInfo::AuxInfoDsp*>(memory.GetPointer(aux_info))};
- info->read_offset = 0;
- info->write_offset = 0;
- info->total_sample_count = 0;
+ AuxInfo::AuxInfoDsp info{};
+ auto info_ptr{&info};
+ bool host_safe{(aux_info & Core::Memory::YUZU_PAGEMASK) <=
+ (Core::Memory::YUZU_PAGESIZE - sizeof(AuxInfo::AuxInfoDsp))};
+
+ if (host_safe) [[likely]] {
+ info_ptr = memory.GetPointer<AuxInfo::AuxInfoDsp>(aux_info);
+ } else {
+ memory.ReadBlockUnsafe(aux_info, info_ptr, sizeof(AuxInfo::AuxInfoDsp));
+ }
+
+ info_ptr->read_offset = 0;
+ info_ptr->write_offset = 0;
+ info_ptr->total_sample_count = 0;
+
+ if (!host_safe) [[unlikely]] {
+ memory.WriteBlockUnsafe(aux_info, info_ptr, sizeof(AuxInfo::AuxInfoDsp));
+ }
}
/**
@@ -40,11 +55,10 @@ static void ResetAuxBufferDsp(Core::Memory::Memory& memory, const CpuAddr aux_in
* @param update_count - If non-zero, send_info_ will be updated.
* @return Number of samples written.
*/
-static u32 WriteAuxBufferDsp(Core::Memory::Memory& memory, const CpuAddr send_info_,
- [[maybe_unused]] u32 sample_count, const CpuAddr send_buffer,
- const u32 count_max, std::span<const s32> input,
- const u32 write_count_, const u32 write_offset,
- const u32 update_count) {
+static u32 WriteAuxBufferDsp(Core::Memory::Memory& memory, CpuAddr send_info_,
+ [[maybe_unused]] u32 sample_count, CpuAddr send_buffer, u32 count_max,
+ std::span<const s32> input, u32 write_count_, u32 write_offset,
+ u32 update_count) {
if (write_count_ > count_max) {
LOG_ERROR(Service_Audio,
"write_count must be smaller than count_max! write_count {}, count_max {}",
@@ -52,6 +66,11 @@ static u32 WriteAuxBufferDsp(Core::Memory::Memory& memory, const CpuAddr send_in
return 0;
}
+ if (send_info_ == 0) {
+ LOG_ERROR(Service_Audio, "send_info_ is 0!");
+ return 0;
+ }
+
if (input.empty()) {
LOG_ERROR(Service_Audio, "input buffer is empty!");
return 0;
@@ -67,33 +86,47 @@ static u32 WriteAuxBufferDsp(Core::Memory::Memory& memory, const CpuAddr send_in
}
AuxInfo::AuxInfoDsp send_info{};
- memory.ReadBlockUnsafe(send_info_, &send_info, sizeof(AuxInfo::AuxInfoDsp));
+ auto send_ptr = &send_info;
+ bool host_safe = (send_info_ & Core::Memory::YUZU_PAGEMASK) <=
+ (Core::Memory::YUZU_PAGESIZE - sizeof(AuxInfo::AuxInfoDsp));
- u32 target_write_offset{send_info.write_offset + write_offset};
- if (target_write_offset > count_max || write_count_ == 0) {
+ if (host_safe) [[likely]] {
+ send_ptr = memory.GetPointer<AuxInfo::AuxInfoDsp>(send_info_);
+ } else {
+ memory.ReadBlockUnsafe(send_info_, send_ptr, sizeof(AuxInfo::AuxInfoDsp));
+ }
+
+ u32 target_write_offset{send_ptr->write_offset + write_offset};
+ if (target_write_offset > count_max) {
return 0;
}
u32 write_count{write_count_};
- u32 write_pos{0};
+ u32 read_pos{0};
while (write_count > 0) {
u32 to_write{std::min(count_max - target_write_offset, write_count)};
-
- if (to_write > 0) {
+ const auto write_addr = send_buffer + target_write_offset * sizeof(s32);
+ bool write_safe{(write_addr & Core::Memory::YUZU_PAGEMASK) <=
+ (Core::Memory::YUZU_PAGESIZE - (write_addr + to_write * sizeof(s32)))};
+ if (write_safe) [[likely]] {
+ auto ptr = memory.GetPointer(write_addr);
+ std::memcpy(ptr, &input[read_pos], to_write * sizeof(s32));
+ } else {
memory.WriteBlockUnsafe(send_buffer + target_write_offset * sizeof(s32),
- &input[write_pos], to_write * sizeof(s32));
+ &input[read_pos], to_write * sizeof(s32));
}
-
target_write_offset = (target_write_offset + to_write) % count_max;
write_count -= to_write;
- write_pos += to_write;
+ read_pos += to_write;
}
if (update_count) {
- send_info.write_offset = (send_info.write_offset + update_count) % count_max;
+ send_ptr->write_offset = (send_ptr->write_offset + update_count) % count_max;
}
- memory.WriteBlockUnsafe(send_info_, &send_info, sizeof(AuxInfo::AuxInfoDsp));
+ if (!host_safe) [[unlikely]] {
+ memory.WriteBlockUnsafe(send_info_, send_ptr, sizeof(AuxInfo::AuxInfoDsp));
+ }
return write_count_;
}
@@ -102,7 +135,7 @@ static u32 WriteAuxBufferDsp(Core::Memory::Memory& memory, const CpuAddr send_in
* Read the given memory at return_buffer into the output mix buffer, and update return_info_ if
* update_count is set, to notify the game that an update happened.
*
- * @param memory - Core memory for writing.
+ * @param memory - Core memory for reading.
* @param return_info_ - Meta information for where to read the mix buffer.
* @param return_buffer - Memory address to read the samples from.
* @param count_max - Maximum number of samples in the receiving buffer.
@@ -112,16 +145,21 @@ static u32 WriteAuxBufferDsp(Core::Memory::Memory& memory, const CpuAddr send_in
* @param update_count - If non-zero, send_info_ will be updated.
* @return Number of samples read.
*/
-static u32 ReadAuxBufferDsp(Core::Memory::Memory& memory, const CpuAddr return_info_,
- const CpuAddr return_buffer, const u32 count_max, std::span<s32> output,
- const u32 count_, const u32 read_offset, const u32 update_count) {
+static u32 ReadAuxBufferDsp(Core::Memory::Memory& memory, CpuAddr return_info_,
+ CpuAddr return_buffer, u32 count_max, std::span<s32> output,
+ u32 read_count_, u32 read_offset, u32 update_count) {
if (count_max == 0) {
return 0;
}
- if (count_ > count_max) {
+ if (read_count_ > count_max) {
LOG_ERROR(Service_Audio, "count must be smaller than count_max! count {}, count_max {}",
- count_, count_max);
+ read_count_, count_max);
+ return 0;
+ }
+
+ if (return_info_ == 0) {
+ LOG_ERROR(Service_Audio, "return_info_ is 0!");
return 0;
}
@@ -136,35 +174,49 @@ static u32 ReadAuxBufferDsp(Core::Memory::Memory& memory, const CpuAddr return_i
}
AuxInfo::AuxInfoDsp return_info{};
- memory.ReadBlockUnsafe(return_info_, &return_info, sizeof(AuxInfo::AuxInfoDsp));
+ auto return_ptr = &return_info;
+ bool host_safe = (return_info_ & Core::Memory::YUZU_PAGEMASK) <=
+ (Core::Memory::YUZU_PAGESIZE - sizeof(AuxInfo::AuxInfoDsp));
+
+ if (host_safe) [[likely]] {
+ return_ptr = memory.GetPointer<AuxInfo::AuxInfoDsp>(return_info_);
+ } else {
+ memory.ReadBlockUnsafe(return_info_, return_ptr, sizeof(AuxInfo::AuxInfoDsp));
+ }
- u32 target_read_offset{return_info.read_offset + read_offset};
+ u32 target_read_offset{return_ptr->read_offset + read_offset};
if (target_read_offset > count_max) {
return 0;
}
- u32 read_count{count_};
- u32 read_pos{0};
+ u32 read_count{read_count_};
+ u32 write_pos{0};
while (read_count > 0) {
u32 to_read{std::min(count_max - target_read_offset, read_count)};
-
- if (to_read > 0) {
+ const auto read_addr = return_buffer + target_read_offset * sizeof(s32);
+ bool read_safe{(read_addr & Core::Memory::YUZU_PAGEMASK) <=
+ (Core::Memory::YUZU_PAGESIZE - (read_addr + to_read * sizeof(s32)))};
+ if (read_safe) [[likely]] {
+ auto ptr = memory.GetPointer(read_addr);
+ std::memcpy(&output[write_pos], ptr, to_read * sizeof(s32));
+ } else {
memory.ReadBlockUnsafe(return_buffer + target_read_offset * sizeof(s32),
- &output[read_pos], to_read * sizeof(s32));
+ &output[write_pos], to_read * sizeof(s32));
}
-
target_read_offset = (target_read_offset + to_read) % count_max;
read_count -= to_read;
- read_pos += to_read;
+ write_pos += to_read;
}
if (update_count) {
- return_info.read_offset = (return_info.read_offset + update_count) % count_max;
+ return_ptr->read_offset = (return_ptr->read_offset + update_count) % count_max;
}
- memory.WriteBlockUnsafe(return_info_, &return_info, sizeof(AuxInfo::AuxInfoDsp));
+ if (!host_safe) [[unlikely]] {
+ memory.WriteBlockUnsafe(return_info_, return_ptr, sizeof(AuxInfo::AuxInfoDsp));
+ }
- return count_;
+ return read_count_;
}
void AuxCommand::Dump([[maybe_unused]] const ADSP::CommandListProcessor& processor,
@@ -189,7 +241,7 @@ void AuxCommand::Process(const ADSP::CommandListProcessor& processor) {
update_count)};
if (read != processor.sample_count) {
- std::memset(&output_buffer[read], 0, processor.sample_count - read);
+ std::memset(&output_buffer[read], 0, (processor.sample_count - read) * sizeof(s32));
}
} else {
ResetAuxBufferDsp(*processor.memory, send_buffer_info);
diff --git a/src/audio_core/renderer/command/effect/biquad_filter.cpp b/src/audio_core/renderer/command/effect/biquad_filter.cpp
index edb30ce72..dea6423dc 100644
--- a/src/audio_core/renderer/command/effect/biquad_filter.cpp
+++ b/src/audio_core/renderer/command/effect/biquad_filter.cpp
@@ -4,6 +4,7 @@
#include "audio_core/renderer/adsp/command_list_processor.h"
#include "audio_core/renderer/command/effect/biquad_filter.h"
#include "audio_core/renderer/voice/voice_state.h"
+#include "common/bit_cast.h"
namespace AudioCore::AudioRenderer {
/**
@@ -19,21 +20,21 @@ namespace AudioCore::AudioRenderer {
void ApplyBiquadFilterFloat(std::span<s32> output, std::span<const s32> input,
std::array<s16, 3>& b_, std::array<s16, 2>& a_,
VoiceState::BiquadFilterState& state, const u32 sample_count) {
- constexpr s64 min{std::numeric_limits<s32>::min()};
- constexpr s64 max{std::numeric_limits<s32>::max()};
+ constexpr f64 min{std::numeric_limits<s32>::min()};
+ constexpr f64 max{std::numeric_limits<s32>::max()};
std::array<f64, 3> b{Common::FixedPoint<50, 14>::from_base(b_[0]).to_double(),
Common::FixedPoint<50, 14>::from_base(b_[1]).to_double(),
Common::FixedPoint<50, 14>::from_base(b_[2]).to_double()};
std::array<f64, 2> a{Common::FixedPoint<50, 14>::from_base(a_[0]).to_double(),
Common::FixedPoint<50, 14>::from_base(a_[1]).to_double()};
- std::array<f64, 4> s{state.s0.to_double(), state.s1.to_double(), state.s2.to_double(),
- state.s3.to_double()};
+ std::array<f64, 4> s{Common::BitCast<f64>(state.s0), Common::BitCast<f64>(state.s1),
+ Common::BitCast<f64>(state.s2), Common::BitCast<f64>(state.s3)};
for (u32 i = 0; i < sample_count; i++) {
f64 in_sample{static_cast<f64>(input[i])};
auto sample{in_sample * b[0] + s[0] * b[1] + s[1] * b[2] + s[2] * a[0] + s[3] * a[1]};
- output[i] = static_cast<s32>(std::clamp(static_cast<s64>(sample), min, max));
+ output[i] = static_cast<s32>(std::clamp(sample, min, max));
s[1] = s[0];
s[0] = in_sample;
@@ -41,10 +42,10 @@ void ApplyBiquadFilterFloat(std::span<s32> output, std::span<const s32> input,
s[2] = sample;
}
- state.s0 = s[0];
- state.s1 = s[1];
- state.s2 = s[2];
- state.s3 = s[3];
+ state.s0 = Common::BitCast<s64>(s[0]);
+ state.s1 = Common::BitCast<s64>(s[1]);
+ state.s2 = Common::BitCast<s64>(s[2]);
+ state.s3 = Common::BitCast<s64>(s[3]);
}
/**
@@ -58,29 +59,20 @@ void ApplyBiquadFilterFloat(std::span<s32> output, std::span<const s32> input,
* @param sample_count - Number of samples to process.
*/
static void ApplyBiquadFilterInt(std::span<s32> output, std::span<const s32> input,
- std::array<s16, 3>& b_, std::array<s16, 2>& a_,
+ std::array<s16, 3>& b, std::array<s16, 2>& a,
VoiceState::BiquadFilterState& state, const u32 sample_count) {
constexpr s64 min{std::numeric_limits<s32>::min()};
constexpr s64 max{std::numeric_limits<s32>::max()};
- std::array<Common::FixedPoint<50, 14>, 3> b{
- Common::FixedPoint<50, 14>::from_base(b_[0]),
- Common::FixedPoint<50, 14>::from_base(b_[1]),
- Common::FixedPoint<50, 14>::from_base(b_[2]),
- };
- std::array<Common::FixedPoint<50, 14>, 3> a{
- Common::FixedPoint<50, 14>::from_base(a_[0]),
- Common::FixedPoint<50, 14>::from_base(a_[1]),
- };
for (u32 i = 0; i < sample_count; i++) {
- s64 in_sample{input[i]};
- auto sample{in_sample * b[0] + state.s0};
- const auto out_sample{std::clamp(sample.to_long(), min, max)};
+ const s64 in_sample{input[i]};
+ const s64 sample{in_sample * b[0] + state.s0};
+ const s64 out_sample{std::clamp<s64>((sample + (1 << 13)) >> 14, min, max)};
output[i] = static_cast<s32>(out_sample);
state.s0 = state.s1 + b[1] * in_sample + a[0] * out_sample;
- state.s1 = 0 + b[2] * in_sample + a[1] * out_sample;
+ state.s1 = b[2] * in_sample + a[1] * out_sample;
}
}
diff --git a/src/audio_core/renderer/command/effect/i3dl2_reverb.cpp b/src/audio_core/renderer/command/effect/i3dl2_reverb.cpp
index 2187d8a65..27d8b9844 100644
--- a/src/audio_core/renderer/command/effect/i3dl2_reverb.cpp
+++ b/src/audio_core/renderer/command/effect/i3dl2_reverb.cpp
@@ -244,16 +244,16 @@ template <size_t NumChannels>
static void ApplyI3dl2ReverbEffect(I3dl2ReverbInfo::State& state,
std::span<std::span<const s32>> inputs,
std::span<std::span<s32>> outputs, const u32 sample_count) {
- constexpr std::array<u8, I3dl2ReverbInfo::MaxDelayTaps> OutTapIndexes1Ch{
+ static constexpr std::array<u8, I3dl2ReverbInfo::MaxDelayTaps> OutTapIndexes1Ch{
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
};
- constexpr std::array<u8, I3dl2ReverbInfo::MaxDelayTaps> OutTapIndexes2Ch{
+ static constexpr std::array<u8, I3dl2ReverbInfo::MaxDelayTaps> OutTapIndexes2Ch{
0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1,
};
- constexpr std::array<u8, I3dl2ReverbInfo::MaxDelayTaps> OutTapIndexes4Ch{
+ static constexpr std::array<u8, I3dl2ReverbInfo::MaxDelayTaps> OutTapIndexes4Ch{
0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 1, 1, 1, 0, 0, 0, 0, 3, 3, 3,
};
- constexpr std::array<u8, I3dl2ReverbInfo::MaxDelayTaps> OutTapIndexes6Ch{
+ static constexpr std::array<u8, I3dl2ReverbInfo::MaxDelayTaps> OutTapIndexes6Ch{
2, 0, 0, 1, 1, 1, 1, 4, 4, 4, 1, 1, 1, 0, 0, 0, 0, 5, 5, 5,
};
diff --git a/src/audio_core/renderer/command/effect/reverb.cpp b/src/audio_core/renderer/command/effect/reverb.cpp
index 427489214..6fe844ff0 100644
--- a/src/audio_core/renderer/command/effect/reverb.cpp
+++ b/src/audio_core/renderer/command/effect/reverb.cpp
@@ -252,16 +252,16 @@ template <size_t NumChannels>
static void ApplyReverbEffect(const ReverbInfo::ParameterVersion2& params, ReverbInfo::State& state,
std::vector<std::span<const s32>>& inputs,
std::vector<std::span<s32>>& outputs, const u32 sample_count) {
- constexpr std::array<u8, ReverbInfo::MaxDelayTaps> OutTapIndexes1Ch{
+ static constexpr std::array<u8, ReverbInfo::MaxDelayTaps> OutTapIndexes1Ch{
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
};
- constexpr std::array<u8, ReverbInfo::MaxDelayTaps> OutTapIndexes2Ch{
+ static constexpr std::array<u8, ReverbInfo::MaxDelayTaps> OutTapIndexes2Ch{
0, 0, 1, 1, 0, 1, 0, 0, 1, 1,
};
- constexpr std::array<u8, ReverbInfo::MaxDelayTaps> OutTapIndexes4Ch{
+ static constexpr std::array<u8, ReverbInfo::MaxDelayTaps> OutTapIndexes4Ch{
0, 0, 1, 1, 0, 1, 2, 2, 3, 3,
};
- constexpr std::array<u8, ReverbInfo::MaxDelayTaps> OutTapIndexes6Ch{
+ static constexpr std::array<u8, ReverbInfo::MaxDelayTaps> OutTapIndexes6Ch{
0, 0, 1, 1, 2, 2, 4, 4, 5, 5,
};
diff --git a/src/audio_core/renderer/command/resample/upsample.cpp b/src/audio_core/renderer/command/resample/upsample.cpp
index 6c3ff31f7..86ddee1a4 100644
--- a/src/audio_core/renderer/command/resample/upsample.cpp
+++ b/src/audio_core/renderer/command/resample/upsample.cpp
@@ -19,26 +19,26 @@ namespace AudioCore::AudioRenderer {
static void SrcProcessFrame(std::span<s32> output, std::span<const s32> input,
const u32 target_sample_count, const u32 source_sample_count,
UpsamplerState* state) {
- constexpr u32 WindowSize = 10;
- constexpr std::array<Common::FixedPoint<24, 8>, WindowSize> SincWindow1{
- 51.93359375f, -18.80078125f, 9.73046875f, -5.33203125f, 2.84375f,
- -1.41015625f, 0.62109375f, -0.2265625f, 0.0625f, -0.00390625f,
+ static constexpr u32 WindowSize = 10;
+ static constexpr std::array<Common::FixedPoint<17, 15>, WindowSize> WindowedSinc1{
+ 0.95376587f, -0.12872314f, 0.060028076f, -0.032470703f, 0.017669678f,
+ -0.009124756f, 0.004272461f, -0.001739502f, 0.000579834f, -0.000091552734f,
};
- constexpr std::array<Common::FixedPoint<24, 8>, WindowSize> SincWindow2{
- 105.35546875f, -24.52734375f, 11.9609375f, -6.515625f, 3.52734375f,
- -1.796875f, 0.828125f, -0.32421875f, 0.1015625f, -0.015625f,
+ static constexpr std::array<Common::FixedPoint<17, 15>, WindowSize> WindowedSinc2{
+ 0.8230896f, -0.19161987f, 0.093444824f, -0.05090332f, 0.027557373f,
+ -0.014038086f, 0.0064697266f, -0.002532959f, 0.00079345703f, -0.00012207031f,
};
- constexpr std::array<Common::FixedPoint<24, 8>, WindowSize> SincWindow3{
- 122.08203125f, -16.47656250f, 7.68359375f, -4.15625000f, 2.26171875f,
- -1.16796875f, 0.54687500f, -0.22265625f, 0.07421875f, -0.01171875f,
+ static constexpr std::array<Common::FixedPoint<17, 15>, WindowSize> WindowedSinc3{
+ 0.6298828f, -0.19274902f, 0.09725952f, -0.05319214f, 0.028625488f,
+ -0.014373779f, 0.006500244f, -0.0024719238f, 0.0007324219f, -0.000091552734f,
};
- constexpr std::array<Common::FixedPoint<24, 8>, WindowSize> SincWindow4{
- 23.73437500f, -9.62109375f, 5.07812500f, -2.78125000f, 1.46875000f,
- -0.71484375f, 0.30859375f, -0.10546875f, 0.02734375f, 0.00000000f,
+ static constexpr std::array<Common::FixedPoint<17, 15>, WindowSize> WindowedSinc4{
+ 0.4057312f, -0.1468811f, 0.07601929f, -0.041656494f, 0.022216797f,
+ -0.011016846f, 0.004852295f, -0.0017700195f, 0.00048828125f, -0.000030517578f,
};
- constexpr std::array<Common::FixedPoint<24, 8>, WindowSize> SincWindow5{
- 80.62500000f, -24.67187500f, 12.44921875f, -6.80859375f, 3.66406250f,
- -1.83984375f, 0.83203125f, -0.31640625f, 0.09375000f, -0.01171875f,
+ static constexpr std::array<Common::FixedPoint<17, 15>, WindowSize> WindowedSinc5{
+ 0.1854248f, -0.075164795f, 0.03967285f, -0.021728516f, 0.011474609f,
+ -0.005584717f, 0.0024108887f, -0.0008239746f, 0.00021362305f, 0.0f,
};
if (!state->initialized) {
@@ -91,52 +91,31 @@ static void SrcProcessFrame(std::span<s32> output, std::span<const s32> input,
static_cast<u16>((state->history_output_index + 1) % UpsamplerState::HistorySize);
};
- auto calculate_sample = [&state](std::span<const Common::FixedPoint<24, 8>> coeffs1,
- std::span<const Common::FixedPoint<24, 8>> coeffs2) -> s32 {
+ auto calculate_sample = [&state](std::span<const Common::FixedPoint<17, 15>> coeffs1,
+ std::span<const Common::FixedPoint<17, 15>> coeffs2) -> s32 {
auto output_index{state->history_output_index};
- auto start_pos{output_index - state->history_start_index + 1U};
- auto end_pos{10U};
+ u64 result{0};
- if (start_pos < 10) {
- end_pos = start_pos;
- }
-
- u64 prev_contrib{0};
- u32 coeff_index{0};
- for (; coeff_index < end_pos; coeff_index++, output_index--) {
- prev_contrib += static_cast<u64>(state->history[output_index].to_raw()) *
- coeffs1[coeff_index].to_raw();
- }
+ for (u32 coeff_index = 0; coeff_index < 10; coeff_index++) {
+ result += static_cast<u64>(state->history[output_index].to_raw()) *
+ coeffs1[coeff_index].to_raw();
- auto end_index{state->history_end_index};
- for (; start_pos < 9; start_pos++, coeff_index++, end_index--) {
- prev_contrib += static_cast<u64>(state->history[end_index].to_raw()) *
- coeffs1[coeff_index].to_raw();
+ output_index = output_index == state->history_start_index ? state->history_end_index
+ : output_index - 1;
}
output_index =
static_cast<u16>((state->history_output_index + 1) % UpsamplerState::HistorySize);
- start_pos = state->history_end_index - output_index + 1U;
- end_pos = 10U;
- if (start_pos < 10) {
- end_pos = start_pos;
- }
-
- u64 next_contrib{0};
- coeff_index = 0;
- for (; coeff_index < end_pos; coeff_index++, output_index++) {
- next_contrib += static_cast<u64>(state->history[output_index].to_raw()) *
- coeffs2[coeff_index].to_raw();
- }
+ for (u32 coeff_index = 0; coeff_index < 10; coeff_index++) {
+ result += static_cast<u64>(state->history[output_index].to_raw()) *
+ coeffs2[coeff_index].to_raw();
- auto start_index{state->history_start_index};
- for (; start_pos < 9; start_pos++, start_index++, coeff_index++) {
- next_contrib += static_cast<u64>(state->history[start_index].to_raw()) *
- coeffs2[coeff_index].to_raw();
+ output_index = output_index == state->history_end_index ? state->history_start_index
+ : output_index + 1;
}
- return static_cast<s32>(((prev_contrib >> 15) + (next_contrib >> 15)) >> 8);
+ return static_cast<s32>(result >> (8 + 15));
};
switch (state->ratio.to_int_floor()) {
@@ -150,23 +129,23 @@ static void SrcProcessFrame(std::span<s32> output, std::span<const s32> input,
break;
case 1:
- output[write_index] = calculate_sample(SincWindow3, SincWindow4);
+ output[write_index] = calculate_sample(WindowedSinc1, WindowedSinc5);
break;
case 2:
- output[write_index] = calculate_sample(SincWindow2, SincWindow1);
+ output[write_index] = calculate_sample(WindowedSinc2, WindowedSinc4);
break;
case 3:
- output[write_index] = calculate_sample(SincWindow5, SincWindow5);
+ output[write_index] = calculate_sample(WindowedSinc3, WindowedSinc3);
break;
case 4:
- output[write_index] = calculate_sample(SincWindow1, SincWindow2);
+ output[write_index] = calculate_sample(WindowedSinc4, WindowedSinc2);
break;
case 5:
- output[write_index] = calculate_sample(SincWindow4, SincWindow3);
+ output[write_index] = calculate_sample(WindowedSinc5, WindowedSinc1);
break;
}
state->sample_index = static_cast<u8>((state->sample_index + 1) % 6);
@@ -183,11 +162,11 @@ static void SrcProcessFrame(std::span<s32> output, std::span<const s32> input,
break;
case 1:
- output[write_index] = calculate_sample(SincWindow2, SincWindow1);
+ output[write_index] = calculate_sample(WindowedSinc2, WindowedSinc4);
break;
case 2:
- output[write_index] = calculate_sample(SincWindow1, SincWindow2);
+ output[write_index] = calculate_sample(WindowedSinc4, WindowedSinc2);
break;
}
state->sample_index = static_cast<u8>((state->sample_index + 1) % 3);
@@ -204,12 +183,12 @@ static void SrcProcessFrame(std::span<s32> output, std::span<const s32> input,
break;
case 1:
- output[write_index] = calculate_sample(SincWindow1, SincWindow2);
+ output[write_index] = calculate_sample(WindowedSinc4, WindowedSinc2);
break;
case 2:
increment();
- output[write_index] = calculate_sample(SincWindow2, SincWindow1);
+ output[write_index] = calculate_sample(WindowedSinc2, WindowedSinc4);
break;
}
state->sample_index = static_cast<u8>((state->sample_index + 1) % 3);
diff --git a/src/audio_core/renderer/system.cpp b/src/audio_core/renderer/system.cpp
index 4fac30c7c..31cbee282 100644
--- a/src/audio_core/renderer/system.cpp
+++ b/src/audio_core/renderer/system.cpp
@@ -127,7 +127,7 @@ Result System::Initialize(const AudioRendererParameterInternal& params,
render_device = params.rendering_device;
execution_mode = params.execution_mode;
- core.Memory().ZeroBlock(*core.Kernel().CurrentProcess(), transfer_memory->GetSourceAddress(),
+ core.Memory().ZeroBlock(*core.ApplicationProcess(), transfer_memory->GetSourceAddress(),
transfer_memory_size);
// Note: We're not actually using the transfer memory because it's a pain to code for.
diff --git a/src/audio_core/renderer/system_manager.cpp b/src/audio_core/renderer/system_manager.cpp
index f66b2b890..ce631f810 100644
--- a/src/audio_core/renderer/system_manager.cpp
+++ b/src/audio_core/renderer/system_manager.cpp
@@ -94,7 +94,7 @@ bool SystemManager::Remove(System& system_) {
}
void SystemManager::ThreadFunc() {
- constexpr char name[]{"AudioRenderSystemManager"};
+ static constexpr char name[]{"AudioRenderSystemManager"};
MicroProfileOnThreadCreate(name);
Common::SetCurrentThreadName(name);
Common::SetCurrentThreadPriority(Common::ThreadPriority::High);
diff --git a/src/audio_core/renderer/voice/voice_state.h b/src/audio_core/renderer/voice/voice_state.h
index d5497e2fb..ce947233f 100644
--- a/src/audio_core/renderer/voice/voice_state.h
+++ b/src/audio_core/renderer/voice/voice_state.h
@@ -19,10 +19,10 @@ struct VoiceState {
* State of the voice's biquad filter.
*/
struct BiquadFilterState {
- Common::FixedPoint<50, 14> s0;
- Common::FixedPoint<50, 14> s1;
- Common::FixedPoint<50, 14> s2;
- Common::FixedPoint<50, 14> s3;
+ s64 s0;
+ s64 s1;
+ s64 s2;
+ s64 s3;
};
/**
diff --git a/src/audio_core/sink/cubeb_sink.cpp b/src/audio_core/sink/cubeb_sink.cpp
index 32c1b1cb3..9133f5388 100644
--- a/src/audio_core/sink/cubeb_sink.cpp
+++ b/src/audio_core/sink/cubeb_sink.cpp
@@ -302,11 +302,21 @@ std::vector<std::string> ListCubebSinkDevices(bool capture) {
std::vector<std::string> device_list;
cubeb* ctx;
+#ifdef _WIN32
+ auto com_init_result = CoInitializeEx(nullptr, COINIT_MULTITHREADED);
+#endif
+
if (cubeb_init(&ctx, "yuzu Device Enumerator", nullptr) != CUBEB_OK) {
LOG_CRITICAL(Audio_Sink, "cubeb_init failed");
return {};
}
+#ifdef _WIN32
+ if (SUCCEEDED(com_init_result)) {
+ CoUninitialize();
+ }
+#endif
+
auto type{capture ? CUBEB_DEVICE_TYPE_INPUT : CUBEB_DEVICE_TYPE_OUTPUT};
cubeb_device_collection collection;
if (cubeb_enumerate_devices(ctx, type, &collection) != CUBEB_OK) {
@@ -329,12 +339,22 @@ std::vector<std::string> ListCubebSinkDevices(bool capture) {
u32 GetCubebLatency() {
cubeb* ctx;
+#ifdef _WIN32
+ auto com_init_result = CoInitializeEx(nullptr, COINIT_MULTITHREADED);
+#endif
+
if (cubeb_init(&ctx, "yuzu Latency Getter", nullptr) != CUBEB_OK) {
LOG_CRITICAL(Audio_Sink, "cubeb_init failed");
// Return a large latency so we choose SDL instead.
return 10000u;
}
+#ifdef _WIN32
+ if (SUCCEEDED(com_init_result)) {
+ CoUninitialize();
+ }
+#endif
+
cubeb_stream_params params{};
params.rate = TargetSampleRate;
params.channels = 2;
diff --git a/src/audio_core/sink/sink_stream.cpp b/src/audio_core/sink/sink_stream.cpp
index 06c2a876e..39a21b0f0 100644
--- a/src/audio_core/sink/sink_stream.cpp
+++ b/src/audio_core/sink/sink_stream.cpp
@@ -35,7 +35,7 @@ void SinkStream::AppendBuffer(SinkBuffer& buffer, std::vector<s16>& samples) {
if (system_channels == 6 && device_channels == 2) {
// We're given 6 channels, but our device only outputs 2, so downmix.
- constexpr std::array<f32, 4> down_mix_coeff{1.0f, 0.707f, 0.251f, 0.707f};
+ static constexpr std::array<f32, 4> down_mix_coeff{1.0f, 0.707f, 0.251f, 0.707f};
for (u32 read_index = 0, write_index = 0; read_index < samples.size();
read_index += system_channels, write_index += device_channels) {
@@ -202,7 +202,7 @@ void SinkStream::ProcessAudioOutAndRender(std::span<s16> output_buffer, std::siz
// If we're paused or going to shut down, we don't want to consume buffers as coretiming is
// paused and we'll desync, so just play silence.
if (system.IsPaused() || system.IsShuttingDown()) {
- constexpr std::array<s16, 6> silence{};
+ static constexpr std::array<s16, 6> silence{};
for (size_t i = frames_written; i < num_frames; i++) {
std::memcpy(&output_buffer[i * frame_size], &silence[0], frame_size_bytes);
}
@@ -270,7 +270,7 @@ void SinkStream::Stall() {
if (stalled_lock) {
return;
}
- stalled_lock = system.StallProcesses();
+ stalled_lock = system.StallApplication();
}
void SinkStream::Unstall() {
@@ -278,7 +278,7 @@ void SinkStream::Unstall() {
if (!stalled_lock) {
return;
}
- system.UnstallProcesses();
+ system.UnstallApplication();
stalled_lock.unlock();
}
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
common_precompiled_headers.h
common_types.h
concepts.h
+ demangle.cpp
+ demangle.h
div_ceil.h
dynamic_library.cpp
dynamic_library.h
@@ -175,7 +177,7 @@ endif()
create_target_directory_groups(common)
target_link_libraries(common PUBLIC ${Boost_LIBRARIES} fmt::fmt microprofile Threads::Threads)
-target_link_libraries(common PRIVATE lz4::lz4 zstd::zstd)
+target_link_libraries(common PRIVATE lz4::lz4 zstd::zstd LLVM::Demangle)
if (YUZU_USE_PRECOMPILED_HEADERS)
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 @@
namespace Common {
template <typename VaType, size_t AddressSpaceBits>
-concept AddressSpaceValid = std::is_unsigned_v<VaType> && sizeof(VaType) * 8 >= AddressSpaceBits;
+concept AddressSpaceValid = std::is_unsigned_v<VaType> && sizeof(VaType) * 8 >=
+AddressSpaceBits;
struct EmptyStruct {};
@@ -21,7 +22,7 @@ struct EmptyStruct {};
*/
template <typename VaType, VaType UnmappedVa, typename PaType, PaType UnmappedPa,
bool PaContigSplit, size_t AddressSpaceBits, typename ExtraBlockInfo = EmptyStruct>
-requires AddressSpaceValid<VaType, AddressSpaceBits>
+ requires AddressSpaceValid<VaType, AddressSpaceBits>
class FlatAddressSpaceMap {
public:
/// The maximum VA that this AS can technically reach
@@ -109,7 +110,7 @@ private:
* initial, fast linear pass and a subsequent slower pass that iterates until it finds a free block
*/
template <typename VaType, VaType UnmappedVa, size_t AddressSpaceBits>
-requires AddressSpaceValid<VaType, AddressSpaceBits>
+ requires AddressSpaceValid<VaType, AddressSpaceBits>
class FlatAllocator
: public FlatAddressSpaceMap<VaType, UnmappedVa, bool, false, false, AddressSpaceBits> {
private:
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 @@
namespace Common {
template <typename T>
-requires std::is_unsigned_v<T>
+ requires std::is_unsigned_v<T>
[[nodiscard]] constexpr T AlignUp(T value, size_t size) {
auto mod{static_cast<T>(value % size)};
value -= mod;
@@ -18,31 +18,31 @@ requires std::is_unsigned_v<T>
}
template <typename T>
-requires std::is_unsigned_v<T>
+ requires std::is_unsigned_v<T>
[[nodiscard]] constexpr T AlignUpLog2(T value, size_t align_log2) {
return static_cast<T>((value + ((1ULL << align_log2) - 1)) >> align_log2 << align_log2);
}
template <typename T>
-requires std::is_unsigned_v<T>
+ requires std::is_unsigned_v<T>
[[nodiscard]] constexpr T AlignDown(T value, size_t size) {
return static_cast<T>(value - value % size);
}
template <typename T>
-requires std::is_unsigned_v<T>
+ requires std::is_unsigned_v<T>
[[nodiscard]] constexpr bool Is4KBAligned(T value) {
return (value & 0xFFF) == 0;
}
template <typename T>
-requires std::is_unsigned_v<T>
+ requires std::is_unsigned_v<T>
[[nodiscard]] constexpr bool IsWordAligned(T value) {
return (value & 0b11) == 0;
}
template <typename T>
-requires std::is_integral_v<T>
+ requires std::is_integral_v<T>
[[nodiscard]] constexpr bool IsAligned(T value, size_t alignment) {
using U = typename std::make_unsigned_t<T>;
const U mask = static_cast<U>(alignment - 1);
@@ -50,7 +50,7 @@ requires std::is_integral_v<T>
}
template <typename T, typename U>
-requires std::is_integral_v<T>
+ requires std::is_integral_v<T>
[[nodiscard]] constexpr T DivideUp(T x, U y) {
return (x + (y - 1)) / y;
}
@@ -73,11 +73,11 @@ public:
constexpr AlignmentAllocator(const AlignmentAllocator<T2, Align>&) noexcept {}
[[nodiscard]] T* allocate(size_type n) {
- return static_cast<T*>(::operator new (n * sizeof(T), std::align_val_t{Align}));
+ return static_cast<T*>(::operator new(n * sizeof(T), std::align_val_t{Align}));
}
void deallocate(T* p, size_type n) {
- ::operator delete (p, n * sizeof(T), std::align_val_t{Align});
+ ::operator delete(p, n * sizeof(T), std::align_val_t{Align});
}
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*);
#if defined(AE_VCPP) || defined(AE_ICC)
#define AE_FORCEINLINE __forceinline
#elif defined(AE_GCC)
-//#define AE_FORCEINLINE __attribute__((always_inline))
+// #define AE_FORCEINLINE __attribute__((always_inline))
#define AE_FORCEINLINE inline
#else
#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>
}
template <typename T>
-requires std::is_unsigned_v<T>
+ requires std::is_unsigned_v<T>
[[nodiscard]] constexpr bool IsPow2(T value) {
return std::has_single_bit(value);
}
template <typename T>
-requires std::is_integral_v<T>
+ requires std::is_integral_v<T>
[[nodiscard]] T NextPow2(T value) {
return static_cast<T>(1ULL << ((8U * sizeof(T)) - std::countl_zero(value - 1U)));
}
template <size_t bit_index, typename T>
-requires std::is_integral_v<T>
+ requires std::is_integral_v<T>
[[nodiscard]] constexpr bool Bit(const T value) {
static_assert(bit_index < BitSize<T>(), "bit_index must be smaller than size of T");
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>;
// is available on all supported platforms.
template <typename Derived, typename Base>
concept DerivedFrom = requires {
- std::is_base_of_v<Base, Derived>;
- std::is_convertible_v<const volatile Derived*, const volatile Base*>;
-};
+ std::is_base_of_v<Base, Derived>;
+ std::is_convertible_v<const volatile Derived*, const volatile Base*>;
+ };
// TODO: Replace with std::convertible_to when libc++ implements it.
template <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 @@
+// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include <llvm/Demangle/Demangle.h>
+
+#include "common/demangle.h"
+#include "common/scope_exit.h"
+
+namespace Common {
+
+std::string DemangleSymbol(const std::string& mangled) {
+ auto is_itanium = [](const std::string& name) -> bool {
+ // A valid Itanium encoding requires 1-4 leading underscores, followed by 'Z'.
+ auto pos = name.find_first_not_of('_');
+ return pos > 0 && pos <= 4 && pos < name.size() && name[pos] == 'Z';
+ };
+
+ if (mangled.empty()) {
+ return mangled;
+ }
+
+ char* demangled = nullptr;
+ SCOPE_EXIT({ std::free(demangled); });
+
+ if (is_itanium(mangled)) {
+ demangled = llvm::itaniumDemangle(mangled.c_str(), nullptr, nullptr, nullptr);
+ }
+
+ if (!demangled) {
+ return mangled;
+ }
+ return demangled;
+}
+
+} // 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 @@
+// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#pragma once
+
+#include <string>
+
+namespace Common {
+
+std::string DemangleSymbol(const std::string& mangled);
+
+} // 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 {
/// Ceiled integer division.
template <typename N, typename D>
-requires std::is_integral_v<N> && std::is_unsigned_v<D>
+ requires std::is_integral_v<N> && std::is_unsigned_v<D>
[[nodiscard]] constexpr N DivCeil(N number, D divisor) {
return static_cast<N>((static_cast<D>(number) + divisor - 1) / divisor);
}
/// Ceiled integer division with logarithmic divisor in base 2
template <typename N, typename D>
-requires std::is_integral_v<N> && std::is_unsigned_v<D>
+ requires std::is_integral_v<N> && std::is_unsigned_v<D>
[[nodiscard]] constexpr N DivCeilLog2(N value, D alignment_log2) {
return static_cast<N>((static_cast<D>(value) + (D(1) << alignment_log2) - 1) >> alignment_log2);
}
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 {
* Additionally, this requires E to be trivially destructible
*/
template <typename T, typename E, bool = std::is_trivially_destructible_v<T>>
-requires std::is_trivially_destructible_v<E>
+ requires std::is_trivially_destructible_v<E>
struct expected_storage_base {
constexpr expected_storage_base() : m_val{T{}}, m_has_val{true} {}
@@ -111,7 +111,7 @@ struct expected_storage_base {
* Additionally, this requires E to be trivially destructible
*/
template <typename T, typename E>
-requires std::is_trivially_destructible_v<E>
+ requires std::is_trivially_destructible_v<E>
struct expected_storage_base<T, E, true> {
constexpr expected_storage_base() : m_val{T{}}, m_has_val{true} {}
@@ -251,7 +251,7 @@ struct expected_operations_base : expected_storage_base<T, E> {
* Additionally, this requires E to be trivially copy constructible
*/
template <typename T, typename E, bool = std::is_trivially_copy_constructible_v<T>>
-requires std::is_trivially_copy_constructible_v<E>
+ requires std::is_trivially_copy_constructible_v<E>
struct expected_copy_base : expected_operations_base<T, E> {
using expected_operations_base<T, E>::expected_operations_base;
};
@@ -261,7 +261,7 @@ struct expected_copy_base : expected_operations_base<T, E> {
* Additionally, this requires E to be trivially copy constructible
*/
template <typename T, typename E>
-requires std::is_trivially_copy_constructible_v<E>
+ requires std::is_trivially_copy_constructible_v<E>
struct expected_copy_base<T, E, false> : expected_operations_base<T, E> {
using expected_operations_base<T, E>::expected_operations_base;
@@ -289,7 +289,7 @@ struct expected_copy_base<T, E, false> : expected_operations_base<T, E> {
* Additionally, this requires E to be trivially move constructible
*/
template <typename T, typename E, bool = std::is_trivially_move_constructible_v<T>>
-requires std::is_trivially_move_constructible_v<E>
+ requires std::is_trivially_move_constructible_v<E>
struct expected_move_base : expected_copy_base<T, E> {
using expected_copy_base<T, E>::expected_copy_base;
};
@@ -299,7 +299,7 @@ struct expected_move_base : expected_copy_base<T, E> {
* Additionally, this requires E to be trivially move constructible
*/
template <typename T, typename E>
-requires std::is_trivially_move_constructible_v<E>
+ requires std::is_trivially_move_constructible_v<E>
struct expected_move_base<T, E, false> : expected_copy_base<T, E> {
using expected_copy_base<T, E>::expected_copy_base;
@@ -330,9 +330,9 @@ template <typename T, typename E,
bool = std::conjunction_v<std::is_trivially_copy_assignable<T>,
std::is_trivially_copy_constructible<T>,
std::is_trivially_destructible<T>>>
-requires std::conjunction_v<std::is_trivially_copy_assignable<E>,
- std::is_trivially_copy_constructible<E>,
- std::is_trivially_destructible<E>>
+ requires std::conjunction_v<std::is_trivially_copy_assignable<E>,
+ std::is_trivially_copy_constructible<E>,
+ std::is_trivially_destructible<E>>
struct expected_copy_assign_base : expected_move_base<T, E> {
using expected_move_base<T, E>::expected_move_base;
};
@@ -342,9 +342,9 @@ struct expected_copy_assign_base : expected_move_base<T, E> {
* Additionally, this requires E to be trivially copy assignable
*/
template <typename T, typename E>
-requires std::conjunction_v<std::is_trivially_copy_assignable<E>,
- std::is_trivially_copy_constructible<E>,
- std::is_trivially_destructible<E>>
+ requires std::conjunction_v<std::is_trivially_copy_assignable<E>,
+ std::is_trivially_copy_constructible<E>,
+ std::is_trivially_destructible<E>>
struct expected_copy_assign_base<T, E, false> : expected_move_base<T, E> {
using expected_move_base<T, E>::expected_move_base;
@@ -371,9 +371,9 @@ template <typename T, typename E,
bool = std::conjunction_v<std::is_trivially_move_assignable<T>,
std::is_trivially_move_constructible<T>,
std::is_trivially_destructible<T>>>
-requires std::conjunction_v<std::is_trivially_move_assignable<E>,
- std::is_trivially_move_constructible<E>,
- std::is_trivially_destructible<E>>
+ requires std::conjunction_v<std::is_trivially_move_assignable<E>,
+ std::is_trivially_move_constructible<E>,
+ std::is_trivially_destructible<E>>
struct expected_move_assign_base : expected_copy_assign_base<T, E> {
using expected_copy_assign_base<T, E>::expected_copy_assign_base;
};
@@ -383,9 +383,9 @@ struct expected_move_assign_base : expected_copy_assign_base<T, E> {
* Additionally, this requires E to be trivially move assignable
*/
template <typename T, typename E>
-requires std::conjunction_v<std::is_trivially_move_assignable<E>,
- std::is_trivially_move_constructible<E>,
- std::is_trivially_destructible<E>>
+ requires std::conjunction_v<std::is_trivially_move_assignable<E>,
+ std::is_trivially_move_constructible<E>,
+ std::is_trivially_destructible<E>>
struct expected_move_assign_base<T, E, false> : expected_copy_assign_base<T, E> {
using expected_copy_assign_base<T, E>::expected_copy_assign_base;
@@ -412,7 +412,7 @@ struct expected_move_assign_base<T, E, false> : expected_copy_assign_base<T, E>
*/
template <typename T, typename E, bool EnableCopy = std::is_copy_constructible_v<T>,
bool EnableMove = std::is_move_constructible_v<T>>
-requires std::conjunction_v<std::is_copy_constructible<E>, std::is_move_constructible<E>>
+ requires std::conjunction_v<std::is_copy_constructible<E>, std::is_move_constructible<E>>
struct expected_delete_ctor_base {
expected_delete_ctor_base() = default;
expected_delete_ctor_base(const expected_delete_ctor_base&) = default;
@@ -422,7 +422,7 @@ struct expected_delete_ctor_base {
};
template <typename T, typename E>
-requires std::conjunction_v<std::is_copy_constructible<E>, std::is_move_constructible<E>>
+ requires std::conjunction_v<std::is_copy_constructible<E>, std::is_move_constructible<E>>
struct expected_delete_ctor_base<T, E, true, false> {
expected_delete_ctor_base() = default;
expected_delete_ctor_base(const expected_delete_ctor_base&) = default;
@@ -432,7 +432,7 @@ struct expected_delete_ctor_base<T, E, true, false> {
};
template <typename T, typename E>
-requires std::conjunction_v<std::is_copy_constructible<E>, std::is_move_constructible<E>>
+ requires std::conjunction_v<std::is_copy_constructible<E>, std::is_move_constructible<E>>
struct expected_delete_ctor_base<T, E, false, true> {
expected_delete_ctor_base() = default;
expected_delete_ctor_base(const expected_delete_ctor_base&) = delete;
@@ -442,7 +442,7 @@ struct expected_delete_ctor_base<T, E, false, true> {
};
template <typename T, typename E>
-requires std::conjunction_v<std::is_copy_constructible<E>, std::is_move_constructible<E>>
+ requires std::conjunction_v<std::is_copy_constructible<E>, std::is_move_constructible<E>>
struct expected_delete_ctor_base<T, E, false, false> {
expected_delete_ctor_base() = default;
expected_delete_ctor_base(const expected_delete_ctor_base&) = delete;
@@ -460,8 +460,8 @@ template <
typename T, typename E,
bool EnableCopy = std::conjunction_v<std::is_copy_constructible<T>, std::is_copy_assignable<T>>,
bool EnableMove = std::conjunction_v<std::is_move_constructible<T>, std::is_move_assignable<T>>>
-requires std::conjunction_v<std::is_copy_constructible<E>, std::is_move_constructible<E>,
- std::is_copy_assignable<E>, std::is_move_assignable<E>>
+ requires std::conjunction_v<std::is_copy_constructible<E>, std::is_move_constructible<E>,
+ std::is_copy_assignable<E>, std::is_move_assignable<E>>
struct expected_delete_assign_base {
expected_delete_assign_base() = default;
expected_delete_assign_base(const expected_delete_assign_base&) = default;
@@ -471,8 +471,8 @@ struct expected_delete_assign_base {
};
template <typename T, typename E>
-requires std::conjunction_v<std::is_copy_constructible<E>, std::is_move_constructible<E>,
- std::is_copy_assignable<E>, std::is_move_assignable<E>>
+ requires std::conjunction_v<std::is_copy_constructible<E>, std::is_move_constructible<E>,
+ std::is_copy_assignable<E>, std::is_move_assignable<E>>
struct expected_delete_assign_base<T, E, true, false> {
expected_delete_assign_base() = default;
expected_delete_assign_base(const expected_delete_assign_base&) = default;
@@ -482,8 +482,8 @@ struct expected_delete_assign_base<T, E, true, false> {
};
template <typename T, typename E>
-requires std::conjunction_v<std::is_copy_constructible<E>, std::is_move_constructible<E>,
- std::is_copy_assignable<E>, std::is_move_assignable<E>>
+ requires std::conjunction_v<std::is_copy_constructible<E>, std::is_move_constructible<E>,
+ std::is_copy_assignable<E>, std::is_move_assignable<E>>
struct expected_delete_assign_base<T, E, false, true> {
expected_delete_assign_base() = default;
expected_delete_assign_base(const expected_delete_assign_base&) = default;
@@ -493,8 +493,8 @@ struct expected_delete_assign_base<T, E, false, true> {
};
template <typename T, typename E>
-requires std::conjunction_v<std::is_copy_constructible<E>, std::is_move_constructible<E>,
- std::is_copy_assignable<E>, std::is_move_assignable<E>>
+ requires std::conjunction_v<std::is_copy_constructible<E>, std::is_move_constructible<E>,
+ std::is_copy_assignable<E>, std::is_move_assignable<E>>
struct expected_delete_assign_base<T, E, false, false> {
expected_delete_assign_base() = default;
expected_delete_assign_base(const expected_delete_assign_base&) = default;
diff --git a/src/common/input.h b/src/common/input.h
index fc14fd7bf..b5748a6c8 100644
--- a/src/common/input.h
+++ b/src/common/input.h
@@ -51,6 +51,8 @@ enum class PollingMode {
NFC,
// Enable infrared camera polling
IR,
+ // Enable ring controller polling
+ Ring,
};
enum class CameraFormat {
@@ -62,21 +64,22 @@ enum class CameraFormat {
None,
};
-// Vibration reply from the controller
-enum class VibrationError {
- None,
+// Different results that can happen from a device request
+enum class DriverResult {
+ Success,
+ WrongReply,
+ Timeout,
+ UnsupportedControllerType,
+ HandleInUse,
+ ErrorReadingData,
+ ErrorWritingData,
+ NoDeviceDetected,
+ InvalidHandle,
NotSupported,
Disabled,
Unknown,
};
-// Polling mode reply from the controller
-enum class PollingError {
- None,
- NotSupported,
- Unknown,
-};
-
// Nfc reply from the controller
enum class NfcState {
Success,
@@ -90,13 +93,6 @@ enum class NfcState {
Unknown,
};
-// Ir camera reply from the controller
-enum class CameraError {
- None,
- NotSupported,
- Unknown,
-};
-
// Hint for amplification curve to be used
enum class VibrationAmplificationType {
Linear,
@@ -134,6 +130,8 @@ struct ButtonStatus {
bool inverted{};
// Press once to activate, press again to release
bool toggle{};
+ // Spams the button when active
+ bool turbo{};
// Internal lock for the toggle status
bool locked{};
};
@@ -190,6 +188,8 @@ struct TouchStatus {
struct BodyColorStatus {
u32 body{};
u32 buttons{};
+ u32 left_grip{};
+ u32 right_grip{};
};
// HD rumble data
@@ -228,17 +228,31 @@ enum class ButtonNames {
Engine,
// This will display the button by value instead of the button name
Value,
+
+ // Joycon button names
ButtonLeft,
ButtonRight,
ButtonDown,
ButtonUp,
- TriggerZ,
- TriggerR,
- TriggerL,
ButtonA,
ButtonB,
ButtonX,
ButtonY,
+ ButtonPlus,
+ ButtonMinus,
+ ButtonHome,
+ ButtonCapture,
+ ButtonStickL,
+ ButtonStickR,
+ TriggerL,
+ TriggerZL,
+ TriggerSL,
+ TriggerR,
+ TriggerZR,
+ TriggerSR,
+
+ // GC button names
+ TriggerZ,
ButtonStart,
// DS4 button names
@@ -292,9 +306,6 @@ class InputDevice {
public:
virtual ~InputDevice() = default;
- // Request input device to update if necessary
- virtual void SoftUpdate() {}
-
// Force input device to update data regardless of the current state
virtual void ForceUpdate() {}
@@ -319,22 +330,24 @@ class OutputDevice {
public:
virtual ~OutputDevice() = default;
- virtual void SetLED([[maybe_unused]] const LedStatus& led_status) {}
+ virtual DriverResult SetLED([[maybe_unused]] const LedStatus& led_status) {
+ return DriverResult::NotSupported;
+ }
- virtual VibrationError SetVibration([[maybe_unused]] const VibrationStatus& vibration_status) {
- return VibrationError::NotSupported;
+ virtual DriverResult SetVibration([[maybe_unused]] const VibrationStatus& vibration_status) {
+ return DriverResult::NotSupported;
}
virtual bool IsVibrationEnabled() {
return false;
}
- virtual PollingError SetPollingMode([[maybe_unused]] PollingMode polling_mode) {
- return PollingError::NotSupported;
+ virtual DriverResult SetPollingMode([[maybe_unused]] PollingMode polling_mode) {
+ return DriverResult::NotSupported;
}
- virtual CameraError SetCameraFormat([[maybe_unused]] CameraFormat camera_format) {
- return CameraError::NotSupported;
+ virtual DriverResult SetCameraFormat([[maybe_unused]] CameraFormat camera_format) {
+ return DriverResult::NotSupported;
}
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:
template <typename T>
concept HasRedBlackKeyType = requires {
- { std::is_same<typename T::RedBlackKeyType, void>::value } -> std::convertible_to<bool>;
-};
+ {
+ std::is_same<typename T::RedBlackKeyType, void>::value
+ } -> std::convertible_to<bool>;
+ };
namespace impl {
- template <typename T, typename Default>
- consteval auto* GetRedBlackKeyType() {
- if constexpr (HasRedBlackKeyType<T>) {
- return static_cast<typename T::RedBlackKeyType*>(nullptr);
- } else {
- return static_cast<Default*>(nullptr);
- }
+template <typename T, typename Default>
+consteval auto* GetRedBlackKeyType() {
+ if constexpr (HasRedBlackKeyType<T>) {
+ return static_cast<typename T::RedBlackKeyType*>(nullptr);
+ } else {
+ return static_cast<Default*>(nullptr);
}
+}
} // namespace impl
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 @@
namespace Common {
template <class T>
-requires(!std::is_array_v<T>) std::unique_ptr<T> make_unique_for_overwrite() {
+ requires(!std::is_array_v<T>)
+std::unique_ptr<T> make_unique_for_overwrite() {
return std::unique_ptr<T>(new T);
}
template <class T>
-requires std::is_unbounded_array_v<T> std::unique_ptr<T> make_unique_for_overwrite(std::size_t n) {
+ requires std::is_unbounded_array_v<T>
+std::unique_ptr<T> make_unique_for_overwrite(std::size_t n) {
return std::unique_ptr<T>(new std::remove_extent_t<T>[n]);
}
template <class T, class... Args>
-requires std::is_bounded_array_v<T>
+ requires std::is_bounded_array_v<T>
void make_unique_for_overwrite(Args&&...) = delete;
} // 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 {
template <typename T>
concept range = requires(T& t) {
- begin(t);
- end(t);
-};
+ begin(t);
+ end(t);
+ };
template <typename T>
concept input_range = range<T>;
@@ -421,7 +421,7 @@ struct generate_fn {
}
template <typename R, std::copy_constructible F>
- requires std::invocable<F&> && ranges::output_range<R>
+ requires std::invocable<F&> && ranges::output_range<R>
constexpr ranges::iterator_t<R> operator()(R&& r, F gen) const {
return operator()(ranges::begin(r), ranges::end(r), std::move(gen));
}
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 @@
#ifdef __cpp_lib_jthread
+#include <chrono>
+#include <condition_variable>
#include <stop_token>
#include <thread>
@@ -21,23 +23,36 @@ void CondvarWait(Condvar& cv, Lock& lock, std::stop_token token, Pred&& pred) {
cv.wait(lock, token, std::move(pred));
}
+template <typename Rep, typename Period>
+bool StoppableTimedWait(std::stop_token token, const std::chrono::duration<Rep, Period>& rel_time) {
+ std::condition_variable_any cv;
+ std::mutex m;
+
+ // Perform the timed wait.
+ std::unique_lock lk{m};
+ return !cv.wait_for(lk, token, rel_time, [&] { return token.stop_requested(); });
+}
+
} // namespace Common
#else
#include <atomic>
+#include <chrono>
+#include <condition_variable>
#include <functional>
-#include <list>
+#include <map>
#include <memory>
#include <mutex>
#include <optional>
#include <thread>
#include <type_traits>
+#include <utility>
namespace std {
namespace polyfill {
-using stop_state_callbacks = list<function<void()>>;
+using stop_state_callback = size_t;
class stop_state {
public:
@@ -45,61 +60,69 @@ public:
~stop_state() = default;
bool request_stop() {
- stop_state_callbacks callbacks;
+ unique_lock lk{m_lock};
- {
- scoped_lock lk{m_lock};
+ if (m_stop_requested) {
+ // Already set, nothing to do.
+ return false;
+ }
- if (m_stop_requested.load()) {
- // Already set, nothing to do
- return false;
- }
+ // Mark stop requested.
+ m_stop_requested = true;
- // Set as requested
- m_stop_requested = true;
+ while (!m_callbacks.empty()) {
+ // Get an iterator to the first element.
+ const auto it = m_callbacks.begin();
- // Copy callback list
- callbacks = m_callbacks;
- }
+ // Move the callback function out of the map.
+ function<void()> f;
+ swap(it->second, f);
+
+ // Erase the now-empty map element.
+ m_callbacks.erase(it);
- for (auto callback : callbacks) {
- callback();
+ // Run the callback.
+ if (f) {
+ f();
+ }
}
return true;
}
bool stop_requested() const {
- return m_stop_requested.load();
+ unique_lock lk{m_lock};
+ return m_stop_requested;
}
- stop_state_callbacks::const_iterator insert_callback(function<void()> f) {
- stop_state_callbacks::const_iterator ret{};
- bool should_run{};
-
- {
- scoped_lock lk{m_lock};
- should_run = m_stop_requested.load();
- m_callbacks.push_front(f);
- ret = m_callbacks.begin();
- }
+ stop_state_callback insert_callback(function<void()> f) {
+ unique_lock lk{m_lock};
- if (should_run) {
- f();
+ if (m_stop_requested) {
+ // Stop already requested. Don't insert anything,
+ // just run the callback synchronously.
+ if (f) {
+ f();
+ }
+ return 0;
}
+ // Insert the callback.
+ stop_state_callback ret = ++m_next_callback;
+ m_callbacks.emplace(ret, move(f));
return ret;
}
- void remove_callback(stop_state_callbacks::const_iterator it) {
- scoped_lock lk{m_lock};
- m_callbacks.erase(it);
+ void remove_callback(stop_state_callback cb) {
+ unique_lock lk{m_lock};
+ m_callbacks.erase(cb);
}
private:
- mutex m_lock;
- atomic<bool> m_stop_requested;
- stop_state_callbacks m_callbacks;
+ mutable recursive_mutex m_lock;
+ map<stop_state_callback, function<void()>> m_callbacks;
+ stop_state_callback m_next_callback{0};
+ bool m_stop_requested{false};
};
} // namespace polyfill
@@ -190,7 +213,7 @@ public:
using callback_type = Callback;
template <typename C>
- requires constructible_from<Callback, C>
+ requires constructible_from<Callback, C>
explicit stop_callback(const stop_token& st,
C&& cb) noexcept(is_nothrow_constructible_v<Callback, C>)
: m_stop_state(st.m_stop_state) {
@@ -199,7 +222,7 @@ public:
}
}
template <typename C>
- requires constructible_from<Callback, C>
+ requires constructible_from<Callback, C>
explicit stop_callback(stop_token&& st,
C&& cb) noexcept(is_nothrow_constructible_v<Callback, C>)
: m_stop_state(move(st.m_stop_state)) {
@@ -209,7 +232,7 @@ public:
}
~stop_callback() {
if (m_stop_state && m_callback) {
- m_stop_state->remove_callback(*m_callback);
+ m_stop_state->remove_callback(m_callback);
}
}
@@ -220,7 +243,7 @@ public:
private:
shared_ptr<polyfill::stop_state> m_stop_state;
- optional<polyfill::stop_state_callbacks::const_iterator> m_callback;
+ polyfill::stop_state_callback m_callback;
};
template <typename Callback>
@@ -318,6 +341,28 @@ void CondvarWait(Condvar& cv, Lock& lock, std::stop_token token, Pred pred) {
cv.wait(lock, [&] { return pred() || token.stop_requested(); });
}
+template <typename Rep, typename Period>
+bool StoppableTimedWait(std::stop_token token, const std::chrono::duration<Rep, Period>& rel_time) {
+ if (token.stop_requested()) {
+ return false;
+ }
+
+ bool stop_requested = false;
+ std::condition_variable cv;
+ std::mutex m;
+
+ std::stop_callback cb(token, [&] {
+ // Wake up the waiting thread.
+ std::unique_lock lk{m};
+ stop_requested = true;
+ cv.notify_one();
+ });
+
+ // Perform the timed wait.
+ std::unique_lock lk{m};
+ return !cv.wait_for(lk, rel_time, [&] { return stop_requested; });
+}
+
} // namespace Common
#endif
diff --git a/src/common/settings.cpp b/src/common/settings.cpp
index 1638b79f5..49b41c158 100644
--- a/src/common/settings.cpp
+++ b/src/common/settings.cpp
@@ -129,6 +129,10 @@ void UpdateRescalingInfo() {
info.up_scale = 1;
info.down_shift = 0;
break;
+ case ResolutionSetup::Res3_2X:
+ info.up_scale = 3;
+ info.down_shift = 1;
+ break;
case ResolutionSetup::Res2X:
info.up_scale = 2;
info.down_shift = 0;
@@ -149,6 +153,14 @@ void UpdateRescalingInfo() {
info.up_scale = 6;
info.down_shift = 0;
break;
+ case ResolutionSetup::Res7X:
+ info.up_scale = 7;
+ info.down_shift = 0;
+ break;
+ case ResolutionSetup::Res8X:
+ info.up_scale = 8;
+ info.down_shift = 0;
+ break;
default:
ASSERT(false);
info.up_scale = 1;
@@ -187,7 +199,11 @@ void RestoreGlobalState(bool is_powered_on) {
values.renderer_backend.SetGlobal(true);
values.renderer_force_max_clock.SetGlobal(true);
values.vulkan_device.SetGlobal(true);
+ values.fullscreen_mode.SetGlobal(true);
values.aspect_ratio.SetGlobal(true);
+ values.resolution_setup.SetGlobal(true);
+ values.scaling_filter.SetGlobal(true);
+ values.anti_aliasing.SetGlobal(true);
values.max_anisotropy.SetGlobal(true);
values.use_speed_limit.SetGlobal(true);
values.speed_limit.SetGlobal(true);
diff --git a/src/common/settings.h b/src/common/settings.h
index 9eb3711ca..6d27dd5ee 100644
--- a/src/common/settings.h
+++ b/src/common/settings.h
@@ -56,11 +56,14 @@ enum class ResolutionSetup : u32 {
Res1_2X = 0,
Res3_4X = 1,
Res1X = 2,
- Res2X = 3,
- Res3X = 4,
- Res4X = 5,
- Res5X = 6,
- Res6X = 7,
+ Res3_2X = 3,
+ Res2X = 4,
+ Res3X = 5,
+ Res4X = 6,
+ Res5X = 7,
+ Res6X = 8,
+ Res7X = 9,
+ Res8X = 10,
};
enum class ScalingFilter : u32 {
@@ -128,7 +131,8 @@ public:
* @param default_val Intial value of the setting, and default value of the setting
* @param name Label for the setting
*/
- explicit Setting(const Type& default_val, const std::string& name) requires(!ranged)
+ explicit Setting(const Type& default_val, const std::string& name)
+ requires(!ranged)
: value{default_val}, default_value{default_val}, label{name} {}
virtual ~Setting() = default;
@@ -141,7 +145,8 @@ public:
* @param name Label for the setting
*/
explicit Setting(const Type& default_val, const Type& min_val, const Type& max_val,
- const std::string& name) requires(ranged)
+ const std::string& name)
+ requires(ranged)
: value{default_val},
default_value{default_val}, maximum{max_val}, minimum{min_val}, label{name} {}
@@ -229,7 +234,8 @@ public:
* @param default_val Intial value of the setting, and default value of the setting
* @param name Label for the setting
*/
- explicit SwitchableSetting(const Type& default_val, const std::string& name) requires(!ranged)
+ explicit SwitchableSetting(const Type& default_val, const std::string& name)
+ requires(!ranged)
: Setting<Type>{default_val, name} {}
virtual ~SwitchableSetting() = default;
@@ -242,7 +248,8 @@ public:
* @param name Label for the setting
*/
explicit SwitchableSetting(const Type& default_val, const Type& min_val, const Type& max_val,
- const std::string& name) requires(ranged)
+ const std::string& name)
+ requires(ranged)
: Setting<Type, true>{default_val, min_val, max_val, name} {}
/**
@@ -415,7 +422,7 @@ struct Values {
// Renderer
SwitchableSetting<RendererBackend, true> renderer_backend{
RendererBackend::Vulkan, RendererBackend::OpenGL, RendererBackend::Null, "backend"};
- SwitchableSetting<bool> renderer_force_max_clock{true, "force_max_clock"};
+ SwitchableSetting<bool> renderer_force_max_clock{false, "force_max_clock"};
Setting<bool> renderer_debug{false, "debug"};
Setting<bool> renderer_shader_feedback{false, "shader_feedback"};
Setting<bool> enable_nsight_aftermath{false, "nsight_aftermath"};
@@ -480,6 +487,8 @@ struct Values {
Setting<bool> enable_raw_input{false, "enable_raw_input"};
Setting<bool> controller_navigation{true, "controller_navigation"};
+ Setting<bool> enable_joycon_driver{true, "enable_joycon_driver"};
+ Setting<bool> enable_procon_driver{false, "enable_procon_driver"};
SwitchableSetting<bool> vibration_enabled{true, "vibration_enabled"};
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) {
return str;
}
-std::string StringFromBuffer(const std::vector<u8>& data) {
+std::string StringFromBuffer(std::span<const u8> data) {
return std::string(data.begin(), std::find(data.begin(), data.end(), '\0'));
}
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 @@
#pragma once
#include <cstddef>
+#include <span>
#include <string>
#include <vector>
#include "common/common_types.h"
@@ -17,7 +18,7 @@ namespace Common {
/// Make a string uppercase
[[nodiscard]] std::string ToUpper(std::string str);
-[[nodiscard]] std::string StringFromBuffer(const std::vector<u8>& data);
+[[nodiscard]] std::string StringFromBuffer(std::span<const u8> data);
[[nodiscard]] std::string StripSpaces(const std::string& s);
[[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;
template <typename T>
concept HasRBEntry = requires(T& t, const T& ct) {
- { t.GetRBEntry() } -> std::same_as<RBEntry<T>&>;
- { ct.GetRBEntry() } -> std::same_as<const RBEntry<T>&>;
-};
+ { t.GetRBEntry() } -> std::same_as<RBEntry<T>&>;
+ { ct.GetRBEntry() } -> std::same_as<const RBEntry<T>&>;
+ };
template <typename T>
-requires HasRBEntry<T>
+ requires HasRBEntry<T>
class RBHead {
private:
T* m_rbh_root = nullptr;
@@ -130,90 +130,90 @@ public:
};
template <typename T>
-requires HasRBEntry<T>
+ requires HasRBEntry<T>
[[nodiscard]] constexpr RBEntry<T>& RB_ENTRY(T* t) {
return t->GetRBEntry();
}
template <typename T>
-requires HasRBEntry<T>
+ requires HasRBEntry<T>
[[nodiscard]] constexpr const RBEntry<T>& RB_ENTRY(const T* t) {
return t->GetRBEntry();
}
template <typename T>
-requires HasRBEntry<T>
+ requires HasRBEntry<T>
[[nodiscard]] constexpr T* RB_LEFT(T* t) {
return RB_ENTRY(t).Left();
}
template <typename T>
-requires HasRBEntry<T>
+ requires HasRBEntry<T>
[[nodiscard]] constexpr const T* RB_LEFT(const T* t) {
return RB_ENTRY(t).Left();
}
template <typename T>
-requires HasRBEntry<T>
+ requires HasRBEntry<T>
[[nodiscard]] constexpr T* RB_RIGHT(T* t) {
return RB_ENTRY(t).Right();
}
template <typename T>
-requires HasRBEntry<T>
+ requires HasRBEntry<T>
[[nodiscard]] constexpr const T* RB_RIGHT(const T* t) {
return RB_ENTRY(t).Right();
}
template <typename T>
-requires HasRBEntry<T>
+ requires HasRBEntry<T>
[[nodiscard]] constexpr T* RB_PARENT(T* t) {
return RB_ENTRY(t).Parent();
}
template <typename T>
-requires HasRBEntry<T>
+ requires HasRBEntry<T>
[[nodiscard]] constexpr const T* RB_PARENT(const T* t) {
return RB_ENTRY(t).Parent();
}
template <typename T>
-requires HasRBEntry<T>
+ requires HasRBEntry<T>
constexpr void RB_SET_LEFT(T* t, T* e) {
RB_ENTRY(t).SetLeft(e);
}
template <typename T>
-requires HasRBEntry<T>
+ requires HasRBEntry<T>
constexpr void RB_SET_RIGHT(T* t, T* e) {
RB_ENTRY(t).SetRight(e);
}
template <typename T>
-requires HasRBEntry<T>
+ requires HasRBEntry<T>
constexpr void RB_SET_PARENT(T* t, T* e) {
RB_ENTRY(t).SetParent(e);
}
template <typename T>
-requires HasRBEntry<T>
+ requires HasRBEntry<T>
[[nodiscard]] constexpr bool RB_IS_BLACK(const T* t) {
return RB_ENTRY(t).IsBlack();
}
template <typename T>
-requires HasRBEntry<T>
+ requires HasRBEntry<T>
[[nodiscard]] constexpr bool RB_IS_RED(const T* t) {
return RB_ENTRY(t).IsRed();
}
template <typename T>
-requires HasRBEntry<T>
+ requires HasRBEntry<T>
[[nodiscard]] constexpr RBColor RB_COLOR(const T* t) {
return RB_ENTRY(t).Color();
}
template <typename T>
-requires HasRBEntry<T>
+ requires HasRBEntry<T>
constexpr void RB_SET_COLOR(T* t, RBColor c) {
RB_ENTRY(t).SetColor(c);
}
template <typename T>
-requires HasRBEntry<T>
+ requires HasRBEntry<T>
constexpr void RB_SET(T* elm, T* parent) {
auto& rb_entry = RB_ENTRY(elm);
rb_entry.SetParent(parent);
@@ -223,14 +223,14 @@ constexpr void RB_SET(T* elm, T* parent) {
}
template <typename T>
-requires HasRBEntry<T>
+ requires HasRBEntry<T>
constexpr void RB_SET_BLACKRED(T* black, T* red) {
RB_SET_COLOR(black, RBColor::RB_BLACK);
RB_SET_COLOR(red, RBColor::RB_RED);
}
template <typename T>
-requires HasRBEntry<T>
+ requires HasRBEntry<T>
constexpr void RB_ROTATE_LEFT(RBHead<T>& head, T* elm, T*& tmp) {
tmp = RB_RIGHT(elm);
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) {
}
template <typename T>
-requires HasRBEntry<T>
+ requires HasRBEntry<T>
constexpr void RB_ROTATE_RIGHT(RBHead<T>& head, T* elm, T*& tmp) {
tmp = RB_LEFT(elm);
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) {
}
template <typename T>
-requires HasRBEntry<T>
+ requires HasRBEntry<T>
constexpr void RB_REMOVE_COLOR(RBHead<T>& head, T* parent, T* elm) {
T* tmp;
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) {
}
template <typename T>
-requires HasRBEntry<T>
+ requires HasRBEntry<T>
constexpr T* RB_REMOVE(RBHead<T>& head, T* elm) {
T* child = nullptr;
T* parent = nullptr;
@@ -451,7 +451,7 @@ constexpr T* RB_REMOVE(RBHead<T>& head, T* elm) {
}
template <typename T>
-requires HasRBEntry<T>
+ requires HasRBEntry<T>
constexpr void RB_INSERT_COLOR(RBHead<T>& head, T* elm) {
T *parent = nullptr, *tmp = nullptr;
while ((parent = RB_PARENT(elm)) != nullptr && RB_IS_RED(parent)) {
@@ -499,7 +499,7 @@ constexpr void RB_INSERT_COLOR(RBHead<T>& head, T* elm) {
}
template <typename T, typename Compare>
-requires HasRBEntry<T>
+ requires HasRBEntry<T>
constexpr T* RB_INSERT(RBHead<T>& head, T* elm, Compare cmp) {
T* parent = nullptr;
T* tmp = head.Root();
@@ -534,7 +534,7 @@ constexpr T* RB_INSERT(RBHead<T>& head, T* elm, Compare cmp) {
}
template <typename T, typename Compare>
-requires HasRBEntry<T>
+ requires HasRBEntry<T>
constexpr T* RB_FIND(RBHead<T>& head, T* elm, Compare cmp) {
T* tmp = head.Root();
@@ -553,7 +553,7 @@ constexpr T* RB_FIND(RBHead<T>& head, T* elm, Compare cmp) {
}
template <typename T, typename Compare>
-requires HasRBEntry<T>
+ requires HasRBEntry<T>
constexpr T* RB_NFIND(RBHead<T>& head, T* elm, Compare cmp) {
T* tmp = head.Root();
T* res = nullptr;
@@ -574,7 +574,7 @@ constexpr T* RB_NFIND(RBHead<T>& head, T* elm, Compare cmp) {
}
template <typename T, typename U, typename Compare>
-requires HasRBEntry<T>
+ requires HasRBEntry<T>
constexpr T* RB_FIND_KEY(RBHead<T>& head, const U& key, Compare cmp) {
T* tmp = head.Root();
@@ -593,7 +593,7 @@ constexpr T* RB_FIND_KEY(RBHead<T>& head, const U& key, Compare cmp) {
}
template <typename T, typename U, typename Compare>
-requires HasRBEntry<T>
+ requires HasRBEntry<T>
constexpr T* RB_NFIND_KEY(RBHead<T>& head, const U& key, Compare cmp) {
T* tmp = head.Root();
T* res = nullptr;
@@ -614,7 +614,7 @@ constexpr T* RB_NFIND_KEY(RBHead<T>& head, const U& key, Compare cmp) {
}
template <typename T, typename Compare>
-requires HasRBEntry<T>
+ requires HasRBEntry<T>
constexpr T* RB_FIND_EXISTING(RBHead<T>& head, T* elm, Compare cmp) {
T* tmp = head.Root();
@@ -631,7 +631,7 @@ constexpr T* RB_FIND_EXISTING(RBHead<T>& head, T* elm, Compare cmp) {
}
template <typename T, typename U, typename Compare>
-requires HasRBEntry<T>
+ requires HasRBEntry<T>
constexpr T* RB_FIND_EXISTING_KEY(RBHead<T>& head, const U& key, Compare cmp) {
T* tmp = head.Root();
@@ -648,7 +648,7 @@ constexpr T* RB_FIND_EXISTING_KEY(RBHead<T>& head, const U& key, Compare cmp) {
}
template <typename T>
-requires HasRBEntry<T>
+ requires HasRBEntry<T>
constexpr T* RB_NEXT(T* elm) {
if (RB_RIGHT(elm)) {
elm = RB_RIGHT(elm);
@@ -669,7 +669,7 @@ constexpr T* RB_NEXT(T* elm) {
}
template <typename T>
-requires HasRBEntry<T>
+ requires HasRBEntry<T>
constexpr T* RB_PREV(T* elm) {
if (RB_LEFT(elm)) {
elm = RB_LEFT(elm);
@@ -690,7 +690,7 @@ constexpr T* RB_PREV(T* elm) {
}
template <typename T>
-requires HasRBEntry<T>
+ requires HasRBEntry<T>
constexpr T* RB_MIN(RBHead<T>& head) {
T* tmp = head.Root();
T* parent = nullptr;
@@ -704,7 +704,7 @@ constexpr T* RB_MIN(RBHead<T>& head) {
}
template <typename T>
-requires HasRBEntry<T>
+ requires HasRBEntry<T>
constexpr T* RB_MAX(RBHead<T>& head) {
T* tmp = head.Root();
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:
// _DEFINE_SWIZZLER2 defines a single such function, DEFINE_SWIZZLER2 defines all of them for all
// component names (x<->r) and permutations (xy<->yx)
#define _DEFINE_SWIZZLER2(a, b, name) \
- [[nodiscard]] constexpr Vec2<T> name() const { \
- return Vec2<T>(a, b); \
- }
+ [[nodiscard]] constexpr Vec2<T> name() const { return Vec2<T>(a, b); }
#define DEFINE_SWIZZLER2(a, b, a2, b2, a3, b3, a4, b4) \
_DEFINE_SWIZZLER2(a, b, a##b); \
_DEFINE_SWIZZLER2(a, b, a2##b2); \
@@ -543,9 +541,7 @@ public:
// DEFINE_SWIZZLER2_COMP2 defines two component functions for all component names (x<->r) and
// permutations (xy<->yx)
#define _DEFINE_SWIZZLER2(a, b, name) \
- [[nodiscard]] constexpr Vec2<T> name() const { \
- return Vec2<T>(a, b); \
- }
+ [[nodiscard]] constexpr Vec2<T> name() const { return Vec2<T>(a, b); }
#define DEFINE_SWIZZLER2_COMP1(a, a2) \
_DEFINE_SWIZZLER2(a, a, a##a); \
_DEFINE_SWIZZLER2(a, a, a2##a2)
@@ -570,9 +566,7 @@ public:
#undef _DEFINE_SWIZZLER2
#define _DEFINE_SWIZZLER3(a, b, c, name) \
- [[nodiscard]] constexpr Vec3<T> name() const { \
- return Vec3<T>(a, b, c); \
- }
+ [[nodiscard]] constexpr Vec3<T> name() const { return Vec3<T>(a, b, c); }
#define DEFINE_SWIZZLER3_COMP1(a, a2) \
_DEFINE_SWIZZLER3(a, a, a, a##a##a); \
_DEFINE_SWIZZLER3(a, a, a, a2##a2##a2)
@@ -641,8 +635,8 @@ template <typename T>
// linear interpolation via float: 0.0=begin, 1.0=end
template <typename X>
-[[nodiscard]] constexpr decltype(X{} * float{} + X{} * float{})
- Lerp(const X& begin, const X& end, const float t) {
+[[nodiscard]] constexpr decltype(X{} * float{} + X{} * float{}) Lerp(const X& begin, const X& end,
+ const float t) {
return begin * (1.f - t) + end * t;
}
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt
index 0252c8c31..ff5502d87 100644
--- a/src/core/CMakeLists.txt
+++ b/src/core/CMakeLists.txt
@@ -182,6 +182,8 @@ add_library(core STATIC
hle/kernel/k_auto_object_container.cpp
hle/kernel/k_auto_object_container.h
hle/kernel/k_affinity_mask.h
+ hle/kernel/k_capabilities.cpp
+ hle/kernel/k_capabilities.h
hle/kernel/k_class_token.cpp
hle/kernel/k_class_token.h
hle/kernel/k_client_port.cpp
@@ -193,6 +195,8 @@ add_library(core STATIC
hle/kernel/k_condition_variable.cpp
hle/kernel/k_condition_variable.h
hle/kernel/k_debug.h
+ hle/kernel/k_device_address_space.cpp
+ hle/kernel/k_device_address_space.h
hle/kernel/k_dynamic_page_manager.h
hle/kernel/k_dynamic_resource_manager.h
hle/kernel/k_dynamic_slab_heap.h
@@ -221,11 +225,14 @@ add_library(core STATIC
hle/kernel/k_memory_manager.h
hle/kernel/k_memory_region.h
hle/kernel/k_memory_region_type.h
+ hle/kernel/k_object_name.cpp
+ hle/kernel/k_object_name.h
hle/kernel/k_page_bitmap.h
hle/kernel/k_page_buffer.cpp
hle/kernel/k_page_buffer.h
hle/kernel/k_page_heap.cpp
hle/kernel/k_page_heap.h
+ hle/kernel/k_page_group.cpp
hle/kernel/k_page_group.h
hle/kernel/k_page_table.cpp
hle/kernel/k_page_table.h
@@ -293,7 +300,43 @@ add_library(core STATIC
hle/kernel/svc.h
hle/kernel/svc_common.h
hle/kernel/svc_types.h
- hle/kernel/svc_wrap.h
+ hle/kernel/svc/svc_activity.cpp
+ hle/kernel/svc/svc_address_arbiter.cpp
+ hle/kernel/svc/svc_address_translation.cpp
+ hle/kernel/svc/svc_cache.cpp
+ hle/kernel/svc/svc_code_memory.cpp
+ hle/kernel/svc/svc_condition_variable.cpp
+ hle/kernel/svc/svc_debug.cpp
+ hle/kernel/svc/svc_debug_string.cpp
+ hle/kernel/svc/svc_device_address_space.cpp
+ hle/kernel/svc/svc_event.cpp
+ hle/kernel/svc/svc_exception.cpp
+ hle/kernel/svc/svc_info.cpp
+ hle/kernel/svc/svc_insecure_memory.cpp
+ hle/kernel/svc/svc_interrupt_event.cpp
+ hle/kernel/svc/svc_io_pool.cpp
+ hle/kernel/svc/svc_ipc.cpp
+ hle/kernel/svc/svc_kernel_debug.cpp
+ hle/kernel/svc/svc_light_ipc.cpp
+ hle/kernel/svc/svc_lock.cpp
+ hle/kernel/svc/svc_memory.cpp
+ hle/kernel/svc/svc_physical_memory.cpp
+ hle/kernel/svc/svc_port.cpp
+ hle/kernel/svc/svc_power_management.cpp
+ hle/kernel/svc/svc_process.cpp
+ hle/kernel/svc/svc_process_memory.cpp
+ hle/kernel/svc/svc_processor.cpp
+ hle/kernel/svc/svc_query_memory.cpp
+ hle/kernel/svc/svc_register.cpp
+ hle/kernel/svc/svc_resource_limit.cpp
+ hle/kernel/svc/svc_secure_monitor_call.cpp
+ hle/kernel/svc/svc_session.cpp
+ hle/kernel/svc/svc_shared_memory.cpp
+ hle/kernel/svc/svc_synchronization.cpp
+ hle/kernel/svc/svc_thread.cpp
+ hle/kernel/svc/svc_thread_profiler.cpp
+ hle/kernel/svc/svc_tick.cpp
+ hle/kernel/svc/svc_transfer_memory.cpp
hle/result.h
hle/service/acc/acc.cpp
hle/service/acc/acc.h
@@ -343,8 +386,6 @@ add_library(core STATIC
hle/service/am/omm.h
hle/service/am/spsm.cpp
hle/service/am/spsm.h
- hle/service/am/tcap.cpp
- hle/service/am/tcap.h
hle/service/aoc/aoc_u.cpp
hle/service/aoc/aoc_u.h
hle/service/apm/apm.cpp
@@ -355,28 +396,18 @@ add_library(core STATIC
hle/service/apm/apm_interface.h
hle/service/audio/audctl.cpp
hle/service/audio/audctl.h
- hle/service/audio/auddbg.cpp
- hle/service/audio/auddbg.h
- hle/service/audio/audin_a.cpp
- hle/service/audio/audin_a.h
hle/service/audio/audin_u.cpp
hle/service/audio/audin_u.h
hle/service/audio/audio.cpp
hle/service/audio/audio.h
- hle/service/audio/audout_a.cpp
- hle/service/audio/audout_a.h
hle/service/audio/audout_u.cpp
hle/service/audio/audout_u.h
hle/service/audio/audrec_a.cpp
hle/service/audio/audrec_a.h
hle/service/audio/audrec_u.cpp
hle/service/audio/audrec_u.h
- hle/service/audio/audren_a.cpp
- hle/service/audio/audren_a.h
hle/service/audio/audren_u.cpp
hle/service/audio/audren_u.h
- hle/service/audio/codecctl.cpp
- hle/service/audio/codecctl.h
hle/service/audio/errors.h
hle/service/audio/hwopus.cpp
hle/service/audio/hwopus.h
@@ -671,8 +702,6 @@ add_library(core STATIC
hle/service/sm/sm_controller.h
hle/service/sockets/bsd.cpp
hle/service/sockets/bsd.h
- hle/service/sockets/ethc.cpp
- hle/service/sockets/ethc.h
hle/service/sockets/nsd.cpp
hle/service/sockets/nsd.h
hle/service/sockets/sfdnsres.cpp
@@ -739,8 +768,6 @@ add_library(core STATIC
hle/service/vi/vi_s.h
hle/service/vi/vi_u.cpp
hle/service/vi/vi_u.h
- hle/service/wlan/wlan.cpp
- hle/service/wlan/wlan.h
internal_network/network.cpp
internal_network/network.h
internal_network/network_interface.cpp
diff --git a/src/core/arm/arm_interface.cpp b/src/core/arm/arm_interface.cpp
index 2df7b0ee8..4a331d4c1 100644
--- a/src/core/arm/arm_interface.cpp
+++ b/src/core/arm/arm_interface.cpp
@@ -1,14 +1,12 @@
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
-#ifndef _MSC_VER
-#include <cxxabi.h>
-#endif
-
#include <map>
#include <optional>
+
#include "common/bit_field.h"
#include "common/common_types.h"
+#include "common/demangle.h"
#include "common/logging/log.h"
#include "core/arm/arm_interface.h"
#include "core/arm/symbols.h"
@@ -45,9 +43,9 @@ void ARM_Interface::SymbolicateBacktrace(Core::System& system, std::vector<Backt
std::map<std::string, Symbols::Symbols> symbols;
for (const auto& module : modules) {
- symbols.insert_or_assign(module.second,
- Symbols::GetSymbols(module.first, system.Memory(),
- system.CurrentProcess()->Is64BitProcess()));
+ symbols.insert_or_assign(
+ module.second, Symbols::GetSymbols(module.first, system.Memory(),
+ system.ApplicationProcess()->Is64BitProcess()));
}
for (auto& entry : out) {
@@ -71,20 +69,8 @@ void ARM_Interface::SymbolicateBacktrace(Core::System& system, std::vector<Backt
const auto symbol_set = symbols.find(entry.module);
if (symbol_set != symbols.end()) {
const auto symbol = Symbols::GetSymbolName(symbol_set->second, entry.offset);
- if (symbol.has_value()) {
-#ifdef _MSC_VER
- // TODO(DarkLordZach): Add demangling of symbol names.
- entry.name = *symbol;
-#else
- int status{-1};
- char* demangled{abi::__cxa_demangle(symbol->c_str(), nullptr, nullptr, &status)};
- if (status == 0 && demangled != nullptr) {
- entry.name = demangled;
- std::free(demangled);
- } else {
- entry.name = *symbol;
- }
-#endif
+ if (symbol) {
+ entry.name = Common::DemangleSymbol(*symbol);
}
}
}
diff --git a/src/core/arm/arm_interface.h b/src/core/arm/arm_interface.h
index 7d62d030e..c40771c97 100644
--- a/src/core/arm/arm_interface.h
+++ b/src/core/arm/arm_interface.h
@@ -5,6 +5,7 @@
#include <array>
#include <span>
+#include <string>
#include <vector>
#include <dynarmic/interface/halt_reason.h>
diff --git a/src/core/core.cpp b/src/core/core.cpp
index 47292cd78..fb9b25d12 100644
--- a/src/core/core.cpp
+++ b/src/core/core.cpp
@@ -186,7 +186,7 @@ struct System::Impl {
void Run() {
std::unique_lock<std::mutex> lk(suspend_guard);
- kernel.Suspend(false);
+ kernel.SuspendApplication(false);
core_timing.SyncPause(false);
is_paused.store(false, std::memory_order_relaxed);
}
@@ -195,7 +195,7 @@ struct System::Impl {
std::unique_lock<std::mutex> lk(suspend_guard);
core_timing.SyncPause(true);
- kernel.Suspend(true);
+ kernel.SuspendApplication(true);
is_paused.store(true, std::memory_order_relaxed);
}
@@ -203,17 +203,17 @@ struct System::Impl {
return is_paused.load(std::memory_order_relaxed);
}
- std::unique_lock<std::mutex> StallProcesses() {
+ std::unique_lock<std::mutex> StallApplication() {
std::unique_lock<std::mutex> lk(suspend_guard);
- kernel.Suspend(true);
+ kernel.SuspendApplication(true);
core_timing.SyncPause(true);
return lk;
}
- void UnstallProcesses() {
+ void UnstallApplication() {
if (!IsPaused()) {
core_timing.SyncPause(false);
- kernel.Suspend(false);
+ kernel.SuspendApplication(false);
}
}
@@ -221,7 +221,7 @@ struct System::Impl {
debugger = std::make_unique<Debugger>(system, port);
}
- SystemResultStatus SetupForMainProcess(System& system, Frontend::EmuWindow& emu_window) {
+ SystemResultStatus SetupForApplicationProcess(System& system, Frontend::EmuWindow& emu_window) {
LOG_DEBUG(Core, "initialized OK");
// Setting changes may require a full system reinitialization (e.g., disabling multicore).
@@ -273,7 +273,7 @@ struct System::Impl {
return SystemResultStatus::ErrorGetLoader;
}
- SystemResultStatus init_result{SetupForMainProcess(system, emu_window)};
+ SystemResultStatus init_result{SetupForApplicationProcess(system, emu_window)};
if (init_result != SystemResultStatus::Success) {
LOG_CRITICAL(Core, "Failed to initialize system (Error {})!",
static_cast<int>(init_result));
@@ -302,7 +302,7 @@ struct System::Impl {
static_cast<u32>(SystemResultStatus::ErrorLoader) + static_cast<u32>(load_result));
}
AddGlueRegistrationForProcess(*app_loader, *main_process);
- kernel.MakeCurrentProcess(main_process);
+ kernel.MakeApplicationProcess(main_process);
kernel.InitializeCores();
// Initialize cheat engine
@@ -585,12 +585,12 @@ void System::DetachDebugger() {
}
}
-std::unique_lock<std::mutex> System::StallProcesses() {
- return impl->StallProcesses();
+std::unique_lock<std::mutex> System::StallApplication() {
+ return impl->StallApplication();
}
-void System::UnstallProcesses() {
- impl->UnstallProcesses();
+void System::UnstallApplication() {
+ impl->UnstallApplication();
}
void System::InitializeDebugger() {
@@ -648,8 +648,8 @@ const Kernel::GlobalSchedulerContext& System::GlobalSchedulerContext() const {
return impl->kernel.GlobalSchedulerContext();
}
-Kernel::KProcess* System::CurrentProcess() {
- return impl->kernel.CurrentProcess();
+Kernel::KProcess* System::ApplicationProcess() {
+ return impl->kernel.ApplicationProcess();
}
Core::DeviceMemory& System::DeviceMemory() {
@@ -660,8 +660,8 @@ const Core::DeviceMemory& System::DeviceMemory() const {
return *impl->device_memory;
}
-const Kernel::KProcess* System::CurrentProcess() const {
- return impl->kernel.CurrentProcess();
+const Kernel::KProcess* System::ApplicationProcess() const {
+ return impl->kernel.ApplicationProcess();
}
ARM_Interface& System::ArmInterface(std::size_t core_index) {
@@ -760,8 +760,8 @@ const Core::SpeedLimiter& System::SpeedLimiter() const {
return impl->speed_limiter;
}
-u64 System::GetCurrentProcessProgramID() const {
- return impl->kernel.CurrentProcess()->GetProgramID();
+u64 System::GetApplicationProcessProgramID() const {
+ return impl->kernel.ApplicationProcess()->GetProgramID();
}
Loader::ResultStatus System::GetGameName(std::string& out) const {
@@ -880,11 +880,11 @@ bool System::GetExitLock() const {
return impl->exit_lock;
}
-void System::SetCurrentProcessBuildID(const CurrentBuildProcessID& id) {
+void System::SetApplicationProcessBuildID(const CurrentBuildProcessID& id) {
impl->build_id = id;
}
-const System::CurrentBuildProcessID& System::GetCurrentProcessBuildID() const {
+const System::CurrentBuildProcessID& System::GetApplicationProcessBuildID() const {
return impl->build_id;
}
diff --git a/src/core/core.h b/src/core/core.h
index fb5cda2f5..0042ac170 100644
--- a/src/core/core.h
+++ b/src/core/core.h
@@ -184,8 +184,8 @@ public:
/// Forcibly detach the debugger if it is running.
void DetachDebugger();
- std::unique_lock<std::mutex> StallProcesses();
- void UnstallProcesses();
+ std::unique_lock<std::mutex> StallApplication();
+ void UnstallApplication();
/**
* Initialize the debugger.
@@ -295,11 +295,11 @@ public:
/// Gets the manager for the guest device memory
[[nodiscard]] const Core::DeviceMemory& DeviceMemory() const;
- /// Provides a pointer to the current process
- [[nodiscard]] Kernel::KProcess* CurrentProcess();
+ /// Provides a pointer to the application process
+ [[nodiscard]] Kernel::KProcess* ApplicationProcess();
- /// Provides a constant pointer to the current process.
- [[nodiscard]] const Kernel::KProcess* CurrentProcess() const;
+ /// Provides a constant pointer to the application process.
+ [[nodiscard]] const Kernel::KProcess* ApplicationProcess() const;
/// Provides a reference to the core timing instance.
[[nodiscard]] Timing::CoreTiming& CoreTiming();
@@ -331,7 +331,7 @@ public:
/// Provides a constant reference to the speed limiter
[[nodiscard]] const Core::SpeedLimiter& SpeedLimiter() const;
- [[nodiscard]] u64 GetCurrentProcessProgramID() const;
+ [[nodiscard]] u64 GetApplicationProcessProgramID() const;
/// Gets the name of the current game
[[nodiscard]] Loader::ResultStatus GetGameName(std::string& out) const;
@@ -396,8 +396,8 @@ public:
void SetExitLock(bool locked);
[[nodiscard]] bool GetExitLock() const;
- void SetCurrentProcessBuildID(const CurrentBuildProcessID& id);
- [[nodiscard]] const CurrentBuildProcessID& GetCurrentProcessBuildID() const;
+ void SetApplicationProcessBuildID(const CurrentBuildProcessID& id);
+ [[nodiscard]] const CurrentBuildProcessID& GetApplicationProcessBuildID() const;
/// Register a host thread as an emulated CPU Core.
void RegisterCoreThread(std::size_t id);
diff --git a/src/core/core_timing.cpp b/src/core/core_timing.cpp
index 0e7b5f943..3a63b52e3 100644
--- a/src/core/core_timing.cpp
+++ b/src/core/core_timing.cpp
@@ -45,7 +45,7 @@ CoreTiming::~CoreTiming() {
}
void CoreTiming::ThreadEntry(CoreTiming& instance) {
- constexpr char name[] = "HostTiming";
+ static constexpr char name[] = "HostTiming";
MicroProfileOnThreadCreate(name);
Common::SetCurrentThreadName(name);
Common::SetCurrentThreadPriority(Common::ThreadPriority::Critical);
@@ -142,16 +142,24 @@ void CoreTiming::ScheduleLoopingEvent(std::chrono::nanoseconds start_time,
}
void CoreTiming::UnscheduleEvent(const std::shared_ptr<EventType>& event_type,
- std::uintptr_t user_data) {
- std::scoped_lock scope{basic_lock};
- const auto itr = std::remove_if(event_queue.begin(), event_queue.end(), [&](const Event& e) {
- return e.type.lock().get() == event_type.get() && e.user_data == user_data;
- });
-
- // Removing random items breaks the invariant so we have to re-establish it.
- if (itr != event_queue.end()) {
- event_queue.erase(itr, event_queue.end());
- std::make_heap(event_queue.begin(), event_queue.end(), std::greater<>());
+ std::uintptr_t user_data, bool wait) {
+ {
+ std::scoped_lock lk{basic_lock};
+ const auto itr =
+ std::remove_if(event_queue.begin(), event_queue.end(), [&](const Event& e) {
+ return e.type.lock().get() == event_type.get() && e.user_data == user_data;
+ });
+
+ // Removing random items breaks the invariant so we have to re-establish it.
+ if (itr != event_queue.end()) {
+ event_queue.erase(itr, event_queue.end());
+ std::make_heap(event_queue.begin(), event_queue.end(), std::greater<>());
+ }
+ }
+
+ // Force any in-progress events to finish
+ if (wait) {
+ std::scoped_lock lk{advance_lock};
}
}
@@ -190,20 +198,6 @@ u64 CoreTiming::GetClockTicks() const {
return CpuCyclesToClockCycles(ticks);
}
-void CoreTiming::RemoveEvent(const std::shared_ptr<EventType>& event_type) {
- std::scoped_lock lock{basic_lock};
-
- const auto itr = std::remove_if(event_queue.begin(), event_queue.end(), [&](const Event& e) {
- return e.type.lock().get() == event_type.get();
- });
-
- // Removing random items breaks the invariant so we have to re-establish it.
- if (itr != event_queue.end()) {
- event_queue.erase(itr, event_queue.end());
- std::make_heap(event_queue.begin(), event_queue.end(), std::greater<>());
- }
-}
-
std::optional<s64> CoreTiming::Advance() {
std::scoped_lock lock{advance_lock, basic_lock};
global_timer = GetGlobalTimeNs().count();
diff --git a/src/core/core_timing.h b/src/core/core_timing.h
index b5925193c..da366637b 100644
--- a/src/core/core_timing.h
+++ b/src/core/core_timing.h
@@ -98,10 +98,13 @@ public:
const std::shared_ptr<EventType>& event_type,
std::uintptr_t user_data = 0, bool absolute_time = false);
- void UnscheduleEvent(const std::shared_ptr<EventType>& event_type, std::uintptr_t user_data);
+ void UnscheduleEvent(const std::shared_ptr<EventType>& event_type, std::uintptr_t user_data,
+ bool wait = true);
- /// We only permit one event of each type in the queue at a time.
- void RemoveEvent(const std::shared_ptr<EventType>& event_type);
+ void UnscheduleEventWithoutWait(const std::shared_ptr<EventType>& event_type,
+ std::uintptr_t user_data) {
+ UnscheduleEvent(event_type, user_data, false);
+ }
void AddTicks(u64 ticks_to_add);
diff --git a/src/core/debugger/gdbstub.cpp b/src/core/debugger/gdbstub.cpp
index a64a9ac64..945ec528e 100644
--- a/src/core/debugger/gdbstub.cpp
+++ b/src/core/debugger/gdbstub.cpp
@@ -11,6 +11,7 @@
#include "common/hex_util.h"
#include "common/logging/log.h"
#include "common/scope_exit.h"
+#include "common/settings.h"
#include "core/arm/arm_interface.h"
#include "core/core.h"
#include "core/debugger/gdbstub.h"
@@ -95,7 +96,7 @@ static std::string EscapeXML(std::string_view data) {
GDBStub::GDBStub(DebuggerBackend& backend_, Core::System& system_)
: DebuggerFrontend(backend_), system{system_} {
- if (system.CurrentProcess()->Is64BitProcess()) {
+ if (system.ApplicationProcess()->Is64BitProcess()) {
arch = std::make_unique<GDBStubA64>();
} else {
arch = std::make_unique<GDBStubA32>();
@@ -339,15 +340,15 @@ void GDBStub::HandleBreakpointInsert(std::string_view command) {
success = true;
break;
case BreakpointType::WriteWatch:
- success = system.CurrentProcess()->InsertWatchpoint(system, addr, size,
- Kernel::DebugWatchpointType::Write);
+ success = system.ApplicationProcess()->InsertWatchpoint(system, addr, size,
+ Kernel::DebugWatchpointType::Write);
break;
case BreakpointType::ReadWatch:
- success = system.CurrentProcess()->InsertWatchpoint(system, addr, size,
- Kernel::DebugWatchpointType::Read);
+ success = system.ApplicationProcess()->InsertWatchpoint(system, addr, size,
+ Kernel::DebugWatchpointType::Read);
break;
case BreakpointType::AccessWatch:
- success = system.CurrentProcess()->InsertWatchpoint(
+ success = system.ApplicationProcess()->InsertWatchpoint(
system, addr, size, Kernel::DebugWatchpointType::ReadOrWrite);
break;
case BreakpointType::Hardware:
@@ -390,15 +391,15 @@ void GDBStub::HandleBreakpointRemove(std::string_view command) {
break;
}
case BreakpointType::WriteWatch:
- success = system.CurrentProcess()->RemoveWatchpoint(system, addr, size,
- Kernel::DebugWatchpointType::Write);
+ success = system.ApplicationProcess()->RemoveWatchpoint(system, addr, size,
+ Kernel::DebugWatchpointType::Write);
break;
case BreakpointType::ReadWatch:
- success = system.CurrentProcess()->RemoveWatchpoint(system, addr, size,
- Kernel::DebugWatchpointType::Read);
+ success = system.ApplicationProcess()->RemoveWatchpoint(system, addr, size,
+ Kernel::DebugWatchpointType::Read);
break;
case BreakpointType::AccessWatch:
- success = system.CurrentProcess()->RemoveWatchpoint(
+ success = system.ApplicationProcess()->RemoveWatchpoint(
system, addr, size, Kernel::DebugWatchpointType::ReadOrWrite);
break;
case BreakpointType::Hardware:
@@ -481,7 +482,7 @@ static std::optional<std::string> GetNameFromThreadType64(Core::Memory::Memory&
static std::optional<std::string> GetThreadName(Core::System& system,
const Kernel::KThread* thread) {
- if (system.CurrentProcess()->Is64BitProcess()) {
+ if (system.ApplicationProcess()->Is64BitProcess()) {
return GetNameFromThreadType64(system.Memory(), thread);
} else {
return GetNameFromThreadType32(system.Memory(), thread);
@@ -554,7 +555,7 @@ void GDBStub::HandleQuery(std::string_view command) {
SendReply(fmt::format("TextSeg={:x}", main->first));
} else {
SendReply(fmt::format("TextSeg={:x}",
- system.CurrentProcess()->PageTable().GetCodeRegionStart()));
+ system.ApplicationProcess()->PageTable().GetCodeRegionStart()));
}
} else if (command.starts_with("Xfer:libraries:read::")) {
Loader::AppLoader::Modules modules;
@@ -728,10 +729,28 @@ void GDBStub::HandleRcmd(const std::vector<u8>& command) {
std::string_view command_str{reinterpret_cast<const char*>(&command[0]), command.size()};
std::string reply;
- auto* process = system.CurrentProcess();
+ auto* process = system.ApplicationProcess();
auto& page_table = process->PageTable();
- if (command_str == "get info") {
+ const char* commands = "Commands:\n"
+ " get fastmem\n"
+ " get info\n"
+ " get mappings\n";
+
+ if (command_str == "get fastmem") {
+ if (Settings::IsFastmemEnabled()) {
+ const auto& impl = page_table.PageTableImpl();
+ const auto region = reinterpret_cast<uintptr_t>(impl.fastmem_arena);
+ const auto region_bits = impl.current_address_space_width_in_bits;
+ const auto region_size = 1ULL << region_bits;
+
+ reply = fmt::format("Region bits: {}\n"
+ "Host address: {:#x} - {:#x}\n",
+ region_bits, region, region + region_size - 1);
+ } else {
+ reply = "Fastmem is not enabled.\n";
+ }
+ } else if (command_str == "get info") {
Loader::AppLoader::Modules modules;
system.GetAppLoader().ReadNSOModules(modules);
@@ -787,9 +806,10 @@ void GDBStub::HandleRcmd(const std::vector<u8>& command) {
cur_addr = next_address;
}
} else if (command_str == "help") {
- reply = "Commands:\n get info\n get mappings\n";
+ reply = commands;
} else {
- reply = "Unknown command.\nCommands:\n get info\n get mappings\n";
+ reply = "Unknown command.\n";
+ reply += commands;
}
std::span<const u8> reply_span{reinterpret_cast<u8*>(&reply.front()), reply.size()};
diff --git a/src/core/debugger/gdbstub_arch.cpp b/src/core/debugger/gdbstub_arch.cpp
index 4bef09bd7..831c48513 100644
--- a/src/core/debugger/gdbstub_arch.cpp
+++ b/src/core/debugger/gdbstub_arch.cpp
@@ -41,9 +41,8 @@ static void PutSIMDRegister(std::array<u32, 64>& simd_regs, size_t offset, const
// For sample XML files see the GDB source /gdb/features
// This XML defines what the registers are for this specific ARM device
-std::string GDBStubA64::GetTargetXML() const {
- constexpr const char* target_xml =
- R"(<?xml version="1.0"?>
+std::string_view GDBStubA64::GetTargetXML() const {
+ return R"(<?xml version="1.0"?>
<!DOCTYPE target SYSTEM "gdb-target.dtd">
<target version="1.0">
<architecture>aarch64</architecture>
@@ -178,8 +177,6 @@ std::string GDBStubA64::GetTargetXML() const {
<reg name="fpcr" bitsize="32"/>
</feature>
</target>)";
-
- return target_xml;
}
std::string GDBStubA64::RegRead(const Kernel::KThread* thread, size_t id) const {
@@ -270,9 +267,8 @@ u32 GDBStubA64::BreakpointInstruction() const {
return 0xd4200000;
}
-std::string GDBStubA32::GetTargetXML() const {
- constexpr const char* target_xml =
- R"(<?xml version="1.0"?>
+std::string_view GDBStubA32::GetTargetXML() const {
+ return R"(<?xml version="1.0"?>
<!DOCTYPE target SYSTEM "gdb-target.dtd">
<target version="1.0">
<architecture>arm</architecture>
@@ -378,8 +374,6 @@ std::string GDBStubA32::GetTargetXML() const {
<reg name="fpscr" bitsize="32" type="int" group="float" regnum="80"/>
</feature>
</target>)";
-
- return target_xml;
}
std::string GDBStubA32::RegRead(const Kernel::KThread* thread, size_t id) const {
diff --git a/src/core/debugger/gdbstub_arch.h b/src/core/debugger/gdbstub_arch.h
index 2540d6456..34530c788 100644
--- a/src/core/debugger/gdbstub_arch.h
+++ b/src/core/debugger/gdbstub_arch.h
@@ -16,7 +16,7 @@ namespace Core {
class GDBStubArch {
public:
virtual ~GDBStubArch() = default;
- virtual std::string GetTargetXML() const = 0;
+ virtual std::string_view GetTargetXML() const = 0;
virtual std::string RegRead(const Kernel::KThread* thread, size_t id) const = 0;
virtual void RegWrite(Kernel::KThread* thread, size_t id, std::string_view value) const = 0;
virtual std::string ReadRegisters(const Kernel::KThread* thread) const = 0;
@@ -27,7 +27,7 @@ public:
class GDBStubA64 final : public GDBStubArch {
public:
- std::string GetTargetXML() const override;
+ std::string_view GetTargetXML() const override;
std::string RegRead(const Kernel::KThread* thread, size_t id) const override;
void RegWrite(Kernel::KThread* thread, size_t id, std::string_view value) const override;
std::string ReadRegisters(const Kernel::KThread* thread) const override;
@@ -47,7 +47,7 @@ private:
class GDBStubA32 final : public GDBStubArch {
public:
- std::string GetTargetXML() const override;
+ std::string_view GetTargetXML() const override;
std::string RegRead(const Kernel::KThread* thread, size_t id) const override;
void RegWrite(Kernel::KThread* thread, size_t id, std::string_view value) const override;
std::string ReadRegisters(const Kernel::KThread* thread) const override;
diff --git a/src/core/file_sys/ips_layer.cpp b/src/core/file_sys/ips_layer.cpp
index 5aab428bb..efdf18cee 100644
--- a/src/core/file_sys/ips_layer.cpp
+++ b/src/core/file_sys/ips_layer.cpp
@@ -41,12 +41,12 @@ static IPSFileType IdentifyMagic(const std::vector<u8>& magic) {
return IPSFileType::Error;
}
- constexpr std::array<u8, 5> patch_magic{{'P', 'A', 'T', 'C', 'H'}};
+ static constexpr std::array<u8, 5> patch_magic{{'P', 'A', 'T', 'C', 'H'}};
if (std::equal(magic.begin(), magic.end(), patch_magic.begin())) {
return IPSFileType::IPS;
}
- constexpr std::array<u8, 5> ips32_magic{{'I', 'P', 'S', '3', '2'}};
+ static constexpr std::array<u8, 5> ips32_magic{{'I', 'P', 'S', '3', '2'}};
if (std::equal(magic.begin(), magic.end(), ips32_magic.begin())) {
return IPSFileType::IPS32;
}
@@ -55,12 +55,12 @@ static IPSFileType IdentifyMagic(const std::vector<u8>& magic) {
}
static bool IsEOF(IPSFileType type, const std::vector<u8>& data) {
- constexpr std::array<u8, 3> eof{{'E', 'O', 'F'}};
+ static constexpr std::array<u8, 3> eof{{'E', 'O', 'F'}};
if (type == IPSFileType::IPS && std::equal(data.begin(), data.end(), eof.begin())) {
return true;
}
- constexpr std::array<u8, 4> eeof{{'E', 'E', 'O', 'F'}};
+ static constexpr std::array<u8, 4> eeof{{'E', 'E', 'O', 'F'}};
return type == IPSFileType::IPS32 && std::equal(data.begin(), data.end(), eeof.begin());
}
diff --git a/src/core/file_sys/registered_cache.cpp b/src/core/file_sys/registered_cache.cpp
index 878d832c2..a6960170c 100644
--- a/src/core/file_sys/registered_cache.cpp
+++ b/src/core/file_sys/registered_cache.cpp
@@ -71,7 +71,7 @@ static std::string GetRelativePathFromNcaID(const std::array<u8, 16>& nca_id, bo
}
static std::string GetCNMTName(TitleType type, u64 title_id) {
- constexpr std::array<const char*, 9> TITLE_TYPE_NAMES{
+ static constexpr std::array<const char*, 9> TITLE_TYPE_NAMES{
"SystemProgram",
"SystemData",
"SystemUpdate",
diff --git a/src/core/file_sys/savedata_factory.cpp b/src/core/file_sys/savedata_factory.cpp
index 1567da231..769065b6f 100644
--- a/src/core/file_sys/savedata_factory.cpp
+++ b/src/core/file_sys/savedata_factory.cpp
@@ -172,7 +172,7 @@ std::string SaveDataFactory::GetFullPath(Core::System& system, VirtualDir dir,
// be interpreted as the title id of the current process.
if (type == SaveDataType::SaveData || type == SaveDataType::DeviceSaveData) {
if (title_id == 0) {
- title_id = system.GetCurrentProcessProgramID();
+ title_id = system.GetApplicationProcessProgramID();
}
}
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{
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,
};
+static constexpr inline size_t NumVirtualCores = Common::BitSize<u64>();
+
+static constexpr inline u64 VirtualCoreMask = [] {
+ u64 mask = 0;
+ for (size_t i = 0; i < NumVirtualCores; ++i) {
+ mask |= (UINT64_C(1) << i);
+ }
+ return mask;
+}();
+
+static constexpr inline u64 ConvertVirtualCoreMaskToPhysical(u64 v_core_mask) {
+ u64 p_core_mask = 0;
+ while (v_core_mask != 0) {
+ const u64 next = std::countr_zero(v_core_mask);
+ v_core_mask &= ~(static_cast<u64>(1) << next);
+ p_core_mask |= (static_cast<u64>(1) << VirtualToPhysicalCoreMap[next]);
+ }
+ return p_core_mask;
+}
+
// Cortex-A57 supports 4 memory watchpoints
constexpr u64 NUM_WATCHPOINTS = 4;
diff --git a/src/core/hid/emulated_console.cpp b/src/core/hid/emulated_console.cpp
index 1c91bbe40..17d663379 100644
--- a/src/core/hid/emulated_console.cpp
+++ b/src/core/hid/emulated_console.cpp
@@ -23,7 +23,8 @@ void EmulatedConsole::SetTouchParams() {
// We can't use mouse as touch if native mouse is enabled
if (!Settings::values.mouse_enabled) {
- touch_params[index++] = Common::ParamPackage{"engine:mouse,axis_x:10,axis_y:11,button:0"};
+ touch_params[index++] =
+ Common::ParamPackage{"engine:mouse,axis_x:0,axis_y:1,button:0,port:2"};
}
touch_params[index++] =
diff --git a/src/core/hid/emulated_controller.cpp b/src/core/hid/emulated_controller.cpp
index 71364c323..6d5a3dead 100644
--- a/src/core/hid/emulated_controller.cpp
+++ b/src/core/hid/emulated_controller.cpp
@@ -2,6 +2,7 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#include <algorithm>
+#include <common/scope_exit.h>
#include "common/polyfill_ranges.h"
#include "common/thread.h"
@@ -11,6 +12,7 @@
namespace Core::HID {
constexpr s32 HID_JOYSTICK_MAX = 0x7fff;
constexpr s32 HID_TRIGGER_MAX = 0x7fff;
+constexpr u32 TURBO_BUTTON_DELAY = 4;
// Use a common UUID for TAS and Virtual Gamepad
constexpr Common::UUID TAS_UUID =
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() {
motion_params[index] = Common::ParamPackage(player.motions[index]);
}
+ controller.color_values = {};
controller.colors_state.fullkey = {
.body = GetNpadColor(player.body_color_left),
.button = GetNpadColor(player.button_color_left),
@@ -106,6 +109,8 @@ void EmulatedController::ReloadFromSettings() {
.button = GetNpadColor(player.button_color_right),
};
+ ring_params[0] = Common::ParamPackage(Settings::values.ringcon_analogs);
+
// Other or debug controller should always be a pro controller
if (npad_id_type != NpadIdType::Other) {
SetNpadStyleIndex(MapSettingsTypeToNPad(player.controller_type));
@@ -132,18 +137,28 @@ void EmulatedController::LoadDevices() {
trigger_params[LeftIndex] = button_params[Settings::NativeButton::ZL];
trigger_params[RightIndex] = button_params[Settings::NativeButton::ZR];
+ color_params[LeftIndex] = left_joycon;
+ color_params[RightIndex] = right_joycon;
+ color_params[LeftIndex].Set("color", true);
+ color_params[RightIndex].Set("color", true);
+
battery_params[LeftIndex] = left_joycon;
battery_params[RightIndex] = right_joycon;
battery_params[LeftIndex].Set("battery", true);
battery_params[RightIndex].Set("battery", true);
- camera_params = Common::ParamPackage{"engine:camera,camera:1"};
- nfc_params = Common::ParamPackage{"engine:virtual_amiibo,nfc:1"};
+ camera_params[0] = right_joycon;
+ camera_params[0].Set("camera", true);
+ camera_params[1] = Common::ParamPackage{"engine:camera,camera:1"};
+ ring_params[1] = Common::ParamPackage{"engine:joycon,axis_x:100,axis_y:101"};
+ nfc_params[0] = Common::ParamPackage{"engine:virtual_amiibo,nfc:1"};
+ nfc_params[1] = right_joycon;
+ nfc_params[1].Set("nfc", true);
output_params[LeftIndex] = left_joycon;
output_params[RightIndex] = right_joycon;
- output_params[2] = camera_params;
- output_params[3] = nfc_params;
+ output_params[2] = camera_params[1];
+ output_params[3] = nfc_params[0];
output_params[LeftIndex].Set("output", true);
output_params[RightIndex].Set("output", true);
output_params[2].Set("output", true);
@@ -159,8 +174,11 @@ void EmulatedController::LoadDevices() {
Common::Input::CreateInputDevice);
std::ranges::transform(battery_params, battery_devices.begin(),
Common::Input::CreateInputDevice);
- camera_devices = Common::Input::CreateInputDevice(camera_params);
- nfc_devices = Common::Input::CreateInputDevice(nfc_params);
+ std::ranges::transform(color_params, color_devices.begin(), Common::Input::CreateInputDevice);
+ std::ranges::transform(camera_params, camera_devices.begin(), Common::Input::CreateInputDevice);
+ std::ranges::transform(ring_params, ring_analog_devices.begin(),
+ Common::Input::CreateInputDevice);
+ std::ranges::transform(nfc_params, nfc_devices.begin(), Common::Input::CreateInputDevice);
std::ranges::transform(output_params, output_devices.begin(),
Common::Input::CreateOutputDevice);
@@ -322,6 +340,19 @@ void EmulatedController::ReloadInput() {
battery_devices[index]->ForceUpdate();
}
+ for (std::size_t index = 0; index < color_devices.size(); ++index) {
+ if (!color_devices[index]) {
+ continue;
+ }
+ color_devices[index]->SetCallback({
+ .on_change =
+ [this, index](const Common::Input::CallbackStatus& callback) {
+ SetColors(callback, index);
+ },
+ });
+ color_devices[index]->ForceUpdate();
+ }
+
for (std::size_t index = 0; index < motion_devices.size(); ++index) {
if (!motion_devices[index]) {
continue;
@@ -335,22 +366,37 @@ void EmulatedController::ReloadInput() {
motion_devices[index]->ForceUpdate();
}
- if (camera_devices) {
- camera_devices->SetCallback({
+ for (std::size_t index = 0; index < camera_devices.size(); ++index) {
+ if (!camera_devices[index]) {
+ continue;
+ }
+ camera_devices[index]->SetCallback({
.on_change =
[this](const Common::Input::CallbackStatus& callback) { SetCamera(callback); },
});
- camera_devices->ForceUpdate();
+ camera_devices[index]->ForceUpdate();
}
- if (nfc_devices) {
- if (npad_id_type == NpadIdType::Handheld || npad_id_type == NpadIdType::Player1) {
- nfc_devices->SetCallback({
- .on_change =
- [this](const Common::Input::CallbackStatus& callback) { SetNfc(callback); },
- });
- nfc_devices->ForceUpdate();
+ for (std::size_t index = 0; index < ring_analog_devices.size(); ++index) {
+ if (!ring_analog_devices[index]) {
+ continue;
}
+ ring_analog_devices[index]->SetCallback({
+ .on_change =
+ [this](const Common::Input::CallbackStatus& callback) { SetRingAnalog(callback); },
+ });
+ ring_analog_devices[index]->ForceUpdate();
+ }
+
+ for (std::size_t index = 0; index < nfc_devices.size(); ++index) {
+ if (!nfc_devices[index]) {
+ continue;
+ }
+ nfc_devices[index]->SetCallback({
+ .on_change =
+ [this](const Common::Input::CallbackStatus& callback) { SetNfc(callback); },
+ });
+ nfc_devices[index]->ForceUpdate();
}
// Register TAS devices. No need to force update
@@ -402,6 +448,7 @@ void EmulatedController::ReloadInput() {
},
});
}
+ turbo_button_state = 0;
}
void EmulatedController::UnloadInput() {
@@ -420,6 +467,9 @@ void EmulatedController::UnloadInput() {
for (auto& battery : battery_devices) {
battery.reset();
}
+ for (auto& color : color_devices) {
+ color.reset();
+ }
for (auto& output : output_devices) {
output.reset();
}
@@ -435,8 +485,15 @@ void EmulatedController::UnloadInput() {
for (auto& stick : virtual_stick_devices) {
stick.reset();
}
- camera_devices.reset();
- nfc_devices.reset();
+ for (auto& camera : camera_devices) {
+ camera.reset();
+ }
+ for (auto& ring : ring_analog_devices) {
+ ring.reset();
+ }
+ for (auto& nfc : nfc_devices) {
+ nfc.reset();
+ }
}
void EmulatedController::EnableConfiguration() {
@@ -448,6 +505,11 @@ void EmulatedController::EnableConfiguration() {
void EmulatedController::DisableConfiguration() {
is_configuring = false;
+ // Get Joycon colors before turning on the controller
+ for (const auto& color_device : color_devices) {
+ color_device->ForceUpdate();
+ }
+
// Apply temporary npad type to the real controller
if (tmp_npad_type != npad_type) {
if (is_connected) {
@@ -501,6 +563,9 @@ void EmulatedController::SaveCurrentConfig() {
for (std::size_t index = 0; index < player.motions.size(); ++index) {
player.motions[index] = motion_params[index].Serialize();
}
+ if (npad_id_type == NpadIdType::Player1) {
+ Settings::values.ringcon_analogs = ring_params[0].Serialize();
+ }
}
void EmulatedController::RestoreConfig() {
@@ -624,6 +689,7 @@ void EmulatedController::SetButton(const Common::Input::CallbackStatus& callback
}
current_status.toggle = new_status.toggle;
+ current_status.turbo = new_status.turbo;
current_status.uuid = uuid;
// Update button status with current
@@ -772,17 +838,21 @@ void EmulatedController::SetStick(const Common::Input::CallbackStatus& callback,
if (index >= controller.stick_values.size()) {
return;
}
- std::unique_lock lock{mutex};
+ auto trigger_guard =
+ SCOPE_GUARD({ TriggerOnChange(ControllerTriggerType::Stick, !is_configuring); });
+ std::scoped_lock lock{mutex};
const auto stick_value = TransformToStick(callback);
// Only read stick values that have the same uuid or are over the threshold to avoid flapping
if (controller.stick_values[index].uuid != uuid) {
const bool is_tas = uuid == TAS_UUID;
if (is_tas && stick_value.x.value == 0 && stick_value.y.value == 0) {
+ trigger_guard.Cancel();
return;
}
if (!is_tas && !stick_value.down && !stick_value.up && !stick_value.left &&
!stick_value.right) {
+ trigger_guard.Cancel();
return;
}
}
@@ -793,8 +863,6 @@ void EmulatedController::SetStick(const Common::Input::CallbackStatus& callback,
if (is_configuring) {
controller.analog_stick_state.left = {};
controller.analog_stick_state.right = {};
- lock.unlock();
- TriggerOnChange(ControllerTriggerType::Stick, false);
return;
}
@@ -819,9 +887,6 @@ void EmulatedController::SetStick(const Common::Input::CallbackStatus& callback,
controller.npad_button_state.stick_r_down.Assign(controller.stick_values[index].down);
break;
}
-
- lock.unlock();
- TriggerOnChange(ControllerTriggerType::Stick, true);
}
void EmulatedController::SetTrigger(const Common::Input::CallbackStatus& callback,
@@ -829,7 +894,9 @@ void EmulatedController::SetTrigger(const Common::Input::CallbackStatus& callbac
if (index >= controller.trigger_values.size()) {
return;
}
- std::unique_lock lock{mutex};
+ auto trigger_guard =
+ SCOPE_GUARD({ TriggerOnChange(ControllerTriggerType::Trigger, !is_configuring); });
+ std::scoped_lock lock{mutex};
const auto trigger_value = TransformToTrigger(callback);
// 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
if (is_configuring) {
controller.gc_trigger_state.left = 0;
controller.gc_trigger_state.right = 0;
- lock.unlock();
- TriggerOnChange(ControllerTriggerType::Trigger, false);
return;
}
// Only GC controllers have analog triggers
if (npad_type != NpadStyleIndex::GameCube) {
+ trigger_guard.Cancel();
return;
}
@@ -868,9 +934,6 @@ void EmulatedController::SetTrigger(const Common::Input::CallbackStatus& callbac
controller.npad_button_state.zr.Assign(trigger.pressed.value);
break;
}
-
- lock.unlock();
- TriggerOnChange(ControllerTriggerType::Trigger, true);
}
void EmulatedController::SetMotion(const Common::Input::CallbackStatus& callback,
@@ -878,7 +941,8 @@ void EmulatedController::SetMotion(const Common::Input::CallbackStatus& callback
if (index >= controller.motion_values.size()) {
return;
}
- std::unique_lock lock{mutex};
+ SCOPE_EXIT({ TriggerOnChange(ControllerTriggerType::Motion, !is_configuring); });
+ std::scoped_lock lock{mutex};
auto& raw_status = controller.motion_values[index].raw_status;
auto& emulated = controller.motion_values[index].emulated;
@@ -893,14 +957,12 @@ void EmulatedController::SetMotion(const Common::Input::CallbackStatus& callback
raw_status.gyro.y.value,
raw_status.gyro.z.value,
});
- emulated.SetGyroThreshold(raw_status.gyro.x.properties.threshold);
+ emulated.SetUserGyroThreshold(raw_status.gyro.x.properties.threshold);
emulated.UpdateRotation(raw_status.delta_timestamp);
emulated.UpdateOrientation(raw_status.delta_timestamp);
force_update_motion = raw_status.force_update;
if (is_configuring) {
- lock.unlock();
- TriggerOnChange(ControllerTriggerType::Motion, false);
return;
}
@@ -910,9 +972,56 @@ void EmulatedController::SetMotion(const Common::Input::CallbackStatus& callback
motion.rotation = emulated.GetRotations();
motion.orientation = emulated.GetOrientation();
motion.is_at_rest = !emulated.IsMoving(motion_sensitivity);
+}
- lock.unlock();
- TriggerOnChange(ControllerTriggerType::Motion, true);
+void EmulatedController::SetColors(const Common::Input::CallbackStatus& callback,
+ std::size_t index) {
+ if (index >= controller.color_values.size()) {
+ return;
+ }
+ auto trigger_guard =
+ SCOPE_GUARD({ TriggerOnChange(ControllerTriggerType::Color, !is_configuring); });
+ std::scoped_lock lock{mutex};
+ controller.color_values[index] = TransformToColor(callback);
+
+ if (is_configuring) {
+ return;
+ }
+
+ if (controller.color_values[index].body == 0) {
+ trigger_guard.Cancel();
+ return;
+ }
+
+ controller.colors_state.fullkey = {
+ .body = GetNpadColor(controller.color_values[index].body),
+ .button = GetNpadColor(controller.color_values[index].buttons),
+ };
+ if (npad_type == NpadStyleIndex::ProController) {
+ controller.colors_state.left = {
+ .body = GetNpadColor(controller.color_values[index].left_grip),
+ .button = GetNpadColor(controller.color_values[index].buttons),
+ };
+ controller.colors_state.right = {
+ .body = GetNpadColor(controller.color_values[index].right_grip),
+ .button = GetNpadColor(controller.color_values[index].buttons),
+ };
+ } else {
+ switch (index) {
+ case LeftIndex:
+ controller.colors_state.left = {
+ .body = GetNpadColor(controller.color_values[index].body),
+ .button = GetNpadColor(controller.color_values[index].buttons),
+ };
+ break;
+ case RightIndex:
+ controller.colors_state.right = {
+ .body = GetNpadColor(controller.color_values[index].body),
+ .button = GetNpadColor(controller.color_values[index].buttons),
+ };
+ break;
+ }
+ }
}
void EmulatedController::SetBattery(const Common::Input::CallbackStatus& callback,
@@ -920,12 +1029,11 @@ void EmulatedController::SetBattery(const Common::Input::CallbackStatus& callbac
if (index >= controller.battery_values.size()) {
return;
}
- std::unique_lock lock{mutex};
+ SCOPE_EXIT({ TriggerOnChange(ControllerTriggerType::Battery, !is_configuring); });
+ std::scoped_lock lock{mutex};
controller.battery_values[index] = TransformToBattery(callback);
if (is_configuring) {
- lock.unlock();
- TriggerOnChange(ControllerTriggerType::Battery, false);
return;
}
@@ -981,18 +1089,14 @@ void EmulatedController::SetBattery(const Common::Input::CallbackStatus& callbac
};
break;
}
-
- lock.unlock();
- TriggerOnChange(ControllerTriggerType::Battery, true);
}
void EmulatedController::SetCamera(const Common::Input::CallbackStatus& callback) {
- std::unique_lock lock{mutex};
+ SCOPE_EXIT({ TriggerOnChange(ControllerTriggerType::IrSensor, !is_configuring); });
+ std::scoped_lock lock{mutex};
controller.camera_values = TransformToCamera(callback);
if (is_configuring) {
- lock.unlock();
- TriggerOnChange(ControllerTriggerType::IrSensor, false);
return;
}
@@ -1000,18 +1104,28 @@ void EmulatedController::SetCamera(const Common::Input::CallbackStatus& callback
controller.camera_state.format =
static_cast<Core::IrSensor::ImageTransferProcessorFormat>(controller.camera_values.format);
controller.camera_state.data = controller.camera_values.data;
+}
- lock.unlock();
- TriggerOnChange(ControllerTriggerType::IrSensor, true);
+void EmulatedController::SetRingAnalog(const Common::Input::CallbackStatus& callback) {
+ SCOPE_EXIT({ TriggerOnChange(ControllerTriggerType::RingController, !is_configuring); });
+ std::scoped_lock lock{mutex};
+ const auto force_value = TransformToStick(callback);
+
+ controller.ring_analog_value = force_value.x;
+
+ if (is_configuring) {
+ return;
+ }
+
+ controller.ring_analog_state.force = force_value.x.value;
}
void EmulatedController::SetNfc(const Common::Input::CallbackStatus& callback) {
- std::unique_lock lock{mutex};
+ SCOPE_EXIT({ TriggerOnChange(ControllerTriggerType::Nfc, !is_configuring); });
+ std::scoped_lock lock{mutex};
controller.nfc_values = TransformToNfc(callback);
if (is_configuring) {
- lock.unlock();
- TriggerOnChange(ControllerTriggerType::Nfc, false);
return;
}
@@ -1019,9 +1133,6 @@ void EmulatedController::SetNfc(const Common::Input::CallbackStatus& callback) {
controller.nfc_values.state,
controller.nfc_values.data,
};
-
- lock.unlock();
- TriggerOnChange(ControllerTriggerType::Nfc, true);
}
bool EmulatedController::SetVibration(std::size_t device_index, VibrationValue vibration) {
@@ -1053,7 +1164,7 @@ bool EmulatedController::SetVibration(std::size_t device_index, VibrationValue v
.type = type,
};
return output_devices[device_index]->SetVibration(status) ==
- Common::Input::VibrationError::None;
+ Common::Input::DriverResult::Success;
}
bool EmulatedController::IsVibrationEnabled(std::size_t device_index) {
@@ -1075,16 +1186,32 @@ bool EmulatedController::IsVibrationEnabled(std::size_t device_index) {
return output_devices[device_index]->IsVibrationEnabled();
}
-bool EmulatedController::SetPollingMode(Common::Input::PollingMode polling_mode) {
- LOG_INFO(Service_HID, "Set polling mode {}", polling_mode);
- auto& output_device = output_devices[static_cast<std::size_t>(DeviceIndex::Right)];
+Common::Input::DriverResult EmulatedController::SetPollingMode(
+ EmulatedDeviceIndex device_index, Common::Input::PollingMode polling_mode) {
+ LOG_INFO(Service_HID, "Set polling mode {}, device_index={}", polling_mode, device_index);
+
+ auto& left_output_device = output_devices[static_cast<std::size_t>(DeviceIndex::Left)];
+ auto& right_output_device = output_devices[static_cast<std::size_t>(DeviceIndex::Right)];
auto& nfc_output_device = output_devices[3];
- const auto virtual_nfc_result = nfc_output_device->SetPollingMode(polling_mode);
- const auto mapped_nfc_result = output_device->SetPollingMode(polling_mode);
+ if (device_index == EmulatedDeviceIndex::LeftIndex) {
+ return left_output_device->SetPollingMode(polling_mode);
+ }
+
+ if (device_index == EmulatedDeviceIndex::RightIndex) {
+ const auto virtual_nfc_result = nfc_output_device->SetPollingMode(polling_mode);
+ const auto mapped_nfc_result = right_output_device->SetPollingMode(polling_mode);
- return virtual_nfc_result == Common::Input::PollingError::None ||
- mapped_nfc_result == Common::Input::PollingError::None;
+ if (virtual_nfc_result == Common::Input::DriverResult::Success) {
+ return virtual_nfc_result;
+ }
+ return mapped_nfc_result;
+ }
+
+ left_output_device->SetPollingMode(polling_mode);
+ right_output_device->SetPollingMode(polling_mode);
+ nfc_output_device->SetPollingMode(polling_mode);
+ return Common::Input::DriverResult::Success;
}
bool EmulatedController::SetCameraFormat(
@@ -1095,13 +1222,22 @@ bool EmulatedController::SetCameraFormat(
auto& camera_output_device = output_devices[2];
if (right_output_device->SetCameraFormat(static_cast<Common::Input::CameraFormat>(
- camera_format)) == Common::Input::CameraError::None) {
+ camera_format)) == Common::Input::DriverResult::Success) {
return true;
}
// Fallback to Qt camera if native device doesn't have support
return camera_output_device->SetCameraFormat(static_cast<Common::Input::CameraFormat>(
- camera_format)) == Common::Input::CameraError::None;
+ camera_format)) == Common::Input::DriverResult::Success;
+}
+
+Common::ParamPackage EmulatedController::GetRingParam() const {
+ return ring_params[0];
+}
+
+void EmulatedController::SetRingParam(Common::ParamPackage param) {
+ ring_params[0] = std::move(param);
+ ReloadInput();
}
bool EmulatedController::HasNfc() const {
@@ -1148,6 +1284,26 @@ void EmulatedController::SetLedPattern() {
}
}
+void EmulatedController::SetGyroscopeZeroDriftMode(GyroscopeZeroDriftMode mode) {
+ for (auto& motion : controller.motion_values) {
+ switch (mode) {
+ case GyroscopeZeroDriftMode::Loose:
+ motion_sensitivity = motion.emulated.IsAtRestLoose;
+ motion.emulated.SetGyroThreshold(motion.emulated.ThresholdLoose);
+ break;
+ case GyroscopeZeroDriftMode::Tight:
+ motion_sensitivity = motion.emulated.IsAtRestThight;
+ motion.emulated.SetGyroThreshold(motion.emulated.ThresholdThight);
+ break;
+ case GyroscopeZeroDriftMode::Standard:
+ default:
+ motion_sensitivity = motion.emulated.IsAtRestStandard;
+ motion.emulated.SetGyroThreshold(motion.emulated.ThresholdStandard);
+ break;
+ }
+ }
+}
+
void EmulatedController::SetSupportedNpadStyleTag(NpadStyleTag supported_styles) {
supported_style_tag = supported_styles;
if (!is_connected) {
@@ -1255,39 +1411,35 @@ void EmulatedController::Connect(bool use_temporary_value) {
return;
}
- std::unique_lock lock{mutex};
+ auto trigger_guard =
+ SCOPE_GUARD({ TriggerOnChange(ControllerTriggerType::Connected, !is_configuring); });
+ std::scoped_lock lock{mutex};
if (is_configuring) {
tmp_is_connected = true;
- lock.unlock();
- TriggerOnChange(ControllerTriggerType::Connected, false);
return;
}
if (is_connected) {
+ trigger_guard.Cancel();
return;
}
is_connected = true;
-
- lock.unlock();
- TriggerOnChange(ControllerTriggerType::Connected, true);
}
void EmulatedController::Disconnect() {
- std::unique_lock lock{mutex};
+ auto trigger_guard =
+ SCOPE_GUARD({ TriggerOnChange(ControllerTriggerType::Disconnected, !is_configuring); });
+ std::scoped_lock lock{mutex};
if (is_configuring) {
tmp_is_connected = false;
- lock.unlock();
- TriggerOnChange(ControllerTriggerType::Disconnected, false);
return;
}
if (!is_connected) {
+ trigger_guard.Cancel();
return;
}
is_connected = false;
-
- lock.unlock();
- TriggerOnChange(ControllerTriggerType::Disconnected, true);
}
bool EmulatedController::IsConnected(bool get_temporary_value) const {
@@ -1312,19 +1464,21 @@ NpadStyleIndex EmulatedController::GetNpadStyleIndex(bool get_temporary_value) c
}
void EmulatedController::SetNpadStyleIndex(NpadStyleIndex npad_type_) {
- std::unique_lock lock{mutex};
+ auto trigger_guard =
+ SCOPE_GUARD({ TriggerOnChange(ControllerTriggerType::Type, !is_configuring); });
+ std::scoped_lock lock{mutex};
if (is_configuring) {
if (tmp_npad_type == npad_type_) {
+ trigger_guard.Cancel();
return;
}
tmp_npad_type = npad_type_;
- lock.unlock();
- TriggerOnChange(ControllerTriggerType::Type, false);
return;
}
if (npad_type == npad_type_) {
+ trigger_guard.Cancel();
return;
}
if (is_connected) {
@@ -1332,9 +1486,6 @@ void EmulatedController::SetNpadStyleIndex(NpadStyleIndex npad_type_) {
NpadIdTypeToIndex(npad_id_type));
}
npad_type = npad_type_;
-
- lock.unlock();
- TriggerOnChange(ControllerTriggerType::Type, true);
}
LedPattern EmulatedController::GetLedPattern() const {
@@ -1395,6 +1546,10 @@ CameraValues EmulatedController::GetCameraValues() const {
return controller.camera_values;
}
+RingAnalogValue EmulatedController::GetRingSensorValues() const {
+ return controller.ring_analog_value;
+}
+
HomeButtonState EmulatedController::GetHomeButtons() const {
std::scoped_lock lock{mutex};
if (is_configuring) {
@@ -1416,7 +1571,7 @@ NpadButtonState EmulatedController::GetNpadButtons() const {
if (is_configuring) {
return {};
}
- return controller.npad_button_state;
+ return {controller.npad_button_state.raw & GetTurboButtonMask()};
}
DebugPadButton EmulatedController::GetDebugPadButtons() const {
@@ -1428,22 +1583,12 @@ DebugPadButton EmulatedController::GetDebugPadButtons() const {
}
AnalogSticks EmulatedController::GetSticks() const {
- std::unique_lock lock{mutex};
+ std::scoped_lock lock{mutex};
if (is_configuring) {
return {};
}
- // Some drivers like stick from buttons need constant refreshing
- for (auto& device : stick_devices) {
- if (!device) {
- continue;
- }
- lock.unlock();
- device->SoftUpdate();
- lock.lock();
- }
-
return controller.analog_stick_state;
}
@@ -1488,6 +1633,10 @@ const CameraState& EmulatedController::GetCamera() const {
return controller.camera_state;
}
+RingSensorForce EmulatedController::GetRingSensorForce() const {
+ return controller.ring_analog_state;
+}
+
const NfcState& EmulatedController::GetNfc() const {
std::scoped_lock lock{mutex};
return controller.nfc_state;
@@ -1530,4 +1679,74 @@ void EmulatedController::DeleteCallback(int key) {
}
callback_list.erase(iterator);
}
+
+void EmulatedController::TurboButtonUpdate() {
+ turbo_button_state = (turbo_button_state + 1) % (TURBO_BUTTON_DELAY * 2);
+}
+
+NpadButton EmulatedController::GetTurboButtonMask() const {
+ // Apply no mask when disabled
+ if (turbo_button_state < TURBO_BUTTON_DELAY) {
+ return {NpadButton::All};
+ }
+
+ NpadButtonState button_mask{};
+ for (std::size_t index = 0; index < controller.button_values.size(); ++index) {
+ if (!controller.button_values[index].turbo) {
+ continue;
+ }
+
+ switch (index) {
+ case Settings::NativeButton::A:
+ button_mask.a.Assign(1);
+ break;
+ case Settings::NativeButton::B:
+ button_mask.b.Assign(1);
+ break;
+ case Settings::NativeButton::X:
+ button_mask.x.Assign(1);
+ break;
+ case Settings::NativeButton::Y:
+ button_mask.y.Assign(1);
+ break;
+ case Settings::NativeButton::L:
+ button_mask.l.Assign(1);
+ break;
+ case Settings::NativeButton::R:
+ button_mask.r.Assign(1);
+ break;
+ case Settings::NativeButton::ZL:
+ button_mask.zl.Assign(1);
+ break;
+ case Settings::NativeButton::ZR:
+ button_mask.zr.Assign(1);
+ break;
+ case Settings::NativeButton::DLeft:
+ button_mask.left.Assign(1);
+ break;
+ case Settings::NativeButton::DUp:
+ button_mask.up.Assign(1);
+ break;
+ case Settings::NativeButton::DRight:
+ button_mask.right.Assign(1);
+ break;
+ case Settings::NativeButton::DDown:
+ button_mask.down.Assign(1);
+ break;
+ case Settings::NativeButton::SL:
+ button_mask.left_sl.Assign(1);
+ button_mask.right_sl.Assign(1);
+ break;
+ case Settings::NativeButton::SR:
+ button_mask.left_sr.Assign(1);
+ button_mask.right_sr.Assign(1);
+ break;
+ default:
+ break;
+ }
+ }
+
+ return static_cast<NpadButton>(~button_mask.raw);
+}
+
} // namespace Core::HID
diff --git a/src/core/hid/emulated_controller.h b/src/core/hid/emulated_controller.h
index a398543a6..a9da465a2 100644
--- a/src/core/hid/emulated_controller.h
+++ b/src/core/hid/emulated_controller.h
@@ -35,19 +35,27 @@ using ControllerMotionDevices =
std::array<std::unique_ptr<Common::Input::InputDevice>, Settings::NativeMotion::NumMotions>;
using TriggerDevices =
std::array<std::unique_ptr<Common::Input::InputDevice>, Settings::NativeTrigger::NumTriggers>;
+using ColorDevices =
+ std::array<std::unique_ptr<Common::Input::InputDevice>, max_emulated_controllers>;
using BatteryDevices =
std::array<std::unique_ptr<Common::Input::InputDevice>, max_emulated_controllers>;
-using CameraDevices = std::unique_ptr<Common::Input::InputDevice>;
-using NfcDevices = std::unique_ptr<Common::Input::InputDevice>;
+using CameraDevices =
+ std::array<std::unique_ptr<Common::Input::InputDevice>, max_emulated_controllers>;
+using RingAnalogDevices =
+ std::array<std::unique_ptr<Common::Input::InputDevice>, max_emulated_controllers>;
+using NfcDevices =
+ std::array<std::unique_ptr<Common::Input::InputDevice>, max_emulated_controllers>;
using OutputDevices = std::array<std::unique_ptr<Common::Input::OutputDevice>, output_devices_size>;
using ButtonParams = std::array<Common::ParamPackage, Settings::NativeButton::NumButtons>;
using StickParams = std::array<Common::ParamPackage, Settings::NativeAnalog::NumAnalogs>;
using ControllerMotionParams = std::array<Common::ParamPackage, Settings::NativeMotion::NumMotions>;
using TriggerParams = std::array<Common::ParamPackage, Settings::NativeTrigger::NumTriggers>;
+using ColorParams = std::array<Common::ParamPackage, max_emulated_controllers>;
using BatteryParams = std::array<Common::ParamPackage, max_emulated_controllers>;
-using CameraParams = Common::ParamPackage;
-using NfcParams = Common::ParamPackage;
+using CameraParams = std::array<Common::ParamPackage, max_emulated_controllers>;
+using RingAnalogParams = std::array<Common::ParamPackage, max_emulated_controllers>;
+using NfcParams = std::array<Common::ParamPackage, max_emulated_controllers>;
using OutputParams = std::array<Common::ParamPackage, output_devices_size>;
using ButtonValues = std::array<Common::Input::ButtonStatus, Settings::NativeButton::NumButtons>;
@@ -58,6 +66,7 @@ using ControllerMotionValues = std::array<ControllerMotionInfo, Settings::Native
using ColorValues = std::array<Common::Input::BodyColorStatus, max_emulated_controllers>;
using BatteryValues = std::array<Common::Input::BatteryStatus, max_emulated_controllers>;
using CameraValues = Common::Input::CameraStatus;
+using RingAnalogValue = Common::Input::AnalogStatus;
using NfcValues = Common::Input::NfcStatus;
using VibrationValues = std::array<Common::Input::VibrationStatus, max_emulated_controllers>;
@@ -84,6 +93,10 @@ struct CameraState {
std::size_t sample{};
};
+struct RingSensorForce {
+ f32 force;
+};
+
struct NfcState {
Common::Input::NfcState state{};
std::vector<u8> data{};
@@ -116,6 +129,7 @@ struct ControllerStatus {
BatteryValues battery_values{};
VibrationValues vibration_values{};
CameraValues camera_values{};
+ RingAnalogValue ring_analog_value{};
NfcValues nfc_values{};
// Data for HID serices
@@ -129,6 +143,7 @@ struct ControllerStatus {
ControllerColors colors_state{};
BatteryLevelState battery_state{};
CameraState camera_state{};
+ RingSensorForce ring_analog_state{};
NfcState nfc_state{};
};
@@ -141,6 +156,7 @@ enum class ControllerTriggerType {
Battery,
Vibration,
IrSensor,
+ RingController,
Nfc,
Connected,
Disconnected,
@@ -294,6 +310,9 @@ public:
/// Returns the latest camera status from the controller with parameters
CameraValues GetCameraValues() const;
+ /// Returns the latest status of analog input from the ring sensor with parameters
+ RingAnalogValue GetRingSensorValues() const;
+
/// Returns the latest status of button input for the hid::HomeButton service
HomeButtonState GetHomeButtons() const;
@@ -324,6 +343,9 @@ public:
/// Returns the latest camera status from the controller
const CameraState& GetCamera() const;
+ /// Returns the latest ringcon force sensor value
+ RingSensorForce GetRingSensorForce() const;
+
/// Returns the latest ntag status from the controller
const NfcState& GetNfc() const;
@@ -341,10 +363,12 @@ public:
/**
* Sets the desired data to be polled from a controller
+ * @param device_index index of the controller to set the polling mode
* @param polling_mode type of input desired buttons, gyro, nfc, ir, etc.
- * @return true if SetPollingMode was successfull
+ * @return driver result from this command
*/
- bool SetPollingMode(Common::Input::PollingMode polling_mode);
+ Common::Input::DriverResult SetPollingMode(EmulatedDeviceIndex device_index,
+ Common::Input::PollingMode polling_mode);
/**
* Sets the desired camera format to be polled from a controller
@@ -353,6 +377,15 @@ public:
*/
bool SetCameraFormat(Core::IrSensor::ImageTransferProcessorFormat camera_format);
+ // Returns the current mapped ring device
+ Common::ParamPackage GetRingParam() const;
+
+ /**
+ * Updates the current mapped ring device
+ * @param param ParamPackage with ring sensor data to be mapped
+ */
+ void SetRingParam(Common::ParamPackage param);
+
/// Returns true if the device has nfc support
bool HasNfc() const;
@@ -365,6 +398,9 @@ public:
/// Asks the output device to change the player led pattern
void SetLedPattern();
+ /// Changes sensitivity of the motion sensor
+ void SetGyroscopeZeroDriftMode(GyroscopeZeroDriftMode mode);
+
/**
* Adds a callback to the list of events
* @param update_callback A ConsoleUpdateCallback that will be triggered
@@ -378,6 +414,9 @@ public:
*/
void DeleteCallback(int key);
+ /// Swaps the state of the turbo buttons
+ void TurboButtonUpdate();
+
private:
/// creates input devices from params
void LoadDevices();
@@ -433,9 +472,16 @@ private:
void SetMotion(const Common::Input::CallbackStatus& callback, std::size_t index);
/**
+ * Updates the color status of the controller
+ * @param callback A CallbackStatus containing the color status
+ * @param index color ID of the to be updated
+ */
+ void SetColors(const Common::Input::CallbackStatus& callback, std::size_t index);
+
+ /**
* Updates the battery status of the controller
* @param callback A CallbackStatus containing the battery status
- * @param index Button ID of the to be updated
+ * @param index battery ID of the to be updated
*/
void SetBattery(const Common::Input::CallbackStatus& callback, std::size_t index);
@@ -446,6 +492,12 @@ private:
void SetCamera(const Common::Input::CallbackStatus& callback);
/**
+ * Updates the ring analog sensor status of the ring controller
+ * @param callback A CallbackStatus containing the force status
+ */
+ void SetRingAnalog(const Common::Input::CallbackStatus& callback);
+
+ /**
* Updates the nfc status of the controller
* @param callback A CallbackStatus containing the nfc status
*/
@@ -465,6 +517,8 @@ private:
*/
void TriggerOnChange(ControllerTriggerType type, bool is_service_update);
+ NpadButton GetTurboButtonMask() const;
+
const NpadIdType npad_id_type;
NpadStyleIndex npad_type{NpadStyleIndex::None};
NpadStyleIndex original_npad_type{NpadStyleIndex::None};
@@ -472,8 +526,9 @@ private:
bool is_connected{false};
bool is_configuring{false};
bool system_buttons_enabled{true};
- f32 motion_sensitivity{0.01f};
+ f32 motion_sensitivity{Core::HID::MotionInput::IsAtRestStandard};
bool force_update_motion{false};
+ u32 turbo_button_state{0};
// Temporary values to avoid doing changes while the controller is in configuring mode
NpadStyleIndex tmp_npad_type{NpadStyleIndex::None};
@@ -484,7 +539,9 @@ private:
ControllerMotionParams motion_params;
TriggerParams trigger_params;
BatteryParams battery_params;
+ ColorParams color_params;
CameraParams camera_params;
+ RingAnalogParams ring_params;
NfcParams nfc_params;
OutputParams output_params;
@@ -493,7 +550,9 @@ private:
ControllerMotionDevices motion_devices;
TriggerDevices trigger_devices;
BatteryDevices battery_devices;
+ ColorDevices color_devices;
CameraDevices camera_devices;
+ RingAnalogDevices ring_analog_devices;
NfcDevices nfc_devices;
OutputDevices output_devices;
diff --git a/src/core/hid/emulated_devices.cpp b/src/core/hid/emulated_devices.cpp
index e421828d2..578a6ff61 100644
--- a/src/core/hid/emulated_devices.cpp
+++ b/src/core/hid/emulated_devices.cpp
@@ -14,7 +14,6 @@ EmulatedDevices::EmulatedDevices() = default;
EmulatedDevices::~EmulatedDevices() = default;
void EmulatedDevices::ReloadFromSettings() {
- ring_params = Common::ParamPackage(Settings::values.ringcon_analogs);
ReloadInput();
}
@@ -35,9 +34,12 @@ void EmulatedDevices::ReloadInput() {
// First two axis are reserved for mouse position
key_index = 2;
for (auto& mouse_device : mouse_analog_devices) {
+ // Mouse axis are only mapped on port 1, pad 0
Common::ParamPackage mouse_params;
mouse_params.Set("engine", "mouse");
mouse_params.Set("axis", static_cast<int>(key_index));
+ mouse_params.Set("port", 1);
+ mouse_params.Set("pad", 0);
mouse_device = Common::Input::CreateInputDevice(mouse_params);
key_index++;
}
@@ -66,8 +68,6 @@ void EmulatedDevices::ReloadInput() {
key_index++;
}
- ring_analog_device = Common::Input::CreateInputDevice(ring_params);
-
for (std::size_t index = 0; index < mouse_button_devices.size(); ++index) {
if (!mouse_button_devices[index]) {
continue;
@@ -122,13 +122,6 @@ void EmulatedDevices::ReloadInput() {
},
});
}
-
- if (ring_analog_device) {
- ring_analog_device->SetCallback({
- .on_change =
- [this](const Common::Input::CallbackStatus& callback) { SetRingAnalog(callback); },
- });
- }
}
void EmulatedDevices::UnloadInput() {
@@ -145,7 +138,6 @@ void EmulatedDevices::UnloadInput() {
for (auto& button : keyboard_modifier_devices) {
button.reset();
}
- ring_analog_device.reset();
}
void EmulatedDevices::EnableConfiguration() {
@@ -165,7 +157,6 @@ void EmulatedDevices::SaveCurrentConfig() {
if (!is_configuring) {
return;
}
- Settings::values.ringcon_analogs = ring_params.Serialize();
}
void EmulatedDevices::RestoreConfig() {
@@ -175,15 +166,6 @@ void EmulatedDevices::RestoreConfig() {
ReloadFromSettings();
}
-Common::ParamPackage EmulatedDevices::GetRingParam() const {
- return ring_params;
-}
-
-void EmulatedDevices::SetRingParam(Common::ParamPackage param) {
- ring_params = std::move(param);
- ReloadInput();
-}
-
void EmulatedDevices::SetKeyboardButton(const Common::Input::CallbackStatus& callback,
std::size_t index) {
if (index >= device_status.keyboard_values.size()) {
@@ -430,23 +412,6 @@ void EmulatedDevices::SetMouseStick(const Common::Input::CallbackStatus& callbac
TriggerOnChange(DeviceTriggerType::Mouse);
}
-void EmulatedDevices::SetRingAnalog(const Common::Input::CallbackStatus& callback) {
- std::lock_guard lock{mutex};
- const auto force_value = TransformToStick(callback);
-
- device_status.ring_analog_value = force_value.x;
-
- if (is_configuring) {
- device_status.ring_analog_value = {};
- TriggerOnChange(DeviceTriggerType::RingController);
- return;
- }
-
- device_status.ring_analog_state.force = force_value.x.value;
-
- TriggerOnChange(DeviceTriggerType::RingController);
-}
-
KeyboardValues EmulatedDevices::GetKeyboardValues() const {
std::scoped_lock lock{mutex};
return device_status.keyboard_values;
@@ -462,10 +427,6 @@ MouseButtonValues EmulatedDevices::GetMouseButtonsValues() const {
return device_status.mouse_button_values;
}
-RingAnalogValue EmulatedDevices::GetRingSensorValues() const {
- return device_status.ring_analog_value;
-}
-
KeyboardKey EmulatedDevices::GetKeyboard() const {
std::scoped_lock lock{mutex};
return device_status.keyboard_state;
@@ -491,10 +452,6 @@ AnalogStickState EmulatedDevices::GetMouseWheel() const {
return device_status.mouse_wheel_state;
}
-RingSensorForce EmulatedDevices::GetRingSensorForce() const {
- return device_status.ring_analog_state;
-}
-
void EmulatedDevices::TriggerOnChange(DeviceTriggerType type) {
std::scoped_lock lock{callback_mutex};
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
using MouseAnalogDevices = std::array<std::unique_ptr<Common::Input::InputDevice>,
Settings::NativeMouseWheel::NumMouseWheels>;
using MouseStickDevice = std::unique_ptr<Common::Input::InputDevice>;
-using RingAnalogDevice = std::unique_ptr<Common::Input::InputDevice>;
using MouseButtonParams =
std::array<Common::ParamPackage, Settings::NativeMouseButton::NumMouseButtons>;
-using RingAnalogParams = Common::ParamPackage;
using KeyboardValues =
std::array<Common::Input::ButtonStatus, Settings::NativeKeyboard::NumKeyboardKeys>;
@@ -41,17 +39,12 @@ using MouseButtonValues =
using MouseAnalogValues =
std::array<Common::Input::AnalogStatus, Settings::NativeMouseWheel::NumMouseWheels>;
using MouseStickValue = Common::Input::TouchStatus;
-using RingAnalogValue = Common::Input::AnalogStatus;
struct MousePosition {
f32 x;
f32 y;
};
-struct RingSensorForce {
- f32 force;
-};
-
struct DeviceStatus {
// Data from input_common
KeyboardValues keyboard_values{};
@@ -59,7 +52,6 @@ struct DeviceStatus {
MouseButtonValues mouse_button_values{};
MouseAnalogValues mouse_analog_values{};
MouseStickValue mouse_stick_value{};
- RingAnalogValue ring_analog_value{};
// Data for HID serices
KeyboardKey keyboard_state{};
@@ -67,7 +59,6 @@ struct DeviceStatus {
MouseButton mouse_button_state{};
MousePosition mouse_position_state{};
AnalogStickState mouse_wheel_state{};
- RingSensorForce ring_analog_state{};
};
enum class DeviceTriggerType {
@@ -138,9 +129,6 @@ public:
/// Returns the latest status of button input from the mouse with parameters
MouseButtonValues GetMouseButtonsValues() const;
- /// Returns the latest status of analog input from the ring sensor with parameters
- RingAnalogValue GetRingSensorValues() const;
-
/// Returns the latest status of button input from the keyboard
KeyboardKey GetKeyboard() const;
@@ -156,9 +144,6 @@ public:
/// Returns the latest mouse wheel change
AnalogStickState GetMouseWheel() const;
- /// Returns the latest ringcon force sensor value
- RingSensorForce GetRingSensorForce() const;
-
/**
* Adds a callback to the list of events
* @param update_callback InterfaceUpdateCallback that will be triggered
@@ -224,14 +209,11 @@ private:
bool is_configuring{false};
- RingAnalogParams ring_params;
-
KeyboardDevices keyboard_devices;
KeyboardModifierDevices keyboard_modifier_devices;
MouseButtonDevices mouse_button_devices;
MouseAnalogDevices mouse_analog_devices;
MouseStickDevice mouse_stick_device;
- RingAnalogDevice ring_analog_device;
mutable std::mutex mutex;
mutable std::mutex callback_mutex;
diff --git a/src/core/hid/hid_types.h b/src/core/hid/hid_types.h
index e3b1cfbc6..6b35f448c 100644
--- a/src/core/hid/hid_types.h
+++ b/src/core/hid/hid_types.h
@@ -282,6 +282,13 @@ enum class VibrationGcErmCommand : u64 {
StopHard = 2,
};
+// This is nn::hid::GyroscopeZeroDriftMode
+enum class GyroscopeZeroDriftMode : u32 {
+ Loose = 0,
+ Standard = 1,
+ Tight = 2,
+};
+
// This is nn::hid::NpadStyleTag
struct NpadStyleTag {
union {
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
return nfc;
}
+Common::Input::BodyColorStatus TransformToColor(const Common::Input::CallbackStatus& callback) {
+ switch (callback.type) {
+ case Common::Input::InputType::Color:
+ return callback.color_status;
+ break;
+ default:
+ LOG_ERROR(Input, "Conversion from type {} to color not implemented", callback.type);
+ return {};
+ break;
+ }
+}
+
void SanitizeAnalog(Common::Input::AnalogStatus& analog, bool clamp_value) {
const auto& properties = analog.properties;
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
* Converts raw input data into a valid nfc status.
*
* @param callback Supported callbacks: Nfc.
- * @return A valid CameraObject object.
+ * @return A valid data tag vector.
*/
Common::Input::NfcStatus TransformToNfc(const Common::Input::CallbackStatus& callback);
/**
+ * Converts raw input data into a valid color status.
+ *
+ * @param callback Supported callbacks: Color.
+ * @return A valid Color object.
+ */
+Common::Input::BodyColorStatus TransformToColor(const Common::Input::CallbackStatus& callback);
+
+/**
* Converts raw analog data into a valid analog value
* @param analog An analog object containing raw data and properties
* @param clamp_value determines if the value needs to be clamped between -1.0f and 1.0f.
diff --git a/src/core/hid/motion_input.cpp b/src/core/hid/motion_input.cpp
index b1f658e62..eef6edf4b 100644
--- a/src/core/hid/motion_input.cpp
+++ b/src/core/hid/motion_input.cpp
@@ -9,7 +9,7 @@ namespace Core::HID {
MotionInput::MotionInput() {
// Initialize PID constants with default values
SetPID(0.3f, 0.005f, 0.0f);
- SetGyroThreshold(0.007f);
+ SetGyroThreshold(ThresholdStandard);
}
void MotionInput::SetPID(f32 new_kp, f32 new_ki, f32 new_kd) {
@@ -26,11 +26,11 @@ void MotionInput::SetGyroscope(const Common::Vec3f& gyroscope) {
gyro = gyroscope - gyro_bias;
// Auto adjust drift to minimize drift
- if (!IsMoving(0.1f)) {
+ if (!IsMoving(IsAtRestRelaxed)) {
gyro_bias = (gyro_bias * 0.9999f) + (gyroscope * 0.0001f);
}
- if (gyro.Length() < gyro_threshold) {
+ if (gyro.Length() < gyro_threshold * user_gyro_threshold) {
gyro = {};
} else {
only_accelerometer = false;
@@ -49,6 +49,10 @@ void MotionInput::SetGyroThreshold(f32 threshold) {
gyro_threshold = threshold;
}
+void MotionInput::SetUserGyroThreshold(f32 threshold) {
+ user_gyro_threshold = threshold / ThresholdStandard;
+}
+
void MotionInput::EnableReset(bool reset) {
reset_enabled = reset;
}
@@ -208,7 +212,7 @@ void MotionInput::ResetOrientation() {
if (!reset_enabled || only_accelerometer) {
return;
}
- if (!IsMoving(0.5f) && accel.z <= -0.9f) {
+ if (!IsMoving(IsAtRestRelaxed) && accel.z <= -0.9f) {
++reset_counter;
if (reset_counter > 900) {
quat.w = 0;
diff --git a/src/core/hid/motion_input.h b/src/core/hid/motion_input.h
index f5fd90db5..9180bb9aa 100644
--- a/src/core/hid/motion_input.h
+++ b/src/core/hid/motion_input.h
@@ -11,6 +11,15 @@ namespace Core::HID {
class MotionInput {
public:
+ static constexpr float ThresholdLoose = 0.01f;
+ static constexpr float ThresholdStandard = 0.007f;
+ static constexpr float ThresholdThight = 0.002f;
+
+ static constexpr float IsAtRestRelaxed = 0.05f;
+ static constexpr float IsAtRestLoose = 0.02f;
+ static constexpr float IsAtRestStandard = 0.01f;
+ static constexpr float IsAtRestThight = 0.005f;
+
explicit MotionInput();
MotionInput(const MotionInput&) = default;
@@ -26,6 +35,9 @@ public:
void SetGyroBias(const Common::Vec3f& bias);
void SetGyroThreshold(f32 threshold);
+ /// Applies a modifier on top of the normal gyro threshold
+ void SetUserGyroThreshold(f32 threshold);
+
void EnableReset(bool reset);
void ResetRotations();
@@ -74,6 +86,9 @@ private:
// Minimum gyro amplitude to detect if the device is moving
f32 gyro_threshold = 0.0f;
+ // Multiplies gyro_threshold by this value
+ f32 user_gyro_threshold = 0.0f;
+
// Number of invalid sequential data
u32 reset_counter = 0;
diff --git a/src/core/hle/ipc_helpers.h b/src/core/hle/ipc_helpers.h
index a86bec252..38d6cfaff 100644
--- a/src/core/hle/ipc_helpers.h
+++ b/src/core/hle/ipc_helpers.h
@@ -148,7 +148,7 @@ public:
if (context->GetManager()->IsDomain()) {
context->AddDomainObject(std::move(iface));
} else {
- kernel.CurrentProcess()->GetResourceLimit()->Reserve(
+ kernel.ApplicationProcess()->GetResourceLimit()->Reserve(
Kernel::LimitableResource::SessionCountMax, 1);
auto* session = Kernel::KSession::Create(kernel);
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 @@
#include "common/common_funcs.h"
#include "common/common_types.h"
#include "common/logging/log.h"
+#include "common/scratch_buffer.h"
#include "core/hle/ipc_helpers.h"
#include "core/hle/kernel/hle_ipc.h"
#include "core/hle/kernel/k_auto_object.h"
@@ -325,7 +326,7 @@ Result HLERequestContext::WriteToOutgoingCommandBuffer(KThread& requesting_threa
return ResultSuccess;
}
-std::vector<u8> HLERequestContext::ReadBuffer(std::size_t buffer_index) const {
+std::vector<u8> HLERequestContext::ReadBufferCopy(std::size_t buffer_index) const {
const bool is_buffer_a{BufferDescriptorA().size() > buffer_index &&
BufferDescriptorA()[buffer_index].Size()};
if (is_buffer_a) {
@@ -345,6 +346,33 @@ std::vector<u8> HLERequestContext::ReadBuffer(std::size_t buffer_index) const {
}
}
+std::span<const u8> HLERequestContext::ReadBuffer(std::size_t buffer_index) const {
+ static thread_local std::array<Common::ScratchBuffer<u8>, 2> read_buffer_a;
+ static thread_local std::array<Common::ScratchBuffer<u8>, 2> read_buffer_x;
+
+ const bool is_buffer_a{BufferDescriptorA().size() > buffer_index &&
+ BufferDescriptorA()[buffer_index].Size()};
+ if (is_buffer_a) {
+ ASSERT_OR_EXECUTE_MSG(
+ BufferDescriptorA().size() > buffer_index, { return {}; },
+ "BufferDescriptorA invalid buffer_index {}", buffer_index);
+ auto& read_buffer = read_buffer_a[buffer_index];
+ read_buffer.resize_destructive(BufferDescriptorA()[buffer_index].Size());
+ memory.ReadBlock(BufferDescriptorA()[buffer_index].Address(), read_buffer.data(),
+ read_buffer.size());
+ return read_buffer;
+ } else {
+ ASSERT_OR_EXECUTE_MSG(
+ BufferDescriptorX().size() > buffer_index, { return {}; },
+ "BufferDescriptorX invalid buffer_index {}", buffer_index);
+ auto& read_buffer = read_buffer_x[buffer_index];
+ read_buffer.resize_destructive(BufferDescriptorX()[buffer_index].Size());
+ memory.ReadBlock(BufferDescriptorX()[buffer_index].Address(), read_buffer.data(),
+ read_buffer.size());
+ return read_buffer;
+ }
+}
+
std::size_t HLERequestContext::WriteBuffer(const void* buffer, std::size_t size,
std::size_t buffer_index) const {
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 @@
#include <functional>
#include <memory>
#include <optional>
+#include <span>
#include <string>
#include <type_traits>
#include <vector>
@@ -270,8 +271,11 @@ public:
return domain_message_header.has_value();
}
- /// Helper function to read a buffer using the appropriate buffer descriptor
- [[nodiscard]] std::vector<u8> ReadBuffer(std::size_t buffer_index = 0) const;
+ /// Helper function to get a span of a buffer using the appropriate buffer descriptor
+ [[nodiscard]] std::span<const u8> ReadBuffer(std::size_t buffer_index = 0) const;
+
+ /// Helper function to read a copy of a buffer using the appropriate buffer descriptor
+ [[nodiscard]] std::vector<u8> ReadBufferCopy(std::size_t buffer_index = 0) const;
/// Helper function to write a buffer using the appropriate buffer descriptor
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..abdb5639f 100644
--- a/src/core/hle/kernel/init/init_slab_setup.cpp
+++ b/src/core/hle/kernel/init/init_slab_setup.cpp
@@ -11,10 +11,12 @@
#include "core/hle/kernel/init/init_slab_setup.h"
#include "core/hle/kernel/k_code_memory.h"
#include "core/hle/kernel/k_debug.h"
+#include "core/hle/kernel/k_device_address_space.h"
#include "core/hle/kernel/k_event.h"
#include "core/hle/kernel/k_event_info.h"
#include "core/hle/kernel/k_memory_layout.h"
#include "core/hle/kernel/k_memory_manager.h"
+#include "core/hle/kernel/k_object_name.h"
#include "core/hle/kernel/k_page_buffer.h"
#include "core/hle/kernel/k_port.h"
#include "core/hle/kernel/k_process.h"
@@ -43,10 +45,12 @@ namespace Kernel::Init {
HANDLER(KSharedMemoryInfo, (SLAB_COUNT(KSharedMemory) * 8), ##__VA_ARGS__) \
HANDLER(KTransferMemory, (SLAB_COUNT(KTransferMemory)), ##__VA_ARGS__) \
HANDLER(KCodeMemory, (SLAB_COUNT(KCodeMemory)), ##__VA_ARGS__) \
+ HANDLER(KDeviceAddressSpace, (SLAB_COUNT(KDeviceAddressSpace)), ##__VA_ARGS__) \
HANDLER(KSession, (SLAB_COUNT(KSession)), ##__VA_ARGS__) \
HANDLER(KThreadLocalPage, \
(SLAB_COUNT(KProcess) + (SLAB_COUNT(KProcess) + SLAB_COUNT(KThread)) / 8), \
##__VA_ARGS__) \
+ HANDLER(KObjectName, (SLAB_COUNT(KObjectName)), ##__VA_ARGS__) \
HANDLER(KResourceLimit, (SLAB_COUNT(KResourceLimit)), ##__VA_ARGS__) \
HANDLER(KEventInfo, (SLAB_COUNT(KThread) + SLAB_COUNT(KDebug)), ##__VA_ARGS__) \
HANDLER(KDebug, (SLAB_COUNT(KDebug)), ##__VA_ARGS__) \
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:
friend class ::Kernel::KClassTokenGenerator; \
static constexpr inline auto ObjectType = ::Kernel::KClassTokenGenerator::ObjectType::CLASS; \
static constexpr inline const char* const TypeName = #CLASS; \
- static constexpr inline ClassTokenType ClassToken() { \
- return ::Kernel::ClassToken<CLASS>; \
- } \
+ static constexpr inline ClassTokenType ClassToken() { return ::Kernel::ClassToken<CLASS>; } \
\
public: \
YUZU_NON_COPYABLE(CLASS); \
@@ -37,15 +35,9 @@ public:
constexpr ClassTokenType Token = ClassToken(); \
return TypeObj(TypeName, Token); \
} \
- static constexpr const char* GetStaticTypeName() { \
- return TypeName; \
- } \
- virtual TypeObj GetTypeObj() ATTRIBUTE { \
- return GetStaticTypeObj(); \
- } \
- virtual const char* GetTypeName() ATTRIBUTE { \
- return GetStaticTypeName(); \
- } \
+ static constexpr const char* GetStaticTypeName() { return TypeName; } \
+ virtual TypeObj GetTypeObj() ATTRIBUTE { return GetStaticTypeObj(); } \
+ virtual const char* GetTypeName() ATTRIBUTE { return GetStaticTypeName(); } \
\
private: \
constexpr bool operator!=(const TypeObj& rhs)
@@ -245,8 +237,8 @@ public:
}
template <typename U>
- requires(std::derived_from<T, U> ||
- std::derived_from<U, T>) constexpr KScopedAutoObject(KScopedAutoObject<U>&& rhs) {
+ requires(std::derived_from<T, U> || std::derived_from<U, T>)
+ constexpr KScopedAutoObject(KScopedAutoObject<U>&& rhs) {
if constexpr (std::derived_from<U, T>) {
// Upcast.
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..2907cc6e3
--- /dev/null
+++ b/src/core/hle/kernel/k_capabilities.cpp
@@ -0,0 +1,358 @@
+// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include "core/hardware_properties.h"
+#include "core/hle/kernel/k_capabilities.h"
+#include "core/hle/kernel/k_memory_layout.h"
+#include "core/hle/kernel/k_page_table.h"
+#include "core/hle/kernel/kernel.h"
+#include "core/hle/kernel/svc_results.h"
+#include "core/hle/kernel/svc_version.h"
+
+namespace Kernel {
+
+Result KCapabilities::InitializeForKIP(std::span<const u32> kern_caps, KPageTable* page_table) {
+ // We're initializing an initial process.
+ m_svc_access_flags.reset();
+ m_irq_access_flags.reset();
+ m_debug_capabilities = 0;
+ m_handle_table_size = 0;
+ m_intended_kernel_version = 0;
+ m_program_type = 0;
+
+ // Initial processes may run on all cores.
+ constexpr u64 VirtMask = Core::Hardware::VirtualCoreMask;
+ constexpr u64 PhysMask = Core::Hardware::ConvertVirtualCoreMaskToPhysical(VirtMask);
+
+ m_core_mask = VirtMask;
+ m_phys_core_mask = PhysMask;
+
+ // Initial processes may use any user priority they like.
+ m_priority_mask = ~0xFULL;
+
+ // Here, Nintendo sets the kernel version to the current kernel version.
+ // We will follow suit and set the version to the highest supported kernel version.
+ KernelVersion intended_kernel_version{};
+ intended_kernel_version.major_version.Assign(Svc::SupportedKernelMajorVersion);
+ intended_kernel_version.minor_version.Assign(Svc::SupportedKernelMinorVersion);
+ m_intended_kernel_version = intended_kernel_version.raw;
+
+ // Parse the capabilities array.
+ R_RETURN(this->SetCapabilities(kern_caps, page_table));
+}
+
+Result KCapabilities::InitializeForUser(std::span<const u32> user_caps, KPageTable* page_table) {
+ // We're initializing a user process.
+ m_svc_access_flags.reset();
+ m_irq_access_flags.reset();
+ m_debug_capabilities = 0;
+ m_handle_table_size = 0;
+ m_intended_kernel_version = 0;
+ m_program_type = 0;
+
+ // User processes must specify what cores/priorities they can use.
+ m_core_mask = 0;
+ m_priority_mask = 0;
+
+ // Parse the user capabilities array.
+ R_RETURN(this->SetCapabilities(user_caps, page_table));
+}
+
+Result KCapabilities::SetCorePriorityCapability(const u32 cap) {
+ // We can't set core/priority if we've already set them.
+ R_UNLESS(m_core_mask == 0, ResultInvalidArgument);
+ R_UNLESS(m_priority_mask == 0, ResultInvalidArgument);
+
+ // Validate the core/priority.
+ CorePriority pack{cap};
+ const u32 min_core = pack.minimum_core_id;
+ const u32 max_core = pack.maximum_core_id;
+ const u32 max_prio = pack.lowest_thread_priority;
+ const u32 min_prio = pack.highest_thread_priority;
+
+ R_UNLESS(min_core <= max_core, ResultInvalidCombination);
+ R_UNLESS(min_prio <= max_prio, ResultInvalidCombination);
+ R_UNLESS(max_core < Core::Hardware::NumVirtualCores, ResultInvalidCoreId);
+
+ ASSERT(max_prio < Common::BitSize<u64>());
+
+ // Set core mask.
+ for (auto core_id = min_core; core_id <= max_core; core_id++) {
+ m_core_mask |= (1ULL << core_id);
+ }
+ ASSERT((m_core_mask & Core::Hardware::VirtualCoreMask) == m_core_mask);
+
+ // Set physical core mask.
+ m_phys_core_mask = Core::Hardware::ConvertVirtualCoreMaskToPhysical(m_core_mask);
+
+ // Set priority mask.
+ for (auto prio = min_prio; prio <= max_prio; prio++) {
+ m_priority_mask |= (1ULL << prio);
+ }
+
+ // We must have some core/priority we can use.
+ R_UNLESS(m_core_mask != 0, ResultInvalidArgument);
+ R_UNLESS(m_priority_mask != 0, ResultInvalidArgument);
+
+ // Processes must not have access to kernel thread priorities.
+ R_UNLESS((m_priority_mask & 0xF) == 0, ResultInvalidArgument);
+
+ R_SUCCEED();
+}
+
+Result KCapabilities::SetSyscallMaskCapability(const u32 cap, u32& set_svc) {
+ // Validate the index.
+ SyscallMask pack{cap};
+ const u32 mask = pack.mask;
+ const u32 index = pack.index;
+
+ const u32 index_flag = (1U << index);
+ R_UNLESS((set_svc & index_flag) == 0, ResultInvalidCombination);
+ set_svc |= index_flag;
+
+ // Set SVCs.
+ for (size_t i = 0; i < decltype(SyscallMask::mask)::bits; i++) {
+ const u32 svc_id = static_cast<u32>(decltype(SyscallMask::mask)::bits * index + i);
+ if (mask & (1U << i)) {
+ R_UNLESS(this->SetSvcAllowed(svc_id), ResultOutOfRange);
+ }
+ }
+
+ R_SUCCEED();
+}
+
+Result KCapabilities::MapRange_(const u32 cap, const u32 size_cap, KPageTable* page_table) {
+ const auto range_pack = MapRange{cap};
+ const auto size_pack = MapRangeSize{size_cap};
+
+ // Get/validate address/size
+ const u64 phys_addr = range_pack.address.Value() * PageSize;
+
+ // Validate reserved bits are unused.
+ R_UNLESS(size_pack.reserved.Value() == 0, ResultOutOfRange);
+
+ const size_t num_pages = size_pack.pages;
+ const size_t size = num_pages * PageSize;
+ R_UNLESS(num_pages != 0, ResultInvalidSize);
+ R_UNLESS(phys_addr < phys_addr + size, ResultInvalidAddress);
+ R_UNLESS(((phys_addr + size - 1) & ~PhysicalMapAllowedMask) == 0, ResultInvalidAddress);
+
+ // Do the mapping.
+ [[maybe_unused]] const KMemoryPermission perm = range_pack.read_only.Value()
+ ? KMemoryPermission::UserRead
+ : KMemoryPermission::UserReadWrite;
+ if (MapRangeSize{size_cap}.normal) {
+ // R_RETURN(page_table->MapStatic(phys_addr, size, perm));
+ } else {
+ // R_RETURN(page_table->MapIo(phys_addr, size, perm));
+ }
+
+ UNIMPLEMENTED();
+ R_SUCCEED();
+}
+
+Result KCapabilities::MapIoPage_(const u32 cap, KPageTable* page_table) {
+ // Get/validate address/size
+ const u64 phys_addr = MapIoPage{cap}.address.Value() * PageSize;
+ const size_t num_pages = 1;
+ const size_t size = num_pages * PageSize;
+ R_UNLESS(num_pages != 0, ResultInvalidSize);
+ R_UNLESS(phys_addr < phys_addr + size, ResultInvalidAddress);
+ R_UNLESS(((phys_addr + size - 1) & ~PhysicalMapAllowedMask) == 0, ResultInvalidAddress);
+
+ // Do the mapping.
+ // R_RETURN(page_table->MapIo(phys_addr, size, KMemoryPermission_UserReadWrite));
+
+ UNIMPLEMENTED();
+ R_SUCCEED();
+}
+
+template <typename F>
+Result KCapabilities::ProcessMapRegionCapability(const u32 cap, F f) {
+ // Define the allowed memory regions.
+ constexpr std::array<KMemoryRegionType, 4> MemoryRegions{
+ KMemoryRegionType_None,
+ KMemoryRegionType_KernelTraceBuffer,
+ KMemoryRegionType_OnMemoryBootImage,
+ KMemoryRegionType_DTB,
+ };
+
+ // Extract regions/read only.
+ const MapRegion pack{cap};
+ const std::array<RegionType, 3> types{pack.region0, pack.region1, pack.region2};
+ const std::array<u32, 3> ro{pack.read_only0, pack.read_only1, pack.read_only2};
+
+ for (size_t i = 0; i < types.size(); i++) {
+ const auto type = types[i];
+ const auto perm = ro[i] ? KMemoryPermission::UserRead : KMemoryPermission::UserReadWrite;
+ switch (type) {
+ case RegionType::NoMapping:
+ break;
+ case RegionType::KernelTraceBuffer:
+ case RegionType::OnMemoryBootImage:
+ case RegionType::DTB:
+ R_TRY(f(MemoryRegions[static_cast<u32>(type)], perm));
+ break;
+ default:
+ R_THROW(ResultNotFound);
+ }
+ }
+
+ R_SUCCEED();
+}
+
+Result KCapabilities::MapRegion_(const u32 cap, KPageTable* page_table) {
+ // Map each region into the process's page table.
+ return ProcessMapRegionCapability(
+ cap, [](KMemoryRegionType region_type, KMemoryPermission perm) -> Result {
+ // R_RETURN(page_table->MapRegion(region_type, perm));
+ UNIMPLEMENTED();
+ R_SUCCEED();
+ });
+}
+
+Result KCapabilities::CheckMapRegion(KernelCore& kernel, const u32 cap) {
+ // Check that each region has a physical backing store.
+ return ProcessMapRegionCapability(
+ cap, [&](KMemoryRegionType region_type, KMemoryPermission perm) -> Result {
+ R_UNLESS(kernel.MemoryLayout().GetPhysicalMemoryRegionTree().FindFirstDerived(
+ region_type) != nullptr,
+ ResultOutOfRange);
+ R_SUCCEED();
+ });
+}
+
+Result KCapabilities::SetInterruptPairCapability(const u32 cap) {
+ // Extract interrupts.
+ const InterruptPair pack{cap};
+ const std::array<u32, 2> ids{pack.interrupt_id0, pack.interrupt_id1};
+
+ for (size_t i = 0; i < ids.size(); i++) {
+ if (ids[i] != PaddingInterruptId) {
+ UNIMPLEMENTED();
+ // R_UNLESS(Kernel::GetInterruptManager().IsInterruptDefined(ids[i]), ResultOutOfRange);
+ // R_UNLESS(this->SetInterruptPermitted(ids[i]), ResultOutOfRange);
+ }
+ }
+
+ R_SUCCEED();
+}
+
+Result KCapabilities::SetProgramTypeCapability(const u32 cap) {
+ // Validate.
+ const ProgramType pack{cap};
+ R_UNLESS(pack.reserved == 0, ResultReservedUsed);
+
+ m_program_type = pack.type;
+ R_SUCCEED();
+}
+
+Result KCapabilities::SetKernelVersionCapability(const u32 cap) {
+ // Ensure we haven't set our version before.
+ R_UNLESS(KernelVersion{m_intended_kernel_version}.major_version == 0, ResultInvalidArgument);
+
+ // Set, ensure that we set a valid version.
+ m_intended_kernel_version = cap;
+ R_UNLESS(KernelVersion{m_intended_kernel_version}.major_version != 0, ResultInvalidArgument);
+
+ R_SUCCEED();
+}
+
+Result KCapabilities::SetHandleTableCapability(const u32 cap) {
+ // Validate.
+ const HandleTable pack{cap};
+ R_UNLESS(pack.reserved == 0, ResultReservedUsed);
+
+ m_handle_table_size = pack.size;
+ R_SUCCEED();
+}
+
+Result KCapabilities::SetDebugFlagsCapability(const u32 cap) {
+ // Validate.
+ const DebugFlags pack{cap};
+ R_UNLESS(pack.reserved == 0, ResultReservedUsed);
+
+ DebugFlags debug_capabilities{m_debug_capabilities};
+ debug_capabilities.allow_debug.Assign(pack.allow_debug);
+ debug_capabilities.force_debug.Assign(pack.force_debug);
+ m_debug_capabilities = debug_capabilities.raw;
+
+ R_SUCCEED();
+}
+
+Result KCapabilities::SetCapability(const u32 cap, u32& set_flags, u32& set_svc,
+ KPageTable* page_table) {
+ // Validate this is a capability we can act on.
+ const auto type = GetCapabilityType(cap);
+ R_UNLESS(type != CapabilityType::Invalid, ResultInvalidArgument);
+
+ // If the type is padding, we have no work to do.
+ R_SUCCEED_IF(type == CapabilityType::Padding);
+
+ // Check that we haven't already processed this capability.
+ const auto flag = GetCapabilityFlag(type);
+ R_UNLESS(((set_flags & InitializeOnceFlags) & flag) == 0, ResultInvalidCombination);
+ set_flags |= flag;
+
+ // Process the capability.
+ switch (type) {
+ case CapabilityType::CorePriority:
+ R_RETURN(this->SetCorePriorityCapability(cap));
+ case CapabilityType::SyscallMask:
+ R_RETURN(this->SetSyscallMaskCapability(cap, set_svc));
+ case CapabilityType::MapIoPage:
+ R_RETURN(this->MapIoPage_(cap, page_table));
+ case CapabilityType::MapRegion:
+ R_RETURN(this->MapRegion_(cap, page_table));
+ case CapabilityType::InterruptPair:
+ R_RETURN(this->SetInterruptPairCapability(cap));
+ case CapabilityType::ProgramType:
+ R_RETURN(this->SetProgramTypeCapability(cap));
+ case CapabilityType::KernelVersion:
+ R_RETURN(this->SetKernelVersionCapability(cap));
+ case CapabilityType::HandleTable:
+ R_RETURN(this->SetHandleTableCapability(cap));
+ case CapabilityType::DebugFlags:
+ R_RETURN(this->SetDebugFlagsCapability(cap));
+ default:
+ R_THROW(ResultInvalidArgument);
+ }
+}
+
+Result KCapabilities::SetCapabilities(std::span<const u32> caps, KPageTable* page_table) {
+ u32 set_flags = 0, set_svc = 0;
+
+ for (size_t i = 0; i < caps.size(); i++) {
+ const u32 cap{caps[i]};
+
+ if (GetCapabilityType(cap) == CapabilityType::MapRange) {
+ // Check that the pair cap exists.
+ R_UNLESS((++i) < caps.size(), ResultInvalidCombination);
+
+ // Check the pair cap is a map range cap.
+ const u32 size_cap{caps[i]};
+ R_UNLESS(GetCapabilityType(size_cap) == CapabilityType::MapRange,
+ ResultInvalidCombination);
+
+ // Map the range.
+ R_TRY(this->MapRange_(cap, size_cap, page_table));
+ } else {
+ R_TRY(this->SetCapability(cap, set_flags, set_svc, page_table));
+ }
+ }
+
+ R_SUCCEED();
+}
+
+Result KCapabilities::CheckCapabilities(KernelCore& kernel, std::span<const u32> caps) {
+ for (auto cap : caps) {
+ // Check the capability refers to a valid region.
+ if (GetCapabilityType(cap) == CapabilityType::MapRegion) {
+ R_TRY(CheckMapRegion(kernel, cap));
+ }
+ }
+
+ R_SUCCEED();
+}
+
+} // 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 @@
+
+// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#pragma once
+
+#include <bitset>
+#include <span>
+
+#include "common/bit_field.h"
+#include "common/common_types.h"
+
+#include "core/hle/kernel/svc_types.h"
+#include "core/hle/result.h"
+
+namespace Kernel {
+
+class KPageTable;
+class KernelCore;
+
+class KCapabilities {
+public:
+ constexpr explicit KCapabilities() = default;
+
+ Result InitializeForKIP(std::span<const u32> kern_caps, KPageTable* page_table);
+ Result InitializeForUser(std::span<const u32> user_caps, KPageTable* page_table);
+
+ static Result CheckCapabilities(KernelCore& kernel, std::span<const u32> user_caps);
+
+ constexpr u64 GetCoreMask() const {
+ return m_core_mask;
+ }
+
+ constexpr u64 GetPhysicalCoreMask() const {
+ return m_phys_core_mask;
+ }
+
+ constexpr u64 GetPriorityMask() const {
+ return m_priority_mask;
+ }
+
+ constexpr s32 GetHandleTableSize() const {
+ return m_handle_table_size;
+ }
+
+ constexpr const Svc::SvcAccessFlagSet& GetSvcPermissions() const {
+ return m_svc_access_flags;
+ }
+
+ constexpr bool IsPermittedSvc(u32 id) const {
+ return (id < m_svc_access_flags.size()) && m_svc_access_flags[id];
+ }
+
+ constexpr bool IsPermittedInterrupt(u32 id) const {
+ return (id < m_irq_access_flags.size()) && m_irq_access_flags[id];
+ }
+
+ constexpr bool IsPermittedDebug() const {
+ return DebugFlags{m_debug_capabilities}.allow_debug.Value() != 0;
+ }
+
+ constexpr bool CanForceDebug() const {
+ return DebugFlags{m_debug_capabilities}.force_debug.Value() != 0;
+ }
+
+ constexpr u32 GetIntendedKernelMajorVersion() const {
+ return KernelVersion{m_intended_kernel_version}.major_version;
+ }
+
+ constexpr u32 GetIntendedKernelMinorVersion() const {
+ return KernelVersion{m_intended_kernel_version}.minor_version;
+ }
+
+private:
+ static constexpr size_t InterruptIdCount = 0x400;
+ using InterruptFlagSet = std::bitset<InterruptIdCount>;
+
+ enum class CapabilityType : u32 {
+ CorePriority = (1U << 3) - 1,
+ SyscallMask = (1U << 4) - 1,
+ MapRange = (1U << 6) - 1,
+ MapIoPage = (1U << 7) - 1,
+ MapRegion = (1U << 10) - 1,
+ InterruptPair = (1U << 11) - 1,
+ ProgramType = (1U << 13) - 1,
+ KernelVersion = (1U << 14) - 1,
+ HandleTable = (1U << 15) - 1,
+ DebugFlags = (1U << 16) - 1,
+
+ Invalid = 0U,
+ Padding = ~0U,
+ };
+
+ using RawCapabilityValue = u32;
+
+ static constexpr CapabilityType GetCapabilityType(const RawCapabilityValue value) {
+ return static_cast<CapabilityType>((~value & (value + 1)) - 1);
+ }
+
+ static constexpr u32 GetCapabilityFlag(CapabilityType type) {
+ return static_cast<u32>(type) + 1;
+ }
+
+ template <CapabilityType Type>
+ static constexpr inline u32 CapabilityFlag = static_cast<u32>(Type) + 1;
+
+ template <CapabilityType Type>
+ static constexpr inline u32 CapabilityId = std::countr_zero(CapabilityFlag<Type>);
+
+ union CorePriority {
+ static_assert(CapabilityId<CapabilityType::CorePriority> + 1 == 4);
+
+ RawCapabilityValue raw;
+ BitField<0, 4, CapabilityType> id;
+ BitField<4, 6, u32> lowest_thread_priority;
+ BitField<10, 6, u32> highest_thread_priority;
+ BitField<16, 8, u32> minimum_core_id;
+ BitField<24, 8, u32> maximum_core_id;
+ };
+
+ union SyscallMask {
+ static_assert(CapabilityId<CapabilityType::SyscallMask> + 1 == 5);
+
+ RawCapabilityValue raw;
+ BitField<0, 5, CapabilityType> id;
+ BitField<5, 24, u32> mask;
+ BitField<29, 3, u32> index;
+ };
+
+ // #undef MESOSPHERE_ENABLE_LARGE_PHYSICAL_ADDRESS_CAPABILITIES
+ static constexpr u64 PhysicalMapAllowedMask = (1ULL << 36) - 1;
+
+ union MapRange {
+ static_assert(CapabilityId<CapabilityType::MapRange> + 1 == 7);
+
+ RawCapabilityValue raw;
+ BitField<0, 7, CapabilityType> id;
+ BitField<7, 24, u32> address;
+ BitField<31, 1, u32> read_only;
+ };
+
+ union MapRangeSize {
+ static_assert(CapabilityId<CapabilityType::MapRange> + 1 == 7);
+
+ RawCapabilityValue raw;
+ BitField<0, 7, CapabilityType> id;
+ BitField<7, 20, u32> pages;
+ BitField<27, 4, u32> reserved;
+ BitField<31, 1, u32> normal;
+ };
+
+ union MapIoPage {
+ static_assert(CapabilityId<CapabilityType::MapIoPage> + 1 == 8);
+
+ RawCapabilityValue raw;
+ BitField<0, 8, CapabilityType> id;
+ BitField<8, 24, u32> address;
+ };
+
+ enum class RegionType : u32 {
+ NoMapping = 0,
+ KernelTraceBuffer = 1,
+ OnMemoryBootImage = 2,
+ DTB = 3,
+ };
+
+ union MapRegion {
+ static_assert(CapabilityId<CapabilityType::MapRegion> + 1 == 11);
+
+ RawCapabilityValue raw;
+ BitField<0, 11, CapabilityType> id;
+ BitField<11, 6, RegionType> region0;
+ BitField<17, 1, u32> read_only0;
+ BitField<18, 6, RegionType> region1;
+ BitField<24, 1, u32> read_only1;
+ BitField<25, 6, RegionType> region2;
+ BitField<31, 1, u32> read_only2;
+ };
+
+ union InterruptPair {
+ static_assert(CapabilityId<CapabilityType::InterruptPair> + 1 == 12);
+
+ RawCapabilityValue raw;
+ BitField<0, 12, CapabilityType> id;
+ BitField<12, 10, u32> interrupt_id0;
+ BitField<22, 10, u32> interrupt_id1;
+ };
+
+ union ProgramType {
+ static_assert(CapabilityId<CapabilityType::ProgramType> + 1 == 14);
+
+ RawCapabilityValue raw;
+ BitField<0, 14, CapabilityType> id;
+ BitField<14, 3, u32> type;
+ BitField<17, 15, u32> reserved;
+ };
+
+ union KernelVersion {
+ static_assert(CapabilityId<CapabilityType::KernelVersion> + 1 == 15);
+
+ RawCapabilityValue raw;
+ BitField<0, 15, CapabilityType> id;
+ BitField<15, 4, u32> major_version;
+ BitField<19, 13, u32> minor_version;
+ };
+
+ union HandleTable {
+ static_assert(CapabilityId<CapabilityType::HandleTable> + 1 == 16);
+
+ RawCapabilityValue raw;
+ BitField<0, 16, CapabilityType> id;
+ BitField<16, 10, u32> size;
+ BitField<26, 6, u32> reserved;
+ };
+
+ union DebugFlags {
+ static_assert(CapabilityId<CapabilityType::DebugFlags> + 1 == 17);
+
+ RawCapabilityValue raw;
+ BitField<0, 17, CapabilityType> id;
+ BitField<17, 1, u32> allow_debug;
+ BitField<18, 1, u32> force_debug;
+ BitField<19, 13, u32> reserved;
+ };
+
+ static_assert(sizeof(CorePriority) == 4);
+ static_assert(sizeof(SyscallMask) == 4);
+ static_assert(sizeof(MapRange) == 4);
+ static_assert(sizeof(MapRangeSize) == 4);
+ static_assert(sizeof(MapIoPage) == 4);
+ static_assert(sizeof(MapRegion) == 4);
+ static_assert(sizeof(InterruptPair) == 4);
+ static_assert(sizeof(ProgramType) == 4);
+ static_assert(sizeof(KernelVersion) == 4);
+ static_assert(sizeof(HandleTable) == 4);
+ static_assert(sizeof(DebugFlags) == 4);
+
+ static constexpr u32 InitializeOnceFlags =
+ CapabilityFlag<CapabilityType::CorePriority> | CapabilityFlag<CapabilityType::ProgramType> |
+ CapabilityFlag<CapabilityType::KernelVersion> |
+ CapabilityFlag<CapabilityType::HandleTable> | CapabilityFlag<CapabilityType::DebugFlags>;
+
+ static const u32 PaddingInterruptId = 0x3FF;
+ static_assert(PaddingInterruptId < InterruptIdCount);
+
+private:
+ constexpr bool SetSvcAllowed(u32 id) {
+ if (id < m_svc_access_flags.size()) [[likely]] {
+ m_svc_access_flags[id] = true;
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ constexpr bool SetInterruptPermitted(u32 id) {
+ if (id < m_irq_access_flags.size()) [[likely]] {
+ m_irq_access_flags[id] = true;
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ Result SetCorePriorityCapability(const u32 cap);
+ Result SetSyscallMaskCapability(const u32 cap, u32& set_svc);
+ Result MapRange_(const u32 cap, const u32 size_cap, KPageTable* page_table);
+ Result MapIoPage_(const u32 cap, KPageTable* page_table);
+ Result MapRegion_(const u32 cap, KPageTable* page_table);
+ Result SetInterruptPairCapability(const u32 cap);
+ Result SetProgramTypeCapability(const u32 cap);
+ Result SetKernelVersionCapability(const u32 cap);
+ Result SetHandleTableCapability(const u32 cap);
+ Result SetDebugFlagsCapability(const u32 cap);
+
+ template <typename F>
+ static Result ProcessMapRegionCapability(const u32 cap, F f);
+ static Result CheckMapRegion(KernelCore& kernel, const u32 cap);
+
+ Result SetCapability(const u32 cap, u32& set_flags, u32& set_svc, KPageTable* page_table);
+ Result SetCapabilities(std::span<const u32> caps, KPageTable* page_table);
+
+private:
+ Svc::SvcAccessFlagSet m_svc_access_flags{};
+ InterruptFlagSet m_irq_access_flags{};
+ u64 m_core_mask{};
+ u64 m_phys_core_mask{};
+ u64 m_priority_mask{};
+ u32 m_debug_capabilities{};
+ s32 m_handle_table_size{};
+ u32 m_intended_kernel_version{};
+ u32 m_program_type{};
+};
+
+} // namespace Kernel
diff --git a/src/core/hle/kernel/k_client_port.cpp b/src/core/hle/kernel/k_client_port.cpp
index 2ec623a58..c72a91a76 100644
--- a/src/core/hle/kernel/k_client_port.cpp
+++ b/src/core/hle/kernel/k_client_port.cpp
@@ -60,7 +60,8 @@ bool KClientPort::IsSignaled() const {
Result KClientPort::CreateSession(KClientSession** out) {
// Reserve a new session from the resource limit.
- KScopedResourceReservation session_reservation(kernel.CurrentProcess()->GetResourceLimit(),
+ //! FIXME: we are reserving this from the wrong resource limit!
+ KScopedResourceReservation session_reservation(kernel.ApplicationProcess()->GetResourceLimit(),
LimitableResource::SessionCountMax);
R_UNLESS(session_reservation.Succeeded(), ResultLimitReached);
diff --git a/src/core/hle/kernel/k_code_memory.cpp b/src/core/hle/kernel/k_code_memory.cpp
index 4b1c134d4..6c44a9e99 100644
--- a/src/core/hle/kernel/k_code_memory.cpp
+++ b/src/core/hle/kernel/k_code_memory.cpp
@@ -21,19 +21,19 @@ KCodeMemory::KCodeMemory(KernelCore& kernel_)
Result KCodeMemory::Initialize(Core::DeviceMemory& device_memory, VAddr addr, size_t size) {
// Set members.
- m_owner = kernel.CurrentProcess();
+ m_owner = GetCurrentProcessPointer(kernel);
// Get the owner page table.
auto& page_table = m_owner->PageTable();
// Construct the page group.
- m_page_group = {};
+ m_page_group.emplace(kernel, page_table.GetBlockInfoManager());
// Lock the memory.
- R_TRY(page_table.LockForCodeMemory(&m_page_group, addr, size))
+ R_TRY(page_table.LockForCodeMemory(std::addressof(*m_page_group), addr, size))
// Clear the memory.
- for (const auto& block : m_page_group.Nodes()) {
+ for (const auto& block : *m_page_group) {
std::memset(device_memory.GetPointer<void>(block.GetAddress()), 0xFF, block.GetSize());
}
@@ -51,12 +51,13 @@ Result KCodeMemory::Initialize(Core::DeviceMemory& device_memory, VAddr addr, si
void KCodeMemory::Finalize() {
// Unlock.
if (!m_is_mapped && !m_is_owner_mapped) {
- const size_t size = m_page_group.GetNumPages() * PageSize;
- m_owner->PageTable().UnlockForCodeMemory(m_address, size, m_page_group);
+ const size_t size = m_page_group->GetNumPages() * PageSize;
+ m_owner->PageTable().UnlockForCodeMemory(m_address, size, *m_page_group);
}
// Close the page group.
- m_page_group = {};
+ m_page_group->Close();
+ m_page_group->Finalize();
// Close our reference to our owner.
m_owner->Close();
@@ -64,7 +65,7 @@ void KCodeMemory::Finalize() {
Result KCodeMemory::Map(VAddr address, size_t size) {
// Validate the size.
- R_UNLESS(m_page_group.GetNumPages() == Common::DivideUp(size, PageSize), ResultInvalidSize);
+ R_UNLESS(m_page_group->GetNumPages() == Common::DivideUp(size, PageSize), ResultInvalidSize);
// Lock ourselves.
KScopedLightLock lk(m_lock);
@@ -73,8 +74,8 @@ Result KCodeMemory::Map(VAddr address, size_t size) {
R_UNLESS(!m_is_mapped, ResultInvalidState);
// Map the memory.
- R_TRY(kernel.CurrentProcess()->PageTable().MapPages(
- address, m_page_group, KMemoryState::CodeOut, KMemoryPermission::UserReadWrite));
+ R_TRY(GetCurrentProcess(kernel).PageTable().MapPageGroup(
+ address, *m_page_group, KMemoryState::CodeOut, KMemoryPermission::UserReadWrite));
// Mark ourselves as mapped.
m_is_mapped = true;
@@ -84,14 +85,14 @@ Result KCodeMemory::Map(VAddr address, size_t size) {
Result KCodeMemory::Unmap(VAddr address, size_t size) {
// Validate the size.
- R_UNLESS(m_page_group.GetNumPages() == Common::DivideUp(size, PageSize), ResultInvalidSize);
+ R_UNLESS(m_page_group->GetNumPages() == Common::DivideUp(size, PageSize), ResultInvalidSize);
// Lock ourselves.
KScopedLightLock lk(m_lock);
// Unmap the memory.
- R_TRY(kernel.CurrentProcess()->PageTable().UnmapPages(address, m_page_group,
- KMemoryState::CodeOut));
+ R_TRY(GetCurrentProcess(kernel).PageTable().UnmapPageGroup(address, *m_page_group,
+ KMemoryState::CodeOut));
// Mark ourselves as unmapped.
m_is_mapped = false;
@@ -101,7 +102,7 @@ Result KCodeMemory::Unmap(VAddr address, size_t size) {
Result KCodeMemory::MapToOwner(VAddr address, size_t size, Svc::MemoryPermission perm) {
// Validate the size.
- R_UNLESS(m_page_group.GetNumPages() == Common::DivideUp(size, PageSize), ResultInvalidSize);
+ R_UNLESS(m_page_group->GetNumPages() == Common::DivideUp(size, PageSize), ResultInvalidSize);
// Lock ourselves.
KScopedLightLock lk(m_lock);
@@ -124,8 +125,8 @@ Result KCodeMemory::MapToOwner(VAddr address, size_t size, Svc::MemoryPermission
}
// Map the memory.
- R_TRY(
- m_owner->PageTable().MapPages(address, m_page_group, KMemoryState::GeneratedCode, k_perm));
+ R_TRY(m_owner->PageTable().MapPageGroup(address, *m_page_group, KMemoryState::GeneratedCode,
+ k_perm));
// Mark ourselves as mapped.
m_is_owner_mapped = true;
@@ -135,13 +136,13 @@ Result KCodeMemory::MapToOwner(VAddr address, size_t size, Svc::MemoryPermission
Result KCodeMemory::UnmapFromOwner(VAddr address, size_t size) {
// Validate the size.
- R_UNLESS(m_page_group.GetNumPages() == Common::DivideUp(size, PageSize), ResultInvalidSize);
+ R_UNLESS(m_page_group->GetNumPages() == Common::DivideUp(size, PageSize), ResultInvalidSize);
// Lock ourselves.
KScopedLightLock lk(m_lock);
// Unmap the memory.
- R_TRY(m_owner->PageTable().UnmapPages(address, m_page_group, KMemoryState::GeneratedCode));
+ R_TRY(m_owner->PageTable().UnmapPageGroup(address, *m_page_group, KMemoryState::GeneratedCode));
// Mark ourselves as unmapped.
m_is_owner_mapped = false;
diff --git a/src/core/hle/kernel/k_code_memory.h b/src/core/hle/kernel/k_code_memory.h
index 2e7e1436a..5b260b385 100644
--- a/src/core/hle/kernel/k_code_memory.h
+++ b/src/core/hle/kernel/k_code_memory.h
@@ -3,6 +3,8 @@
#pragma once
+#include <optional>
+
#include "common/common_types.h"
#include "core/device_memory.h"
#include "core/hle/kernel/k_auto_object.h"
@@ -49,11 +51,11 @@ public:
return m_address;
}
size_t GetSize() const {
- return m_is_initialized ? m_page_group.GetNumPages() * PageSize : 0;
+ return m_is_initialized ? m_page_group->GetNumPages() * PageSize : 0;
}
private:
- KPageGroup m_page_group{};
+ std::optional<KPageGroup> m_page_group{};
KProcess* m_owner{};
VAddr m_address{};
KLightLock m_lock;
diff --git a/src/core/hle/kernel/k_condition_variable.cpp b/src/core/hle/kernel/k_condition_variable.cpp
index 124149697..3f0be1c3f 100644
--- a/src/core/hle/kernel/k_condition_variable.cpp
+++ b/src/core/hle/kernel/k_condition_variable.cpp
@@ -164,14 +164,14 @@ Result KConditionVariable::WaitForAddress(Handle handle, VAddr addr, u32 value)
R_SUCCEED_IF(test_tag != (handle | Svc::HandleWaitMask));
// Get the lock owner thread.
- owner_thread = kernel.CurrentProcess()
- ->GetHandleTable()
+ owner_thread = GetCurrentProcess(kernel)
+ .GetHandleTable()
.GetObjectWithoutPseudoHandle<KThread>(handle)
.ReleasePointerUnsafe();
R_UNLESS(owner_thread != nullptr, ResultInvalidHandle);
// Update the lock.
- cur_thread->SetAddressKey(addr, value);
+ cur_thread->SetUserAddressKey(addr, value);
owner_thread->AddWaiter(cur_thread);
// Begin waiting.
@@ -213,8 +213,8 @@ void KConditionVariable::SignalImpl(KThread* thread) {
thread->EndWait(ResultSuccess);
} else {
// Get the previous owner.
- KThread* owner_thread = kernel.CurrentProcess()
- ->GetHandleTable()
+ KThread* owner_thread = GetCurrentProcess(kernel)
+ .GetHandleTable()
.GetObjectWithoutPseudoHandle<KThread>(
static_cast<Handle>(prev_tag & ~Svc::HandleWaitMask))
.ReleasePointerUnsafe();
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 @@
+// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include "common/assert.h"
+#include "core/core.h"
+#include "core/hle/kernel/k_device_address_space.h"
+#include "core/hle/kernel/kernel.h"
+#include "core/hle/kernel/svc_results.h"
+
+namespace Kernel {
+
+KDeviceAddressSpace::KDeviceAddressSpace(KernelCore& kernel_)
+ : KAutoObjectWithSlabHeapAndContainer(kernel_), m_lock(kernel_), m_is_initialized(false) {}
+KDeviceAddressSpace::~KDeviceAddressSpace() = default;
+
+void KDeviceAddressSpace::Initialize() {
+ // This just forwards to the device page table manager.
+ // KDevicePageTable::Initialize();
+}
+
+// Member functions.
+Result KDeviceAddressSpace::Initialize(u64 address, u64 size) {
+ // Initialize the device page table.
+ // R_TRY(m_table.Initialize(address, size));
+
+ // Set member variables.
+ m_space_address = address;
+ m_space_size = size;
+ m_is_initialized = true;
+
+ R_SUCCEED();
+}
+
+void KDeviceAddressSpace::Finalize() {
+ // Finalize the table.
+ // m_table.Finalize();
+}
+
+Result KDeviceAddressSpace::Attach(Svc::DeviceName device_name) {
+ // Lock the address space.
+ KScopedLightLock lk(m_lock);
+
+ // Attach.
+ // R_RETURN(m_table.Attach(device_name, m_space_address, m_space_size));
+ R_SUCCEED();
+}
+
+Result KDeviceAddressSpace::Detach(Svc::DeviceName device_name) {
+ // Lock the address space.
+ KScopedLightLock lk(m_lock);
+
+ // Detach.
+ // R_RETURN(m_table.Detach(device_name));
+ R_SUCCEED();
+}
+
+Result KDeviceAddressSpace::Map(KPageTable* page_table, VAddr process_address, size_t size,
+ u64 device_address, u32 option, bool is_aligned) {
+ // Check that the address falls within the space.
+ R_UNLESS((m_space_address <= device_address &&
+ device_address + size - 1 <= m_space_address + m_space_size - 1),
+ ResultInvalidCurrentMemory);
+
+ // Decode the option.
+ const Svc::MapDeviceAddressSpaceOption option_pack{option};
+ const auto device_perm = option_pack.permission.Value();
+ const auto flags = option_pack.flags.Value();
+ const auto reserved = option_pack.reserved.Value();
+
+ // Validate the option.
+ // TODO: It is likely that this check for flags == none is only on NX board.
+ R_UNLESS(flags == Svc::MapDeviceAddressSpaceFlag::None, ResultInvalidEnumValue);
+ R_UNLESS(reserved == 0, ResultInvalidEnumValue);
+
+ // Lock the address space.
+ KScopedLightLock lk(m_lock);
+
+ // Lock the page table to prevent concurrent device mapping operations.
+ // KScopedLightLock pt_lk = page_table->AcquireDeviceMapLock();
+
+ // Lock the pages.
+ bool is_io{};
+ R_TRY(page_table->LockForMapDeviceAddressSpace(std::addressof(is_io), process_address, size,
+ ConvertToKMemoryPermission(device_perm),
+ is_aligned, true));
+
+ // Ensure that if we fail, we don't keep unmapped pages locked.
+ ON_RESULT_FAILURE {
+ ASSERT(page_table->UnlockForDeviceAddressSpace(process_address, size) == ResultSuccess);
+ };
+
+ // Check that the io status is allowable.
+ if (is_io) {
+ R_UNLESS(static_cast<u32>(flags & Svc::MapDeviceAddressSpaceFlag::NotIoRegister) == 0,
+ ResultInvalidCombination);
+ }
+
+ // Map the pages.
+ {
+ // Perform the mapping.
+ // R_TRY(m_table.Map(page_table, process_address, size, device_address, device_perm,
+ // is_aligned, is_io));
+
+ // Ensure that we unmap the pages if we fail to update the protections.
+ // NOTE: Nintendo does not check the result of this unmap call.
+ // ON_RESULT_FAILURE { m_table.Unmap(device_address, size); };
+
+ // Update the protections in accordance with how much we mapped.
+ // R_TRY(page_table->UnlockForDeviceAddressSpacePartialMap(process_address, size));
+ }
+
+ // We succeeded.
+ R_SUCCEED();
+}
+
+Result KDeviceAddressSpace::Unmap(KPageTable* page_table, VAddr process_address, size_t size,
+ u64 device_address) {
+ // Check that the address falls within the space.
+ R_UNLESS((m_space_address <= device_address &&
+ device_address + size - 1 <= m_space_address + m_space_size - 1),
+ ResultInvalidCurrentMemory);
+
+ // Lock the address space.
+ KScopedLightLock lk(m_lock);
+
+ // Lock the page table to prevent concurrent device mapping operations.
+ // KScopedLightLock pt_lk = page_table->AcquireDeviceMapLock();
+
+ // Lock the pages.
+ R_TRY(page_table->LockForUnmapDeviceAddressSpace(process_address, size, true));
+
+ // Unmap the pages.
+ {
+ // If we fail to unmap, we want to do a partial unlock.
+ // ON_RESULT_FAILURE {
+ // ASSERT(page_table->UnlockForDeviceAddressSpacePartialMap(process_address, size) ==
+ // ResultSuccess);
+ // };
+
+ // Perform the unmap.
+ // R_TRY(m_table.Unmap(page_table, process_address, size, device_address));
+ }
+
+ // Unlock the pages.
+ ASSERT(page_table->UnlockForDeviceAddressSpace(process_address, size) == ResultSuccess);
+
+ R_SUCCEED();
+}
+
+} // 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 @@
+// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#pragma once
+
+#include <string>
+
+#include "common/common_types.h"
+#include "core/hle/kernel/k_page_table.h"
+#include "core/hle/kernel/slab_helpers.h"
+#include "core/hle/result.h"
+
+namespace Kernel {
+
+class KDeviceAddressSpace final
+ : public KAutoObjectWithSlabHeapAndContainer<KDeviceAddressSpace, KAutoObjectWithList> {
+ KERNEL_AUTOOBJECT_TRAITS(KDeviceAddressSpace, KAutoObject);
+
+public:
+ explicit KDeviceAddressSpace(KernelCore& kernel);
+ ~KDeviceAddressSpace();
+
+ Result Initialize(u64 address, u64 size);
+ void Finalize();
+
+ bool IsInitialized() const {
+ return m_is_initialized;
+ }
+ static void PostDestroy(uintptr_t arg) {}
+
+ Result Attach(Svc::DeviceName device_name);
+ Result Detach(Svc::DeviceName device_name);
+
+ Result MapByForce(KPageTable* page_table, VAddr process_address, size_t size,
+ u64 device_address, u32 option) {
+ R_RETURN(this->Map(page_table, process_address, size, device_address, option, false));
+ }
+
+ Result MapAligned(KPageTable* page_table, VAddr process_address, size_t size,
+ u64 device_address, u32 option) {
+ R_RETURN(this->Map(page_table, process_address, size, device_address, option, true));
+ }
+
+ Result Unmap(KPageTable* page_table, VAddr process_address, size_t size, u64 device_address);
+
+ static void Initialize();
+
+private:
+ Result Map(KPageTable* page_table, VAddr process_address, size_t size, u64 device_address,
+ u32 option, bool is_aligned);
+
+private:
+ KLightLock m_lock;
+ // KDevicePageTable m_table;
+ u64 m_space_address{};
+ u64 m_space_size{};
+ bool m_is_initialized{};
+};
+
+} // namespace Kernel
diff --git a/src/core/hle/kernel/k_handle_table.h b/src/core/hle/kernel/k_handle_table.h
index 37a24e7d9..d7660630c 100644
--- a/src/core/hle/kernel/k_handle_table.h
+++ b/src/core/hle/kernel/k_handle_table.h
@@ -90,7 +90,8 @@ public:
// Handle pseudo-handles.
if constexpr (std::derived_from<KProcess, T>) {
if (handle == Svc::PseudoHandle::CurrentProcess) {
- auto* const cur_process = m_kernel.CurrentProcess();
+ //! FIXME: this is the wrong process!
+ auto* const cur_process = m_kernel.ApplicationProcess();
ASSERT(cur_process != nullptr);
return cur_process;
}
diff --git a/src/core/hle/kernel/k_hardware_timer.cpp b/src/core/hle/kernel/k_hardware_timer.cpp
index 6bba79ea0..4dcd53821 100644
--- a/src/core/hle/kernel/k_hardware_timer.cpp
+++ b/src/core/hle/kernel/k_hardware_timer.cpp
@@ -18,7 +18,8 @@ void KHardwareTimer::Initialize() {
}
void KHardwareTimer::Finalize() {
- this->DisableInterrupt();
+ m_kernel.System().CoreTiming().UnscheduleEvent(m_event_type, reinterpret_cast<uintptr_t>(this));
+ m_wakeup_time = std::numeric_limits<s64>::max();
m_event_type.reset();
}
@@ -59,7 +60,8 @@ void KHardwareTimer::EnableInterrupt(s64 wakeup_time) {
}
void KHardwareTimer::DisableInterrupt() {
- m_kernel.System().CoreTiming().UnscheduleEvent(m_event_type, reinterpret_cast<uintptr_t>(this));
+ m_kernel.System().CoreTiming().UnscheduleEventWithoutWait(m_event_type,
+ reinterpret_cast<uintptr_t>(this));
m_wakeup_time = std::numeric_limits<s64>::max();
}
diff --git a/src/core/hle/kernel/k_interrupt_manager.cpp b/src/core/hle/kernel/k_interrupt_manager.cpp
index 4a6b60d26..fe6a20168 100644
--- a/src/core/hle/kernel/k_interrupt_manager.cpp
+++ b/src/core/hle/kernel/k_interrupt_manager.cpp
@@ -16,7 +16,7 @@ void HandleInterrupt(KernelCore& kernel, s32 core_id) {
auto& current_thread = GetCurrentThread(kernel);
- if (auto* process = kernel.CurrentProcess(); process) {
+ if (auto* process = GetCurrentProcessPointer(kernel); process) {
// If the user disable count is set, we may need to pin the current thread.
if (current_thread.GetUserDisableCount() && !process->GetPinnedThread(core_id)) {
KScopedSchedulerLock sl{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) {
// Add the current thread as a waiter on the owner.
KThread* owner_thread = reinterpret_cast<KThread*>(_owner & ~1ULL);
- cur_thread->SetAddressKey(reinterpret_cast<uintptr_t>(std::addressof(tag)));
+ cur_thread->SetKernelAddressKey(reinterpret_cast<uintptr_t>(std::addressof(tag)));
owner_thread->AddWaiter(cur_thread);
// 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;
constexpr std::size_t KernelResourceSize = KernelPageTableHeapSize + KernelInitialPageHeapSize +
KernelSlabHeapSize + KernelPageBufferHeapSize;
-constexpr bool IsKernelAddressKey(VAddr key) {
- return KernelVirtualAddressSpaceBase <= key && key <= KernelVirtualAddressSpaceLast;
-}
+//! NB: Use KThread::GetAddressKeyIsKernel().
+//! See explanation for deviation of GetAddressKey.
+bool IsKernelAddressKey(VAddr key) = delete;
constexpr bool IsKernelAddress(VAddr address) {
return KernelVirtualAddressSpaceBase <= address && address < KernelVirtualAddressSpaceEnd;
diff --git a/src/core/hle/kernel/k_memory_manager.cpp b/src/core/hle/kernel/k_memory_manager.cpp
index bd33571da..cd6ea388e 100644
--- a/src/core/hle/kernel/k_memory_manager.cpp
+++ b/src/core/hle/kernel/k_memory_manager.cpp
@@ -223,7 +223,7 @@ Result KMemoryManager::AllocatePageGroupImpl(KPageGroup* out, size_t num_pages,
// Ensure that we don't leave anything un-freed.
ON_RESULT_FAILURE {
- for (const auto& it : out->Nodes()) {
+ for (const auto& it : *out) {
auto& manager = this->GetManager(it.GetAddress());
const size_t node_num_pages = std::min<u64>(
it.GetNumPages(), (manager.GetEndAddress() - it.GetAddress()) / PageSize);
@@ -285,7 +285,7 @@ Result KMemoryManager::AllocateAndOpen(KPageGroup* out, size_t num_pages, u32 op
m_has_optimized_process[static_cast<size_t>(pool)], true));
// Open the first reference to the pages.
- for (const auto& block : out->Nodes()) {
+ for (const auto& block : *out) {
PAddr cur_address = block.GetAddress();
size_t remaining_pages = block.GetNumPages();
while (remaining_pages > 0) {
@@ -335,7 +335,7 @@ Result KMemoryManager::AllocateForProcess(KPageGroup* out, size_t num_pages, u32
// Perform optimized memory tracking, if we should.
if (optimized) {
// Iterate over the allocated blocks.
- for (const auto& block : out->Nodes()) {
+ for (const auto& block : *out) {
// Get the block extents.
const PAddr block_address = block.GetAddress();
const size_t block_pages = block.GetNumPages();
@@ -391,7 +391,7 @@ Result KMemoryManager::AllocateForProcess(KPageGroup* out, size_t num_pages, u32
}
} else {
// Set all the allocated memory.
- for (const auto& block : out->Nodes()) {
+ for (const auto& block : *out) {
std::memset(m_system.DeviceMemory().GetPointer<void>(block.GetAddress()), fill_pattern,
block.GetSize());
}
diff --git a/src/core/hle/kernel/k_object_name.cpp b/src/core/hle/kernel/k_object_name.cpp
new file mode 100644
index 000000000..df3a1c4c5
--- /dev/null
+++ b/src/core/hle/kernel/k_object_name.cpp
@@ -0,0 +1,102 @@
+// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include "core/hle/kernel/k_object_name.h"
+
+namespace Kernel {
+
+KObjectNameGlobalData::KObjectNameGlobalData(KernelCore& kernel) : m_object_list_lock{kernel} {}
+KObjectNameGlobalData::~KObjectNameGlobalData() = default;
+
+void KObjectName::Initialize(KAutoObject* obj, const char* name) {
+ // Set member variables.
+ m_object = obj;
+ std::strncpy(m_name.data(), name, sizeof(m_name) - 1);
+ m_name[sizeof(m_name) - 1] = '\x00';
+
+ // Open a reference to the object we hold.
+ m_object->Open();
+}
+
+bool KObjectName::MatchesName(const char* name) const {
+ return std::strncmp(m_name.data(), name, sizeof(m_name)) == 0;
+}
+
+Result KObjectName::NewFromName(KernelCore& kernel, KAutoObject* obj, const char* name) {
+ // Create a new object name.
+ KObjectName* new_name = KObjectName::Allocate(kernel);
+ R_UNLESS(new_name != nullptr, ResultOutOfResource);
+
+ // Initialize the new name.
+ new_name->Initialize(obj, name);
+
+ // Check if there's an existing name.
+ {
+ // Get the global data.
+ KObjectNameGlobalData& gd{kernel.ObjectNameGlobalData()};
+
+ // Ensure we have exclusive access to the global list.
+ KScopedLightLock lk{gd.GetObjectListLock()};
+
+ // If the object doesn't exist, put it into the list.
+ KScopedAutoObject existing_object = FindImpl(kernel, name);
+ if (existing_object.IsNull()) {
+ gd.GetObjectList().push_back(*new_name);
+ R_SUCCEED();
+ }
+ }
+
+ // The object already exists, which is an error condition. Perform cleanup.
+ obj->Close();
+ KObjectName::Free(kernel, new_name);
+ R_THROW(ResultInvalidState);
+}
+
+Result KObjectName::Delete(KernelCore& kernel, KAutoObject* obj, const char* compare_name) {
+ // Get the global data.
+ KObjectNameGlobalData& gd{kernel.ObjectNameGlobalData()};
+
+ // Ensure we have exclusive access to the global list.
+ KScopedLightLock lk{gd.GetObjectListLock()};
+
+ // Find a matching entry in the list, and delete it.
+ for (auto& name : gd.GetObjectList()) {
+ if (name.MatchesName(compare_name) && obj == name.GetObject()) {
+ // We found a match, clean up its resources.
+ obj->Close();
+ gd.GetObjectList().erase(gd.GetObjectList().iterator_to(name));
+ KObjectName::Free(kernel, std::addressof(name));
+ R_SUCCEED();
+ }
+ }
+
+ // We didn't find the object in the list.
+ R_THROW(ResultNotFound);
+}
+
+KScopedAutoObject<KAutoObject> KObjectName::Find(KernelCore& kernel, const char* name) {
+ // Get the global data.
+ KObjectNameGlobalData& gd{kernel.ObjectNameGlobalData()};
+
+ // Ensure we have exclusive access to the global list.
+ KScopedLightLock lk{gd.GetObjectListLock()};
+
+ return FindImpl(kernel, name);
+}
+
+KScopedAutoObject<KAutoObject> KObjectName::FindImpl(KernelCore& kernel, const char* compare_name) {
+ // Get the global data.
+ KObjectNameGlobalData& gd{kernel.ObjectNameGlobalData()};
+
+ // Try to find a matching object in the global list.
+ for (const auto& name : gd.GetObjectList()) {
+ if (name.MatchesName(compare_name)) {
+ return name.GetObject();
+ }
+ }
+
+ // There's no matching entry in the list.
+ return nullptr;
+}
+
+} // namespace Kernel
diff --git a/src/core/hle/kernel/k_object_name.h b/src/core/hle/kernel/k_object_name.h
new file mode 100644
index 000000000..b7f943134
--- /dev/null
+++ b/src/core/hle/kernel/k_object_name.h
@@ -0,0 +1,86 @@
+// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#pragma once
+
+#include <array>
+#include <memory>
+#include <boost/intrusive/list.hpp>
+
+#include "core/hle/kernel/k_light_lock.h"
+#include "core/hle/kernel/slab_helpers.h"
+#include "core/hle/kernel/svc_results.h"
+
+namespace Kernel {
+
+class KObjectNameGlobalData;
+
+class KObjectName : public KSlabAllocated<KObjectName>, public boost::intrusive::list_base_hook<> {
+public:
+ explicit KObjectName(KernelCore&) {}
+ virtual ~KObjectName() = default;
+
+ static constexpr size_t NameLengthMax = 12;
+ using List = boost::intrusive::list<KObjectName>;
+
+ static Result NewFromName(KernelCore& kernel, KAutoObject* obj, const char* name);
+ static Result Delete(KernelCore& kernel, KAutoObject* obj, const char* name);
+
+ static KScopedAutoObject<KAutoObject> Find(KernelCore& kernel, const char* name);
+
+ template <typename Derived>
+ static Result Delete(KernelCore& kernel, const char* name) {
+ // Find the object.
+ KScopedAutoObject obj = Find(kernel, name);
+ R_UNLESS(obj.IsNotNull(), ResultNotFound);
+
+ // Cast the object to the desired type.
+ Derived* derived = obj->DynamicCast<Derived*>();
+ R_UNLESS(derived != nullptr, ResultNotFound);
+
+ // Check that the object is closed.
+ R_UNLESS(derived->IsServerClosed(), ResultInvalidState);
+
+ return Delete(kernel, obj.GetPointerUnsafe(), name);
+ }
+
+ template <typename Derived>
+ requires(std::derived_from<Derived, KAutoObject>)
+ static KScopedAutoObject<Derived> Find(KernelCore& kernel, const char* name) {
+ return Find(kernel, name);
+ }
+
+private:
+ static KScopedAutoObject<KAutoObject> FindImpl(KernelCore& kernel, const char* name);
+
+ void Initialize(KAutoObject* obj, const char* name);
+
+ bool MatchesName(const char* name) const;
+ KAutoObject* GetObject() const {
+ return m_object;
+ }
+
+private:
+ std::array<char, NameLengthMax> m_name{};
+ KAutoObject* m_object{};
+};
+
+class KObjectNameGlobalData {
+public:
+ explicit KObjectNameGlobalData(KernelCore& kernel);
+ ~KObjectNameGlobalData();
+
+ KLightLock& GetObjectListLock() {
+ return m_object_list_lock;
+ }
+
+ KObjectName::List& GetObjectList() {
+ return m_object_list;
+ }
+
+private:
+ KLightLock m_object_list_lock;
+ KObjectName::List m_object_list;
+};
+
+} // namespace Kernel
diff --git a/src/core/hle/kernel/k_page_group.cpp b/src/core/hle/kernel/k_page_group.cpp
new file mode 100644
index 000000000..d8c644a33
--- /dev/null
+++ b/src/core/hle/kernel/k_page_group.cpp
@@ -0,0 +1,121 @@
+// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include "core/hle/kernel/k_dynamic_resource_manager.h"
+#include "core/hle/kernel/k_memory_manager.h"
+#include "core/hle/kernel/k_page_group.h"
+#include "core/hle/kernel/kernel.h"
+#include "core/hle/kernel/svc_results.h"
+
+namespace Kernel {
+
+void KPageGroup::Finalize() {
+ KBlockInfo* cur = m_first_block;
+ while (cur != nullptr) {
+ KBlockInfo* next = cur->GetNext();
+ m_manager->Free(cur);
+ cur = next;
+ }
+
+ m_first_block = nullptr;
+ m_last_block = nullptr;
+}
+
+void KPageGroup::CloseAndReset() {
+ auto& mm = m_kernel.MemoryManager();
+
+ KBlockInfo* cur = m_first_block;
+ while (cur != nullptr) {
+ KBlockInfo* next = cur->GetNext();
+ mm.Close(cur->GetAddress(), cur->GetNumPages());
+ m_manager->Free(cur);
+ cur = next;
+ }
+
+ m_first_block = nullptr;
+ m_last_block = nullptr;
+}
+
+size_t KPageGroup::GetNumPages() const {
+ size_t num_pages = 0;
+
+ for (const auto& it : *this) {
+ num_pages += it.GetNumPages();
+ }
+
+ return num_pages;
+}
+
+Result KPageGroup::AddBlock(KPhysicalAddress addr, size_t num_pages) {
+ // Succeed immediately if we're adding no pages.
+ R_SUCCEED_IF(num_pages == 0);
+
+ // Check for overflow.
+ ASSERT(addr < addr + num_pages * PageSize);
+
+ // Try to just append to the last block.
+ if (m_last_block != nullptr) {
+ R_SUCCEED_IF(m_last_block->TryConcatenate(addr, num_pages));
+ }
+
+ // Allocate a new block.
+ KBlockInfo* new_block = m_manager->Allocate();
+ R_UNLESS(new_block != nullptr, ResultOutOfResource);
+
+ // Initialize the block.
+ new_block->Initialize(addr, num_pages);
+
+ // Add the block to our list.
+ if (m_last_block != nullptr) {
+ m_last_block->SetNext(new_block);
+ } else {
+ m_first_block = new_block;
+ }
+ m_last_block = new_block;
+
+ R_SUCCEED();
+}
+
+void KPageGroup::Open() const {
+ auto& mm = m_kernel.MemoryManager();
+
+ for (const auto& it : *this) {
+ mm.Open(it.GetAddress(), it.GetNumPages());
+ }
+}
+
+void KPageGroup::OpenFirst() const {
+ auto& mm = m_kernel.MemoryManager();
+
+ for (const auto& it : *this) {
+ mm.OpenFirst(it.GetAddress(), it.GetNumPages());
+ }
+}
+
+void KPageGroup::Close() const {
+ auto& mm = m_kernel.MemoryManager();
+
+ for (const auto& it : *this) {
+ mm.Close(it.GetAddress(), it.GetNumPages());
+ }
+}
+
+bool KPageGroup::IsEquivalentTo(const KPageGroup& rhs) const {
+ auto lit = this->begin();
+ auto rit = rhs.begin();
+ auto lend = this->end();
+ auto rend = rhs.end();
+
+ while (lit != lend && rit != rend) {
+ if (*lit != *rit) {
+ return false;
+ }
+
+ ++lit;
+ ++rit;
+ }
+
+ return lit == lend && rit == rend;
+}
+
+} // namespace Kernel
diff --git a/src/core/hle/kernel/k_page_group.h b/src/core/hle/kernel/k_page_group.h
index 316f172f2..c07f17663 100644
--- a/src/core/hle/kernel/k_page_group.h
+++ b/src/core/hle/kernel/k_page_group.h
@@ -1,4 +1,4 @@
-// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
+// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
@@ -13,24 +13,23 @@
namespace Kernel {
+class KBlockInfoManager;
+class KernelCore;
class KPageGroup;
class KBlockInfo {
-private:
- friend class KPageGroup;
-
public:
- constexpr KBlockInfo() = default;
+ constexpr explicit KBlockInfo() : m_next(nullptr) {}
- constexpr void Initialize(PAddr addr, size_t np) {
+ constexpr void Initialize(KPhysicalAddress addr, size_t np) {
ASSERT(Common::IsAligned(addr, PageSize));
ASSERT(static_cast<u32>(np) == np);
- m_page_index = static_cast<u32>(addr) / PageSize;
+ m_page_index = static_cast<u32>(addr / PageSize);
m_num_pages = static_cast<u32>(np);
}
- constexpr PAddr GetAddress() const {
+ constexpr KPhysicalAddress GetAddress() const {
return m_page_index * PageSize;
}
constexpr size_t GetNumPages() const {
@@ -39,10 +38,10 @@ public:
constexpr size_t GetSize() const {
return this->GetNumPages() * PageSize;
}
- constexpr PAddr GetEndAddress() const {
+ constexpr KPhysicalAddress GetEndAddress() const {
return (m_page_index + m_num_pages) * PageSize;
}
- constexpr PAddr GetLastAddress() const {
+ constexpr KPhysicalAddress GetLastAddress() const {
return this->GetEndAddress() - 1;
}
@@ -62,8 +61,8 @@ public:
return !(*this == rhs);
}
- constexpr bool IsStrictlyBefore(PAddr addr) const {
- const PAddr end = this->GetEndAddress();
+ constexpr bool IsStrictlyBefore(KPhysicalAddress addr) const {
+ const KPhysicalAddress end = this->GetEndAddress();
if (m_page_index != 0 && end == 0) {
return false;
@@ -72,11 +71,11 @@ public:
return end < addr;
}
- constexpr bool operator<(PAddr addr) const {
+ constexpr bool operator<(KPhysicalAddress addr) const {
return this->IsStrictlyBefore(addr);
}
- constexpr bool TryConcatenate(PAddr addr, size_t np) {
+ constexpr bool TryConcatenate(KPhysicalAddress addr, size_t np) {
if (addr != 0 && addr == this->GetEndAddress()) {
m_num_pages += static_cast<u32>(np);
return true;
@@ -90,96 +89,118 @@ private:
}
private:
+ friend class KPageGroup;
+
KBlockInfo* m_next{};
u32 m_page_index{};
u32 m_num_pages{};
};
static_assert(sizeof(KBlockInfo) <= 0x10);
-class KPageGroup final {
+class KPageGroup {
public:
- class Node final {
+ class Iterator {
public:
- constexpr Node(u64 addr_, std::size_t num_pages_) : addr{addr_}, num_pages{num_pages_} {}
+ using iterator_category = std::forward_iterator_tag;
+ using value_type = const KBlockInfo;
+ using difference_type = std::ptrdiff_t;
+ using pointer = value_type*;
+ using reference = value_type&;
+
+ constexpr explicit Iterator(pointer n) : m_node(n) {}
+
+ constexpr bool operator==(const Iterator& rhs) const {
+ return m_node == rhs.m_node;
+ }
+ constexpr bool operator!=(const Iterator& rhs) const {
+ return !(*this == rhs);
+ }
- constexpr u64 GetAddress() const {
- return addr;
+ constexpr pointer operator->() const {
+ return m_node;
+ }
+ constexpr reference operator*() const {
+ return *m_node;
}
- constexpr std::size_t GetNumPages() const {
- return num_pages;
+ constexpr Iterator& operator++() {
+ m_node = m_node->GetNext();
+ return *this;
}
- constexpr std::size_t GetSize() const {
- return GetNumPages() * PageSize;
+ constexpr Iterator operator++(int) {
+ const Iterator it{*this};
+ ++(*this);
+ return it;
}
private:
- u64 addr{};
- std::size_t num_pages{};
+ pointer m_node{};
};
-public:
- KPageGroup() = default;
- KPageGroup(u64 address, u64 num_pages) {
- ASSERT(AddBlock(address, num_pages).IsSuccess());
+ explicit KPageGroup(KernelCore& kernel, KBlockInfoManager* m)
+ : m_kernel{kernel}, m_manager{m} {}
+ ~KPageGroup() {
+ this->Finalize();
}
- constexpr std::list<Node>& Nodes() {
- return nodes;
- }
+ void CloseAndReset();
+ void Finalize();
- constexpr const std::list<Node>& Nodes() const {
- return nodes;
+ Iterator begin() const {
+ return Iterator{m_first_block};
+ }
+ Iterator end() const {
+ return Iterator{nullptr};
+ }
+ bool empty() const {
+ return m_first_block == nullptr;
}
- std::size_t GetNumPages() const {
- std::size_t num_pages = 0;
- for (const Node& node : nodes) {
- num_pages += node.GetNumPages();
- }
- return num_pages;
- }
-
- bool IsEqual(KPageGroup& other) const {
- auto this_node = nodes.begin();
- auto other_node = other.nodes.begin();
- while (this_node != nodes.end() && other_node != other.nodes.end()) {
- if (this_node->GetAddress() != other_node->GetAddress() ||
- this_node->GetNumPages() != other_node->GetNumPages()) {
- return false;
- }
- this_node = std::next(this_node);
- other_node = std::next(other_node);
- }
+ Result AddBlock(KPhysicalAddress addr, size_t num_pages);
+ void Open() const;
+ void OpenFirst() const;
+ void Close() const;
+
+ size_t GetNumPages() const;
+
+ bool IsEquivalentTo(const KPageGroup& rhs) const;
+
+ bool operator==(const KPageGroup& rhs) const {
+ return this->IsEquivalentTo(rhs);
+ }
- return this_node == nodes.end() && other_node == other.nodes.end();
+ bool operator!=(const KPageGroup& rhs) const {
+ return !(*this == rhs);
}
- Result AddBlock(u64 address, u64 num_pages) {
- if (!num_pages) {
- return ResultSuccess;
+private:
+ KernelCore& m_kernel;
+ KBlockInfo* m_first_block{};
+ KBlockInfo* m_last_block{};
+ KBlockInfoManager* m_manager{};
+};
+
+class KScopedPageGroup {
+public:
+ explicit KScopedPageGroup(const KPageGroup* gp) : m_pg(gp) {
+ if (m_pg) {
+ m_pg->Open();
}
- if (!nodes.empty()) {
- const auto node = nodes.back();
- if (node.GetAddress() + node.GetNumPages() * PageSize == address) {
- address = node.GetAddress();
- num_pages += node.GetNumPages();
- nodes.pop_back();
- }
+ }
+ explicit KScopedPageGroup(const KPageGroup& gp) : KScopedPageGroup(std::addressof(gp)) {}
+ ~KScopedPageGroup() {
+ if (m_pg) {
+ m_pg->Close();
}
- nodes.push_back({address, num_pages});
- return ResultSuccess;
}
- bool Empty() const {
- return nodes.empty();
+ void CancelClose() {
+ m_pg = nullptr;
}
- void Finalize() {}
-
private:
- std::list<Node> nodes;
+ const KPageGroup* m_pg{};
};
} // namespace Kernel
diff --git a/src/core/hle/kernel/k_page_table.cpp b/src/core/hle/kernel/k_page_table.cpp
index 612fc76fa..2e13d5d0d 100644
--- a/src/core/hle/kernel/k_page_table.cpp
+++ b/src/core/hle/kernel/k_page_table.cpp
@@ -100,7 +100,7 @@ constexpr size_t GetAddressSpaceWidthFromType(FileSys::ProgramAddressSpaceType a
KPageTable::KPageTable(Core::System& system_)
: m_general_lock{system_.Kernel()},
- m_map_physical_memory_lock{system_.Kernel()}, m_system{system_} {}
+ m_map_physical_memory_lock{system_.Kernel()}, m_system{system_}, m_kernel{system_.Kernel()} {}
KPageTable::~KPageTable() = default;
@@ -373,7 +373,7 @@ Result KPageTable::MapProcessCode(VAddr addr, size_t num_pages, KMemoryState sta
m_memory_block_slab_manager);
// Allocate and open.
- KPageGroup pg;
+ KPageGroup pg{m_kernel, m_block_info_manager};
R_TRY(m_system.Kernel().MemoryManager().AllocateAndOpen(
&pg, num_pages,
KMemoryManager::EncodeOption(KMemoryManager::Pool::Application, m_allocation_option)));
@@ -432,9 +432,12 @@ Result KPageTable::MapCodeMemory(VAddr dst_address, VAddr src_address, size_t si
const size_t num_pages = size / PageSize;
// Create page groups for the memory being mapped.
- KPageGroup pg;
+ KPageGroup pg{m_kernel, m_block_info_manager};
AddRegionToPages(src_address, num_pages, pg);
+ // We're going to perform an update, so create a helper.
+ KScopedPageTableUpdater updater(this);
+
// Reprotect the source as kernel-read/not mapped.
const auto new_perm = static_cast<KMemoryPermission>(KMemoryPermission::KernelRead |
KMemoryPermission::NotMapped);
@@ -447,7 +450,10 @@ Result KPageTable::MapCodeMemory(VAddr dst_address, VAddr src_address, size_t si
});
// Map the alias pages.
- R_TRY(MapPages(dst_address, pg, new_perm));
+ const KPageProperties dst_properties = {new_perm, false, false,
+ DisableMergeAttribute::DisableHead};
+ R_TRY(
+ this->MapPageGroupImpl(updater.GetPageList(), dst_address, pg, dst_properties, false));
// We successfully mapped the alias pages, so we don't need to unprotect the src pages on
// failure.
@@ -593,7 +599,7 @@ Result KPageTable::MakePageGroup(KPageGroup& pg, VAddr addr, size_t num_pages) {
const size_t size = num_pages * PageSize;
// We're making a new group, not adding to an existing one.
- R_UNLESS(pg.Empty(), ResultInvalidCurrentMemory);
+ R_UNLESS(pg.empty(), ResultInvalidCurrentMemory);
// Begin traversal.
Common::PageTable::TraversalContext context;
@@ -640,11 +646,10 @@ Result KPageTable::MakePageGroup(KPageGroup& pg, VAddr addr, size_t num_pages) {
R_SUCCEED();
}
-bool KPageTable::IsValidPageGroup(const KPageGroup& pg_ll, VAddr addr, size_t num_pages) {
+bool KPageTable::IsValidPageGroup(const KPageGroup& pg, VAddr addr, size_t num_pages) {
ASSERT(this->IsLockedByCurrentThread());
const size_t size = num_pages * PageSize;
- const auto& pg = pg_ll.Nodes();
const auto& memory_layout = m_system.Kernel().MemoryLayout();
// Empty groups are necessarily invalid.
@@ -942,9 +947,6 @@ Result KPageTable::SetupForIpcServer(VAddr* out_addr, size_t size, VAddr src_add
ON_RESULT_FAILURE {
if (cur_mapped_addr != dst_addr) {
- // HACK: Manually close the pages.
- HACK_ClosePages(dst_addr, (cur_mapped_addr - dst_addr) / PageSize);
-
ASSERT(Operate(dst_addr, (cur_mapped_addr - dst_addr) / PageSize,
KMemoryPermission::None, OperationType::Unmap)
.IsSuccess());
@@ -1020,9 +1022,6 @@ Result KPageTable::SetupForIpcServer(VAddr* out_addr, size_t size, VAddr src_add
// Map the page.
R_TRY(Operate(cur_mapped_addr, 1, test_perm, OperationType::Map, start_partial_page));
- // HACK: Manually open the pages.
- HACK_OpenPages(start_partial_page, 1);
-
// Update tracking extents.
cur_mapped_addr += PageSize;
cur_block_addr += PageSize;
@@ -1051,9 +1050,6 @@ Result KPageTable::SetupForIpcServer(VAddr* out_addr, size_t size, VAddr src_add
R_TRY(Operate(cur_mapped_addr, cur_block_size / PageSize, test_perm, OperationType::Map,
cur_block_addr));
- // HACK: Manually open the pages.
- HACK_OpenPages(cur_block_addr, cur_block_size / PageSize);
-
// Update tracking extents.
cur_mapped_addr += cur_block_size;
cur_block_addr = next_entry.phys_addr;
@@ -1073,9 +1069,6 @@ Result KPageTable::SetupForIpcServer(VAddr* out_addr, size_t size, VAddr src_add
R_TRY(Operate(cur_mapped_addr, last_block_size / PageSize, test_perm, OperationType::Map,
cur_block_addr));
- // HACK: Manually open the pages.
- HACK_OpenPages(cur_block_addr, last_block_size / PageSize);
-
// Update tracking extents.
cur_mapped_addr += last_block_size;
cur_block_addr += last_block_size;
@@ -1107,9 +1100,6 @@ Result KPageTable::SetupForIpcServer(VAddr* out_addr, size_t size, VAddr src_add
// Map the page.
R_TRY(Operate(cur_mapped_addr, 1, test_perm, OperationType::Map, end_partial_page));
-
- // HACK: Manually open the pages.
- HACK_OpenPages(end_partial_page, 1);
}
// Update memory blocks to reflect our changes
@@ -1211,9 +1201,6 @@ Result KPageTable::CleanupForIpcServer(VAddr address, size_t size, KMemoryState
const size_t aligned_size = aligned_end - aligned_start;
const size_t aligned_num_pages = aligned_size / PageSize;
- // HACK: Manually close the pages.
- HACK_ClosePages(aligned_start, aligned_num_pages);
-
// Unmap the pages.
R_TRY(Operate(aligned_start, aligned_num_pages, KMemoryPermission::None, OperationType::Unmap));
@@ -1501,17 +1488,6 @@ void KPageTable::CleanupForIpcClientOnServerSetupFailure([[maybe_unused]] PageLi
}
}
-void KPageTable::HACK_OpenPages(PAddr phys_addr, size_t num_pages) {
- m_system.Kernel().MemoryManager().OpenFirst(phys_addr, num_pages);
-}
-
-void KPageTable::HACK_ClosePages(VAddr virt_addr, size_t num_pages) {
- for (size_t index = 0; index < num_pages; ++index) {
- const auto paddr = GetPhysicalAddr(virt_addr + (index * PageSize));
- m_system.Kernel().MemoryManager().Close(paddr, 1);
- }
-}
-
Result KPageTable::MapPhysicalMemory(VAddr address, size_t size) {
// Lock the physical memory lock.
KScopedLightLock phys_lk(m_map_physical_memory_lock);
@@ -1572,7 +1548,7 @@ Result KPageTable::MapPhysicalMemory(VAddr address, size_t size) {
R_UNLESS(memory_reservation.Succeeded(), ResultLimitReached);
// Allocate pages for the new memory.
- KPageGroup pg;
+ KPageGroup pg{m_kernel, m_block_info_manager};
R_TRY(m_system.Kernel().MemoryManager().AllocateForProcess(
&pg, (size - mapped_size) / PageSize, m_allocate_option, 0, 0));
@@ -1650,7 +1626,7 @@ Result KPageTable::MapPhysicalMemory(VAddr address, size_t size) {
KScopedPageTableUpdater updater(this);
// Prepare to iterate over the memory.
- auto pg_it = pg.Nodes().begin();
+ auto pg_it = pg.begin();
PAddr pg_phys_addr = pg_it->GetAddress();
size_t pg_pages = pg_it->GetNumPages();
@@ -1680,9 +1656,6 @@ Result KPageTable::MapPhysicalMemory(VAddr address, size_t size) {
last_unmap_address + 1 - cur_address) /
PageSize;
- // HACK: Manually close the pages.
- HACK_ClosePages(cur_address, cur_pages);
-
// Unmap.
ASSERT(Operate(cur_address, cur_pages, KMemoryPermission::None,
OperationType::Unmap)
@@ -1703,7 +1676,7 @@ Result KPageTable::MapPhysicalMemory(VAddr address, size_t size) {
// Release any remaining unmapped memory.
m_system.Kernel().MemoryManager().OpenFirst(pg_phys_addr, pg_pages);
m_system.Kernel().MemoryManager().Close(pg_phys_addr, pg_pages);
- for (++pg_it; pg_it != pg.Nodes().end(); ++pg_it) {
+ for (++pg_it; pg_it != pg.end(); ++pg_it) {
m_system.Kernel().MemoryManager().OpenFirst(pg_it->GetAddress(),
pg_it->GetNumPages());
m_system.Kernel().MemoryManager().Close(pg_it->GetAddress(),
@@ -1731,7 +1704,7 @@ Result KPageTable::MapPhysicalMemory(VAddr address, size_t size) {
// Check if we're at the end of the physical block.
if (pg_pages == 0) {
// Ensure there are more pages to map.
- ASSERT(pg_it != pg.Nodes().end());
+ ASSERT(pg_it != pg.end());
// Advance our physical block.
++pg_it;
@@ -1742,10 +1715,7 @@ Result KPageTable::MapPhysicalMemory(VAddr address, size_t size) {
// Map whatever we can.
const size_t cur_pages = std::min(pg_pages, map_pages);
R_TRY(Operate(cur_address, cur_pages, KMemoryPermission::UserReadWrite,
- OperationType::Map, pg_phys_addr));
-
- // HACK: Manually open the pages.
- HACK_OpenPages(pg_phys_addr, cur_pages);
+ OperationType::MapFirst, pg_phys_addr));
// Advance.
cur_address += cur_pages * PageSize;
@@ -1888,9 +1858,6 @@ Result KPageTable::UnmapPhysicalMemory(VAddr address, size_t size) {
last_address + 1 - cur_address) /
PageSize;
- // HACK: Manually close the pages.
- HACK_ClosePages(cur_address, cur_pages);
-
// Unmap.
ASSERT(Operate(cur_address, cur_pages, KMemoryPermission::None, OperationType::Unmap)
.IsSuccess());
@@ -1920,7 +1887,8 @@ Result KPageTable::UnmapPhysicalMemory(VAddr address, size_t size) {
R_SUCCEED();
}
-Result KPageTable::MapMemory(VAddr dst_address, VAddr src_address, size_t size) {
+Result KPageTable::MapMemory(KProcessAddress dst_address, KProcessAddress src_address,
+ size_t size) {
// Lock the table.
KScopedLightLock lk(m_general_lock);
@@ -1941,53 +1909,73 @@ Result KPageTable::MapMemory(VAddr dst_address, VAddr src_address, size_t size)
KMemoryAttribute::None));
// Create an update allocator for the source.
- Result src_allocator_result{ResultSuccess};
+ Result src_allocator_result;
KMemoryBlockManagerUpdateAllocator src_allocator(std::addressof(src_allocator_result),
m_memory_block_slab_manager,
num_src_allocator_blocks);
R_TRY(src_allocator_result);
// Create an update allocator for the destination.
- Result dst_allocator_result{ResultSuccess};
+ Result dst_allocator_result;
KMemoryBlockManagerUpdateAllocator dst_allocator(std::addressof(dst_allocator_result),
m_memory_block_slab_manager,
num_dst_allocator_blocks);
R_TRY(dst_allocator_result);
// Map the memory.
- KPageGroup page_linked_list;
- const size_t num_pages{size / PageSize};
- const KMemoryPermission new_src_perm = static_cast<KMemoryPermission>(
- KMemoryPermission::KernelRead | KMemoryPermission::NotMapped);
- const KMemoryAttribute new_src_attr = KMemoryAttribute::Locked;
-
- AddRegionToPages(src_address, num_pages, page_linked_list);
{
+ // Determine the number of pages being operated on.
+ const size_t num_pages = size / PageSize;
+
+ // Create page groups for the memory being unmapped.
+ KPageGroup pg{m_kernel, m_block_info_manager};
+
+ // Create the page group representing the source.
+ R_TRY(this->MakePageGroup(pg, src_address, num_pages));
+
+ // We're going to perform an update, so create a helper.
+ KScopedPageTableUpdater updater(this);
+
// Reprotect the source as kernel-read/not mapped.
- auto block_guard = detail::ScopeExit([&] {
- Operate(src_address, num_pages, KMemoryPermission::UserReadWrite,
- OperationType::ChangePermissions);
- });
- R_TRY(Operate(src_address, num_pages, new_src_perm, OperationType::ChangePermissions));
- R_TRY(MapPages(dst_address, page_linked_list, KMemoryPermission::UserReadWrite));
+ const KMemoryPermission new_src_perm = static_cast<KMemoryPermission>(
+ KMemoryPermission::KernelRead | KMemoryPermission::NotMapped);
+ const KMemoryAttribute new_src_attr = KMemoryAttribute::Locked;
+ const KPageProperties src_properties = {new_src_perm, false, false,
+ DisableMergeAttribute::DisableHeadBodyTail};
+ R_TRY(this->Operate(src_address, num_pages, src_properties.perm,
+ OperationType::ChangePermissions));
- block_guard.Cancel();
- }
+ // Ensure that we unprotect the source pages on failure.
+ ON_RESULT_FAILURE {
+ const KPageProperties unprotect_properties = {
+ KMemoryPermission::UserReadWrite, false, false,
+ DisableMergeAttribute::EnableHeadBodyTail};
+ ASSERT(this->Operate(src_address, num_pages, unprotect_properties.perm,
+ OperationType::ChangePermissions) == ResultSuccess);
+ };
- // Apply the memory block updates.
- m_memory_block_manager.Update(std::addressof(src_allocator), src_address, num_pages, src_state,
- new_src_perm, new_src_attr,
- KMemoryBlockDisableMergeAttribute::Locked,
- KMemoryBlockDisableMergeAttribute::None);
- m_memory_block_manager.Update(std::addressof(dst_allocator), dst_address, num_pages,
- KMemoryState::Stack, KMemoryPermission::UserReadWrite,
- KMemoryAttribute::None, KMemoryBlockDisableMergeAttribute::Normal,
- KMemoryBlockDisableMergeAttribute::None);
+ // Map the alias pages.
+ const KPageProperties dst_map_properties = {KMemoryPermission::UserReadWrite, false, false,
+ DisableMergeAttribute::DisableHead};
+ R_TRY(this->MapPageGroupImpl(updater.GetPageList(), dst_address, pg, dst_map_properties,
+ false));
+
+ // Apply the memory block updates.
+ m_memory_block_manager.Update(std::addressof(src_allocator), src_address, num_pages,
+ src_state, new_src_perm, new_src_attr,
+ KMemoryBlockDisableMergeAttribute::Locked,
+ KMemoryBlockDisableMergeAttribute::None);
+ m_memory_block_manager.Update(
+ std::addressof(dst_allocator), dst_address, num_pages, KMemoryState::Stack,
+ KMemoryPermission::UserReadWrite, KMemoryAttribute::None,
+ KMemoryBlockDisableMergeAttribute::Normal, KMemoryBlockDisableMergeAttribute::None);
+ }
R_SUCCEED();
}
-Result KPageTable::UnmapMemory(VAddr dst_address, VAddr src_address, size_t size) {
+Result KPageTable::UnmapMemory(KProcessAddress dst_address, KProcessAddress src_address,
+ size_t size) {
// Lock the table.
KScopedLightLock lk(m_general_lock);
@@ -2009,108 +1997,208 @@ Result KPageTable::UnmapMemory(VAddr dst_address, VAddr src_address, size_t size
KMemoryPermission::None, KMemoryAttribute::All, KMemoryAttribute::None));
// Create an update allocator for the source.
- Result src_allocator_result{ResultSuccess};
+ Result src_allocator_result;
KMemoryBlockManagerUpdateAllocator src_allocator(std::addressof(src_allocator_result),
m_memory_block_slab_manager,
num_src_allocator_blocks);
R_TRY(src_allocator_result);
// Create an update allocator for the destination.
- Result dst_allocator_result{ResultSuccess};
+ Result dst_allocator_result;
KMemoryBlockManagerUpdateAllocator dst_allocator(std::addressof(dst_allocator_result),
m_memory_block_slab_manager,
num_dst_allocator_blocks);
R_TRY(dst_allocator_result);
- KPageGroup src_pages;
- KPageGroup dst_pages;
- const size_t num_pages{size / PageSize};
+ // Unmap the memory.
+ {
+ // Determine the number of pages being operated on.
+ const size_t num_pages = size / PageSize;
- AddRegionToPages(src_address, num_pages, src_pages);
- AddRegionToPages(dst_address, num_pages, dst_pages);
+ // Create page groups for the memory being unmapped.
+ KPageGroup pg{m_kernel, m_block_info_manager};
- R_UNLESS(dst_pages.IsEqual(src_pages), ResultInvalidMemoryRegion);
+ // Create the page group representing the destination.
+ R_TRY(this->MakePageGroup(pg, dst_address, num_pages));
- {
- auto block_guard = detail::ScopeExit([&] { MapPages(dst_address, dst_pages, dst_perm); });
+ // Ensure the page group is the valid for the source.
+ R_UNLESS(this->IsValidPageGroup(pg, src_address, num_pages), ResultInvalidMemoryRegion);
- R_TRY(Operate(dst_address, num_pages, KMemoryPermission::None, OperationType::Unmap));
- R_TRY(Operate(src_address, num_pages, KMemoryPermission::UserReadWrite,
- OperationType::ChangePermissions));
+ // We're going to perform an update, so create a helper.
+ KScopedPageTableUpdater updater(this);
- block_guard.Cancel();
- }
+ // Unmap the aliased copy of the pages.
+ const KPageProperties dst_unmap_properties = {KMemoryPermission::None, false, false,
+ DisableMergeAttribute::None};
+ R_TRY(
+ this->Operate(dst_address, num_pages, dst_unmap_properties.perm, OperationType::Unmap));
+
+ // Ensure that we re-map the aliased pages on failure.
+ ON_RESULT_FAILURE {
+ this->RemapPageGroup(updater.GetPageList(), dst_address, size, pg);
+ };
- // Apply the memory block updates.
- m_memory_block_manager.Update(std::addressof(src_allocator), src_address, num_pages, src_state,
- KMemoryPermission::UserReadWrite, KMemoryAttribute::None,
- KMemoryBlockDisableMergeAttribute::None,
- KMemoryBlockDisableMergeAttribute::Locked);
- m_memory_block_manager.Update(std::addressof(dst_allocator), dst_address, num_pages,
- KMemoryState::None, KMemoryPermission::None,
- KMemoryAttribute::None, KMemoryBlockDisableMergeAttribute::None,
- KMemoryBlockDisableMergeAttribute::Normal);
+ // Try to set the permissions for the source pages back to what they should be.
+ const KPageProperties src_properties = {KMemoryPermission::UserReadWrite, false, false,
+ DisableMergeAttribute::EnableAndMergeHeadBodyTail};
+ R_TRY(this->Operate(src_address, num_pages, src_properties.perm,
+ OperationType::ChangePermissions));
+
+ // Apply the memory block updates.
+ m_memory_block_manager.Update(
+ std::addressof(src_allocator), src_address, num_pages, src_state,
+ KMemoryPermission::UserReadWrite, KMemoryAttribute::None,
+ KMemoryBlockDisableMergeAttribute::None, KMemoryBlockDisableMergeAttribute::Locked);
+ m_memory_block_manager.Update(
+ std::addressof(dst_allocator), dst_address, num_pages, KMemoryState::None,
+ KMemoryPermission::None, KMemoryAttribute::None,
+ KMemoryBlockDisableMergeAttribute::None, KMemoryBlockDisableMergeAttribute::Normal);
+ }
R_SUCCEED();
}
-Result KPageTable::MapPages(VAddr addr, const KPageGroup& page_linked_list,
- KMemoryPermission perm) {
+Result KPageTable::AllocateAndMapPagesImpl(PageLinkedList* page_list, KProcessAddress address,
+ size_t num_pages, KMemoryPermission perm) {
ASSERT(this->IsLockedByCurrentThread());
- VAddr cur_addr{addr};
+ // Create a page group to hold the pages we allocate.
+ KPageGroup pg{m_kernel, m_block_info_manager};
- for (const auto& node : page_linked_list.Nodes()) {
- if (const auto result{
- Operate(cur_addr, node.GetNumPages(), perm, OperationType::Map, node.GetAddress())};
- result.IsError()) {
- const size_t num_pages{(addr - cur_addr) / PageSize};
+ // Allocate the pages.
+ R_TRY(
+ m_kernel.MemoryManager().AllocateAndOpen(std::addressof(pg), num_pages, m_allocate_option));
- ASSERT(Operate(addr, num_pages, KMemoryPermission::None, OperationType::Unmap)
- .IsSuccess());
+ // Ensure that the page group is closed when we're done working with it.
+ SCOPE_EXIT({ pg.Close(); });
+
+ // Clear all pages.
+ for (const auto& it : pg) {
+ std::memset(m_system.DeviceMemory().GetPointer<void>(it.GetAddress()), m_heap_fill_value,
+ it.GetSize());
+ }
+
+ // Map the pages.
+ R_RETURN(this->Operate(address, num_pages, pg, OperationType::MapGroup));
+}
+
+Result KPageTable::MapPageGroupImpl(PageLinkedList* page_list, KProcessAddress address,
+ const KPageGroup& pg, const KPageProperties properties,
+ bool reuse_ll) {
+ ASSERT(this->IsLockedByCurrentThread());
+
+ // Note the current address, so that we can iterate.
+ const KProcessAddress start_address = address;
+ KProcessAddress cur_address = address;
- R_RETURN(result);
+ // Ensure that we clean up on failure.
+ ON_RESULT_FAILURE {
+ ASSERT(!reuse_ll);
+ if (cur_address != start_address) {
+ const KPageProperties unmap_properties = {KMemoryPermission::None, false, false,
+ DisableMergeAttribute::None};
+ ASSERT(this->Operate(start_address, (cur_address - start_address) / PageSize,
+ unmap_properties.perm, OperationType::Unmap) == ResultSuccess);
}
+ };
- cur_addr += node.GetNumPages() * PageSize;
+ // Iterate, mapping all pages in the group.
+ for (const auto& block : pg) {
+ // Map and advance.
+ const KPageProperties cur_properties =
+ (cur_address == start_address)
+ ? properties
+ : KPageProperties{properties.perm, properties.io, properties.uncached,
+ DisableMergeAttribute::None};
+ this->Operate(cur_address, block.GetNumPages(), cur_properties.perm, OperationType::Map,
+ block.GetAddress());
+ cur_address += block.GetSize();
}
+ // We succeeded!
R_SUCCEED();
}
-Result KPageTable::MapPages(VAddr address, KPageGroup& page_linked_list, KMemoryState state,
- KMemoryPermission perm) {
- // Check that the map is in range.
- const size_t num_pages{page_linked_list.GetNumPages()};
- const size_t size{num_pages * PageSize};
- R_UNLESS(this->CanContain(address, size, state), ResultInvalidCurrentMemory);
+void KPageTable::RemapPageGroup(PageLinkedList* page_list, KProcessAddress address, size_t size,
+ const KPageGroup& pg) {
+ ASSERT(this->IsLockedByCurrentThread());
- // Lock the table.
- KScopedLightLock lk(m_general_lock);
+ // Note the current address, so that we can iterate.
+ const KProcessAddress start_address = address;
+ const KProcessAddress last_address = start_address + size - 1;
+ const KProcessAddress end_address = last_address + 1;
- // Check the memory state.
- R_TRY(this->CheckMemoryState(address, size, KMemoryState::All, KMemoryState::Free,
- KMemoryPermission::None, KMemoryPermission::None,
- KMemoryAttribute::None, KMemoryAttribute::None));
+ // Iterate over the memory.
+ auto pg_it = pg.begin();
+ ASSERT(pg_it != pg.end());
- // Create an update allocator.
- Result allocator_result{ResultSuccess};
- KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result),
- m_memory_block_slab_manager);
+ KPhysicalAddress pg_phys_addr = pg_it->GetAddress();
+ size_t pg_pages = pg_it->GetNumPages();
- // Map the pages.
- R_TRY(MapPages(address, page_linked_list, perm));
+ auto it = m_memory_block_manager.FindIterator(start_address);
+ while (true) {
+ // Check that the iterator is valid.
+ ASSERT(it != m_memory_block_manager.end());
- // Update the blocks.
- m_memory_block_manager.Update(std::addressof(allocator), address, num_pages, state, perm,
- KMemoryAttribute::None, KMemoryBlockDisableMergeAttribute::Normal,
- KMemoryBlockDisableMergeAttribute::None);
+ // Get the memory info.
+ const KMemoryInfo info = it->GetMemoryInfo();
- R_SUCCEED();
+ // Determine the range to map.
+ KProcessAddress map_address = std::max<VAddr>(info.GetAddress(), start_address);
+ const KProcessAddress map_end_address = std::min<VAddr>(info.GetEndAddress(), end_address);
+ ASSERT(map_end_address != map_address);
+
+ // Determine if we should disable head merge.
+ const bool disable_head_merge =
+ info.GetAddress() >= start_address &&
+ True(info.GetDisableMergeAttribute() & KMemoryBlockDisableMergeAttribute::Normal);
+ const KPageProperties map_properties = {
+ info.GetPermission(), false, false,
+ disable_head_merge ? DisableMergeAttribute::DisableHead : DisableMergeAttribute::None};
+
+ // While we have pages to map, map them.
+ size_t map_pages = (map_end_address - map_address) / PageSize;
+ while (map_pages > 0) {
+ // Check if we're at the end of the physical block.
+ if (pg_pages == 0) {
+ // Ensure there are more pages to map.
+ ASSERT(pg_it != pg.end());
+
+ // Advance our physical block.
+ ++pg_it;
+ pg_phys_addr = pg_it->GetAddress();
+ pg_pages = pg_it->GetNumPages();
+ }
+
+ // Map whatever we can.
+ const size_t cur_pages = std::min(pg_pages, map_pages);
+ ASSERT(this->Operate(map_address, map_pages, map_properties.perm, OperationType::Map,
+ pg_phys_addr) == ResultSuccess);
+
+ // Advance.
+ map_address += cur_pages * PageSize;
+ map_pages -= cur_pages;
+
+ pg_phys_addr += cur_pages * PageSize;
+ pg_pages -= cur_pages;
+ }
+
+ // Check if we're done.
+ if (last_address <= info.GetLastAddress()) {
+ break;
+ }
+
+ // Advance.
+ ++it;
+ }
+
+ // Check that we re-mapped precisely the page group.
+ ASSERT((++pg_it) == pg.end());
}
-Result KPageTable::MapPages(VAddr* out_addr, size_t num_pages, size_t alignment, PAddr phys_addr,
- bool is_pa_valid, VAddr region_start, size_t region_num_pages,
+Result KPageTable::MapPages(KProcessAddress* out_addr, size_t num_pages, size_t alignment,
+ KPhysicalAddress phys_addr, bool is_pa_valid,
+ KProcessAddress region_start, size_t region_num_pages,
KMemoryState state, KMemoryPermission perm) {
ASSERT(Common::IsAligned(alignment, PageSize) && alignment >= PageSize);
@@ -2123,26 +2211,30 @@ Result KPageTable::MapPages(VAddr* out_addr, size_t num_pages, size_t alignment,
KScopedLightLock lk(m_general_lock);
// Find a random address to map at.
- VAddr addr = this->FindFreeArea(region_start, region_num_pages, num_pages, alignment, 0,
- this->GetNumGuardPages());
+ KProcessAddress addr = this->FindFreeArea(region_start, region_num_pages, num_pages, alignment,
+ 0, this->GetNumGuardPages());
R_UNLESS(addr != 0, ResultOutOfMemory);
ASSERT(Common::IsAligned(addr, alignment));
ASSERT(this->CanContain(addr, num_pages * PageSize, state));
ASSERT(this->CheckMemoryState(addr, num_pages * PageSize, KMemoryState::All, KMemoryState::Free,
KMemoryPermission::None, KMemoryPermission::None,
- KMemoryAttribute::None, KMemoryAttribute::None)
- .IsSuccess());
+ KMemoryAttribute::None, KMemoryAttribute::None) == ResultSuccess);
// Create an update allocator.
- Result allocator_result{ResultSuccess};
+ Result allocator_result;
KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result),
m_memory_block_slab_manager);
+ R_TRY(allocator_result);
+
+ // We're going to perform an update, so create a helper.
+ KScopedPageTableUpdater updater(this);
// Perform mapping operation.
if (is_pa_valid) {
- R_TRY(this->Operate(addr, num_pages, perm, OperationType::Map, phys_addr));
+ const KPageProperties properties = {perm, false, false, DisableMergeAttribute::DisableHead};
+ R_TRY(this->Operate(addr, num_pages, properties.perm, OperationType::Map, phys_addr));
} else {
- UNIMPLEMENTED();
+ R_TRY(this->AllocateAndMapPagesImpl(updater.GetPageList(), addr, num_pages, perm));
}
// Update the blocks.
@@ -2155,28 +2247,45 @@ Result KPageTable::MapPages(VAddr* out_addr, size_t num_pages, size_t alignment,
R_SUCCEED();
}
-Result KPageTable::UnmapPages(VAddr addr, const KPageGroup& page_linked_list) {
- ASSERT(this->IsLockedByCurrentThread());
+Result KPageTable::MapPages(KProcessAddress address, size_t num_pages, KMemoryState state,
+ KMemoryPermission perm) {
+ // Check that the map is in range.
+ const size_t size = num_pages * PageSize;
+ R_UNLESS(this->CanContain(address, size, state), ResultInvalidCurrentMemory);
- VAddr cur_addr{addr};
+ // Lock the table.
+ KScopedLightLock lk(m_general_lock);
- for (const auto& node : page_linked_list.Nodes()) {
- if (const auto result{Operate(cur_addr, node.GetNumPages(), KMemoryPermission::None,
- OperationType::Unmap)};
- result.IsError()) {
- R_RETURN(result);
- }
+ // Check the memory state.
+ size_t num_allocator_blocks;
+ R_TRY(this->CheckMemoryState(std::addressof(num_allocator_blocks), address, size,
+ KMemoryState::All, KMemoryState::Free, KMemoryPermission::None,
+ KMemoryPermission::None, KMemoryAttribute::None,
+ KMemoryAttribute::None));
- cur_addr += node.GetNumPages() * PageSize;
- }
+ // Create an update allocator.
+ Result allocator_result;
+ KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result),
+ m_memory_block_slab_manager, num_allocator_blocks);
+ R_TRY(allocator_result);
+
+ // We're going to perform an update, so create a helper.
+ KScopedPageTableUpdater updater(this);
+
+ // Map the pages.
+ R_TRY(this->AllocateAndMapPagesImpl(updater.GetPageList(), address, num_pages, perm));
+
+ // Update the blocks.
+ m_memory_block_manager.Update(std::addressof(allocator), address, num_pages, state, perm,
+ KMemoryAttribute::None, KMemoryBlockDisableMergeAttribute::Normal,
+ KMemoryBlockDisableMergeAttribute::None);
R_SUCCEED();
}
-Result KPageTable::UnmapPages(VAddr address, KPageGroup& page_linked_list, KMemoryState state) {
+Result KPageTable::UnmapPages(KProcessAddress address, size_t num_pages, KMemoryState state) {
// Check that the unmap is in range.
- const size_t num_pages{page_linked_list.GetNumPages()};
- const size_t size{num_pages * PageSize};
+ const size_t size = num_pages * PageSize;
R_UNLESS(this->Contains(address, size), ResultInvalidCurrentMemory);
// Lock the table.
@@ -2190,13 +2299,18 @@ Result KPageTable::UnmapPages(VAddr address, KPageGroup& page_linked_list, KMemo
KMemoryAttribute::None));
// Create an update allocator.
- Result allocator_result{ResultSuccess};
+ Result allocator_result;
KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result),
m_memory_block_slab_manager, num_allocator_blocks);
R_TRY(allocator_result);
+ // We're going to perform an update, so create a helper.
+ KScopedPageTableUpdater updater(this);
+
// Perform the unmap.
- R_TRY(UnmapPages(address, page_linked_list));
+ const KPageProperties unmap_properties = {KMemoryPermission::None, false, false,
+ DisableMergeAttribute::None};
+ R_TRY(this->Operate(address, num_pages, unmap_properties.perm, OperationType::Unmap));
// Update the blocks.
m_memory_block_manager.Update(std::addressof(allocator), address, num_pages, KMemoryState::Free,
@@ -2207,29 +2321,130 @@ Result KPageTable::UnmapPages(VAddr address, KPageGroup& page_linked_list, KMemo
R_SUCCEED();
}
-Result KPageTable::UnmapPages(VAddr address, size_t num_pages, KMemoryState state) {
- // Check that the unmap is in range.
+Result KPageTable::MapPageGroup(KProcessAddress* out_addr, const KPageGroup& pg,
+ KProcessAddress region_start, size_t region_num_pages,
+ KMemoryState state, KMemoryPermission perm) {
+ ASSERT(!this->IsLockedByCurrentThread());
+
+ // Ensure this is a valid map request.
+ const size_t num_pages = pg.GetNumPages();
+ R_UNLESS(this->CanContain(region_start, region_num_pages * PageSize, state),
+ ResultInvalidCurrentMemory);
+ R_UNLESS(num_pages < region_num_pages, ResultOutOfMemory);
+
+ // Lock the table.
+ KScopedLightLock lk(m_general_lock);
+
+ // Find a random address to map at.
+ KProcessAddress addr = this->FindFreeArea(region_start, region_num_pages, num_pages, PageSize,
+ 0, this->GetNumGuardPages());
+ R_UNLESS(addr != 0, ResultOutOfMemory);
+ ASSERT(this->CanContain(addr, num_pages * PageSize, state));
+ ASSERT(this->CheckMemoryState(addr, num_pages * PageSize, KMemoryState::All, KMemoryState::Free,
+ KMemoryPermission::None, KMemoryPermission::None,
+ KMemoryAttribute::None, KMemoryAttribute::None) == ResultSuccess);
+
+ // Create an update allocator.
+ Result allocator_result;
+ KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result),
+ m_memory_block_slab_manager);
+ R_TRY(allocator_result);
+
+ // We're going to perform an update, so create a helper.
+ KScopedPageTableUpdater updater(this);
+
+ // Perform mapping operation.
+ const KPageProperties properties = {perm, state == KMemoryState::Io, false,
+ DisableMergeAttribute::DisableHead};
+ R_TRY(this->MapPageGroupImpl(updater.GetPageList(), addr, pg, properties, false));
+
+ // Update the blocks.
+ m_memory_block_manager.Update(std::addressof(allocator), addr, num_pages, state, perm,
+ KMemoryAttribute::None, KMemoryBlockDisableMergeAttribute::Normal,
+ KMemoryBlockDisableMergeAttribute::None);
+
+ // We successfully mapped the pages.
+ *out_addr = addr;
+ R_SUCCEED();
+}
+
+Result KPageTable::MapPageGroup(KProcessAddress addr, const KPageGroup& pg, KMemoryState state,
+ KMemoryPermission perm) {
+ ASSERT(!this->IsLockedByCurrentThread());
+
+ // Ensure this is a valid map request.
+ const size_t num_pages = pg.GetNumPages();
const size_t size = num_pages * PageSize;
- R_UNLESS(this->Contains(address, size), ResultInvalidCurrentMemory);
+ R_UNLESS(this->CanContain(addr, size, state), ResultInvalidCurrentMemory);
// Lock the table.
KScopedLightLock lk(m_general_lock);
- // Check the memory state.
- size_t num_allocator_blocks{};
+ // Check if state allows us to map.
+ size_t num_allocator_blocks;
+ R_TRY(this->CheckMemoryState(std::addressof(num_allocator_blocks), addr, size,
+ KMemoryState::All, KMemoryState::Free, KMemoryPermission::None,
+ KMemoryPermission::None, KMemoryAttribute::None,
+ KMemoryAttribute::None));
+
+ // Create an update allocator.
+ Result allocator_result;
+ KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result),
+ m_memory_block_slab_manager, num_allocator_blocks);
+ R_TRY(allocator_result);
+
+ // We're going to perform an update, so create a helper.
+ KScopedPageTableUpdater updater(this);
+
+ // Perform mapping operation.
+ const KPageProperties properties = {perm, state == KMemoryState::Io, false,
+ DisableMergeAttribute::DisableHead};
+ R_TRY(this->MapPageGroupImpl(updater.GetPageList(), addr, pg, properties, false));
+
+ // Update the blocks.
+ m_memory_block_manager.Update(std::addressof(allocator), addr, num_pages, state, perm,
+ KMemoryAttribute::None, KMemoryBlockDisableMergeAttribute::Normal,
+ KMemoryBlockDisableMergeAttribute::None);
+
+ // We successfully mapped the pages.
+ R_SUCCEED();
+}
+
+Result KPageTable::UnmapPageGroup(KProcessAddress address, const KPageGroup& pg,
+ KMemoryState state) {
+ ASSERT(!this->IsLockedByCurrentThread());
+
+ // Ensure this is a valid unmap request.
+ const size_t num_pages = pg.GetNumPages();
+ const size_t size = num_pages * PageSize;
+ R_UNLESS(this->CanContain(address, size, state), ResultInvalidCurrentMemory);
+
+ // Lock the table.
+ KScopedLightLock lk(m_general_lock);
+
+ // Check if state allows us to unmap.
+ size_t num_allocator_blocks;
R_TRY(this->CheckMemoryState(std::addressof(num_allocator_blocks), address, size,
KMemoryState::All, state, KMemoryPermission::None,
KMemoryPermission::None, KMemoryAttribute::All,
KMemoryAttribute::None));
+ // Check that the page group is valid.
+ R_UNLESS(this->IsValidPageGroup(pg, address, num_pages), ResultInvalidCurrentMemory);
+
// Create an update allocator.
- Result allocator_result{ResultSuccess};
+ Result allocator_result;
KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result),
m_memory_block_slab_manager, num_allocator_blocks);
R_TRY(allocator_result);
- // Perform the unmap.
- R_TRY(Operate(address, num_pages, KMemoryPermission::None, OperationType::Unmap));
+ // We're going to perform an update, so create a helper.
+ KScopedPageTableUpdater updater(this);
+
+ // Perform unmapping operation.
+ const KPageProperties properties = {KMemoryPermission::None, false, false,
+ DisableMergeAttribute::None};
+ R_TRY(this->Operate(address, num_pages, properties.perm, OperationType::Unmap));
// Update the blocks.
m_memory_block_manager.Update(std::addressof(allocator), address, num_pages, KMemoryState::Free,
@@ -2527,13 +2742,13 @@ Result KPageTable::SetHeapSize(VAddr* out, size_t size) {
R_UNLESS(memory_reservation.Succeeded(), ResultLimitReached);
// Allocate pages for the heap extension.
- KPageGroup pg;
+ KPageGroup pg{m_kernel, m_block_info_manager};
R_TRY(m_system.Kernel().MemoryManager().AllocateAndOpen(
&pg, allocation_size / PageSize,
KMemoryManager::EncodeOption(m_memory_pool, m_allocation_option)));
// Clear all the newly allocated pages.
- for (const auto& it : pg.Nodes()) {
+ for (const auto& it : pg) {
std::memset(m_system.DeviceMemory().GetPointer<void>(it.GetAddress()), m_heap_fill_value,
it.GetSize());
}
@@ -2589,42 +2804,6 @@ Result KPageTable::SetHeapSize(VAddr* out, size_t size) {
}
}
-ResultVal<VAddr> KPageTable::AllocateAndMapMemory(size_t needed_num_pages, size_t align,
- bool is_map_only, VAddr region_start,
- size_t region_num_pages, KMemoryState state,
- KMemoryPermission perm, PAddr map_addr) {
- KScopedLightLock lk(m_general_lock);
-
- R_UNLESS(CanContain(region_start, region_num_pages * PageSize, state),
- ResultInvalidCurrentMemory);
- R_UNLESS(region_num_pages > needed_num_pages, ResultOutOfMemory);
- const VAddr addr{
- AllocateVirtualMemory(region_start, region_num_pages, needed_num_pages, align)};
- R_UNLESS(addr, ResultOutOfMemory);
-
- // Create an update allocator.
- Result allocator_result{ResultSuccess};
- KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result),
- m_memory_block_slab_manager);
-
- if (is_map_only) {
- R_TRY(Operate(addr, needed_num_pages, perm, OperationType::Map, map_addr));
- } else {
- KPageGroup page_group;
- R_TRY(m_system.Kernel().MemoryManager().AllocateForProcess(
- &page_group, needed_num_pages,
- KMemoryManager::EncodeOption(m_memory_pool, m_allocation_option), 0, 0));
- R_TRY(Operate(addr, needed_num_pages, page_group, OperationType::MapGroup));
- }
-
- // Update the blocks.
- m_memory_block_manager.Update(std::addressof(allocator), addr, needed_num_pages, state, perm,
- KMemoryAttribute::None, KMemoryBlockDisableMergeAttribute::Normal,
- KMemoryBlockDisableMergeAttribute::None);
-
- return addr;
-}
-
Result KPageTable::LockForMapDeviceAddressSpace(bool* out_is_io, VAddr address, size_t size,
KMemoryPermission perm, bool is_aligned,
bool check_heap) {
@@ -2795,19 +2974,28 @@ Result KPageTable::Operate(VAddr addr, size_t num_pages, const KPageGroup& page_
ASSERT(num_pages > 0);
ASSERT(num_pages == page_group.GetNumPages());
- for (const auto& node : page_group.Nodes()) {
- const size_t size{node.GetNumPages() * PageSize};
+ switch (operation) {
+ case OperationType::MapGroup: {
+ // We want to maintain a new reference to every page in the group.
+ KScopedPageGroup spg(page_group);
+
+ for (const auto& node : page_group) {
+ const size_t size{node.GetNumPages() * PageSize};
- switch (operation) {
- case OperationType::MapGroup:
+ // Map the pages.
m_system.Memory().MapMemoryRegion(*m_page_table_impl, addr, size, node.GetAddress());
- break;
- default:
- ASSERT(false);
- break;
+
+ addr += size;
}
- addr += size;
+ // We succeeded! We want to persist the reference to the pages.
+ spg.CancelClose();
+
+ break;
+ }
+ default:
+ ASSERT(false);
+ break;
}
R_SUCCEED();
@@ -2822,13 +3010,29 @@ Result KPageTable::Operate(VAddr addr, size_t num_pages, KMemoryPermission perm,
ASSERT(ContainsPages(addr, num_pages));
switch (operation) {
- case OperationType::Unmap:
+ case OperationType::Unmap: {
+ // Ensure that any pages we track close on exit.
+ KPageGroup pages_to_close{m_kernel, this->GetBlockInfoManager()};
+ SCOPE_EXIT({ pages_to_close.CloseAndReset(); });
+
+ this->AddRegionToPages(addr, num_pages, pages_to_close);
m_system.Memory().UnmapRegion(*m_page_table_impl, addr, num_pages * PageSize);
break;
+ }
+ case OperationType::MapFirst:
case OperationType::Map: {
ASSERT(map_addr);
ASSERT(Common::IsAligned(map_addr, PageSize));
m_system.Memory().MapMemoryRegion(*m_page_table_impl, addr, num_pages * PageSize, map_addr);
+
+ // Open references to pages, if we should.
+ if (IsHeapPhysicalAddress(m_kernel.MemoryLayout(), map_addr)) {
+ if (operation == OperationType::MapFirst) {
+ m_kernel.MemoryManager().OpenFirst(map_addr, num_pages);
+ } else {
+ m_kernel.MemoryManager().Open(map_addr, num_pages);
+ }
+ }
break;
}
case OperationType::Separate: {
diff --git a/src/core/hle/kernel/k_page_table.h b/src/core/hle/kernel/k_page_table.h
index f1ca785d7..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;
namespace Kernel {
+enum class DisableMergeAttribute : u8 {
+ None = (0U << 0),
+ DisableHead = (1U << 0),
+ DisableHeadAndBody = (1U << 1),
+ EnableHeadAndBody = (1U << 2),
+ DisableTail = (1U << 3),
+ EnableTail = (1U << 4),
+ EnableAndMergeHeadBodyTail = (1U << 5),
+ EnableHeadBodyTail = EnableHeadAndBody | EnableTail,
+ DisableHeadBodyTail = DisableHeadAndBody | DisableTail,
+};
+
+struct KPageProperties {
+ KMemoryPermission perm;
+ bool io;
+ bool uncached;
+ DisableMergeAttribute disable_merge_attributes;
+};
+static_assert(std::is_trivial_v<KPageProperties>);
+static_assert(sizeof(KPageProperties) == sizeof(u32));
+
class KBlockInfoManager;
class KMemoryBlockManager;
class KResourceLimit;
class KSystemResource;
class KPageTable final {
+protected:
+ struct PageLinkedList;
+
public:
enum class ICacheInvalidationStrategy : u32 { InvalidateRange, InvalidateAll };
@@ -57,27 +81,12 @@ public:
Result UnmapPhysicalMemory(VAddr addr, size_t size);
Result MapMemory(VAddr dst_addr, VAddr src_addr, size_t size);
Result UnmapMemory(VAddr dst_addr, VAddr src_addr, size_t size);
- Result MapPages(VAddr addr, KPageGroup& page_linked_list, KMemoryState state,
- KMemoryPermission perm);
- Result MapPages(VAddr* out_addr, size_t num_pages, size_t alignment, PAddr phys_addr,
- KMemoryState state, KMemoryPermission perm) {
- R_RETURN(this->MapPages(out_addr, num_pages, alignment, phys_addr, true,
- this->GetRegionAddress(state),
- this->GetRegionSize(state) / PageSize, state, perm));
- }
- Result UnmapPages(VAddr addr, KPageGroup& page_linked_list, KMemoryState state);
- Result UnmapPages(VAddr address, size_t num_pages, KMemoryState state);
Result SetProcessMemoryPermission(VAddr addr, size_t size, Svc::MemoryPermission svc_perm);
KMemoryInfo QueryInfo(VAddr addr);
Result SetMemoryPermission(VAddr addr, size_t size, Svc::MemoryPermission perm);
Result SetMemoryAttribute(VAddr addr, size_t size, u32 mask, u32 attr);
Result SetMaxHeapSize(size_t size);
Result SetHeapSize(VAddr* out, size_t size);
- ResultVal<VAddr> AllocateAndMapMemory(size_t needed_num_pages, size_t align, bool is_map_only,
- VAddr region_start, size_t region_num_pages,
- KMemoryState state, KMemoryPermission perm,
- PAddr map_addr = 0);
-
Result LockForMapDeviceAddressSpace(bool* out_is_io, VAddr address, size_t size,
KMemoryPermission perm, bool is_aligned, bool check_heap);
Result LockForUnmapDeviceAddressSpace(VAddr address, size_t size, bool check_heap);
@@ -107,8 +116,46 @@ public:
return *m_page_table_impl;
}
+ KBlockInfoManager* GetBlockInfoManager() {
+ return m_block_info_manager;
+ }
+
bool CanContain(VAddr addr, size_t size, KMemoryState state) const;
+ Result MapPages(KProcessAddress* out_addr, size_t num_pages, size_t alignment,
+ KPhysicalAddress phys_addr, KProcessAddress region_start,
+ size_t region_num_pages, KMemoryState state, KMemoryPermission perm) {
+ R_RETURN(this->MapPages(out_addr, num_pages, alignment, phys_addr, true, region_start,
+ region_num_pages, state, perm));
+ }
+
+ Result MapPages(KProcessAddress* out_addr, size_t num_pages, size_t alignment,
+ KPhysicalAddress phys_addr, KMemoryState state, KMemoryPermission perm) {
+ R_RETURN(this->MapPages(out_addr, num_pages, alignment, phys_addr, true,
+ this->GetRegionAddress(state),
+ this->GetRegionSize(state) / PageSize, state, perm));
+ }
+
+ Result MapPages(KProcessAddress* out_addr, size_t num_pages, KMemoryState state,
+ KMemoryPermission perm) {
+ R_RETURN(this->MapPages(out_addr, num_pages, PageSize, 0, false,
+ this->GetRegionAddress(state),
+ this->GetRegionSize(state) / PageSize, state, perm));
+ }
+
+ Result MapPages(KProcessAddress address, size_t num_pages, KMemoryState state,
+ KMemoryPermission perm);
+ Result UnmapPages(KProcessAddress address, size_t num_pages, KMemoryState state);
+
+ Result MapPageGroup(KProcessAddress* out_addr, const KPageGroup& pg,
+ KProcessAddress region_start, size_t region_num_pages, KMemoryState state,
+ KMemoryPermission perm);
+ Result MapPageGroup(KProcessAddress address, const KPageGroup& pg, KMemoryState state,
+ KMemoryPermission perm);
+ Result UnmapPageGroup(KProcessAddress address, const KPageGroup& pg, KMemoryState state);
+ void RemapPageGroup(PageLinkedList* page_list, KProcessAddress address, size_t size,
+ const KPageGroup& pg);
+
protected:
struct PageLinkedList {
private:
@@ -162,11 +209,9 @@ private:
static constexpr KMemoryAttribute DefaultMemoryIgnoreAttr =
KMemoryAttribute::IpcLocked | KMemoryAttribute::DeviceShared;
- Result MapPages(VAddr addr, const KPageGroup& page_linked_list, KMemoryPermission perm);
- Result MapPages(VAddr* out_addr, size_t num_pages, size_t alignment, PAddr phys_addr,
- bool is_pa_valid, VAddr region_start, size_t region_num_pages,
- KMemoryState state, KMemoryPermission perm);
- Result UnmapPages(VAddr addr, const KPageGroup& page_linked_list);
+ Result MapPages(KProcessAddress* out_addr, size_t num_pages, size_t alignment,
+ KPhysicalAddress phys_addr, bool is_pa_valid, KProcessAddress region_start,
+ size_t region_num_pages, KMemoryState state, KMemoryPermission perm);
bool IsRegionContiguous(VAddr addr, u64 size) const;
void AddRegionToPages(VAddr start, size_t num_pages, KPageGroup& page_linked_list);
KMemoryInfo QueryInfoImpl(VAddr addr);
@@ -261,9 +306,10 @@ private:
void CleanupForIpcClientOnServerSetupFailure(PageLinkedList* page_list, VAddr address,
size_t size, KMemoryPermission prot_perm);
- // HACK: These will be removed once we automatically manage page reference counts.
- void HACK_OpenPages(PAddr phys_addr, size_t num_pages);
- void HACK_ClosePages(VAddr virt_addr, size_t num_pages);
+ Result AllocateAndMapPagesImpl(PageLinkedList* page_list, KProcessAddress address,
+ size_t num_pages, KMemoryPermission perm);
+ Result MapPageGroupImpl(PageLinkedList* page_list, KProcessAddress address,
+ const KPageGroup& pg, const KPageProperties properties, bool reuse_ll);
mutable KLightLock m_general_lock;
mutable KLightLock m_map_physical_memory_lock;
@@ -488,6 +534,7 @@ private:
std::unique_ptr<Common::PageTable> m_page_table_impl;
Core::System& m_system;
+ KernelCore& m_kernel;
};
} // namespace Kernel
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 {
class KThread;
template <typename T>
-concept KPriorityQueueAffinityMask = !std::is_reference_v<T> && requires(T & t) {
- { t.GetAffinityMask() } -> Common::ConvertibleTo<u64>;
- {t.SetAffinityMask(0)};
+concept KPriorityQueueAffinityMask = !
+std::is_reference_v<T>&& requires(T& t) {
+ { t.GetAffinityMask() } -> Common::ConvertibleTo<u64>;
+ { t.SetAffinityMask(0) };
- { t.GetAffinity(0) } -> std::same_as<bool>;
- {t.SetAffinity(0, false)};
- {t.SetAll()};
-};
+ { t.GetAffinity(0) } -> std::same_as<bool>;
+ { t.SetAffinity(0, false) };
+ { t.SetAll() };
+ };
template <typename T>
-concept KPriorityQueueMember = !std::is_reference_v<T> && requires(T & t) {
- {typename T::QueueEntry()};
- {(typename T::QueueEntry()).Initialize()};
- {(typename T::QueueEntry()).SetPrev(std::addressof(t))};
- {(typename T::QueueEntry()).SetNext(std::addressof(t))};
- { (typename T::QueueEntry()).GetNext() } -> std::same_as<T*>;
- { (typename T::QueueEntry()).GetPrev() } -> std::same_as<T*>;
- { t.GetPriorityQueueEntry(0) } -> std::same_as<typename T::QueueEntry&>;
-
- {t.GetAffinityMask()};
- { std::remove_cvref_t<decltype(t.GetAffinityMask())>() } -> KPriorityQueueAffinityMask;
-
- { t.GetActiveCore() } -> Common::ConvertibleTo<s32>;
- { t.GetPriority() } -> Common::ConvertibleTo<s32>;
- { t.IsDummyThread() } -> Common::ConvertibleTo<bool>;
-};
+concept KPriorityQueueMember = !
+std::is_reference_v<T>&& requires(T& t) {
+ { typename T::QueueEntry() };
+ { (typename T::QueueEntry()).Initialize() };
+ { (typename T::QueueEntry()).SetPrev(std::addressof(t)) };
+ { (typename T::QueueEntry()).SetNext(std::addressof(t)) };
+ { (typename T::QueueEntry()).GetNext() } -> std::same_as<T*>;
+ { (typename T::QueueEntry()).GetPrev() } -> std::same_as<T*>;
+ {
+ t.GetPriorityQueueEntry(0)
+ } -> std::same_as<typename T::QueueEntry&>;
+
+ { t.GetAffinityMask() };
+ {
+ std::remove_cvref_t<decltype(t.GetAffinityMask())>()
+ } -> KPriorityQueueAffinityMask;
+
+ { t.GetActiveCore() } -> Common::ConvertibleTo<s32>;
+ { t.GetPriority() } -> Common::ConvertibleTo<s32>;
+ { t.IsDummyThread() } -> Common::ConvertibleTo<bool>;
+ };
template <typename Member, size_t NumCores_, int LowestPriority, int HighestPriority>
-requires KPriorityQueueMember<Member>
+ requires KPriorityQueueMember<Member>
class KPriorityQueue {
public:
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..0e4283a0c 100644
--- a/src/core/hle/kernel/k_process.cpp
+++ b/src/core/hle/kernel/k_process.cpp
@@ -370,7 +370,7 @@ Result KProcess::LoadFromMetadata(const FileSys::ProgramMetadata& metadata, std:
// Initialize proces address space
if (const Result result{page_table.InitializeForProcess(
metadata.GetAddressSpaceType(), false, false, false, KMemoryManager::Pool::Application,
- 0x8000000, code_size, &kernel.GetSystemSystemResource(), resource_limit)};
+ 0x8000000, code_size, &kernel.GetAppSystemResource(), resource_limit)};
result.IsError()) {
R_RETURN(result);
}
@@ -417,9 +417,8 @@ Result KProcess::LoadFromMetadata(const FileSys::ProgramMetadata& metadata, std:
}
void KProcess::Run(s32 main_thread_priority, u64 stack_size) {
- AllocateMainThreadStack(stack_size);
+ ASSERT(AllocateMainThreadStack(stack_size) == ResultSuccess);
resource_limit->Reserve(LimitableResource::ThreadCountMax, 1);
- resource_limit->Reserve(LimitableResource::PhysicalMemoryMax, main_thread_stack_size);
const std::size_t heap_capacity{memory_usage_capacity - (main_thread_stack_size + image_size)};
ASSERT(!page_table.SetMaxHeapSize(heap_capacity).IsError());
@@ -675,20 +674,31 @@ void KProcess::ChangeState(State new_state) {
}
Result KProcess::AllocateMainThreadStack(std::size_t stack_size) {
- ASSERT(stack_size);
-
- // The kernel always ensures that the given stack size is page aligned.
- main_thread_stack_size = Common::AlignUp(stack_size, PageSize);
-
- const VAddr start{page_table.GetStackRegionStart()};
- const std::size_t size{page_table.GetStackRegionEnd() - start};
-
- CASCADE_RESULT(main_thread_stack_top,
- page_table.AllocateAndMapMemory(
- main_thread_stack_size / PageSize, PageSize, false, start, size / PageSize,
- KMemoryState::Stack, KMemoryPermission::UserReadWrite));
+ // Ensure that we haven't already allocated stack.
+ ASSERT(main_thread_stack_size == 0);
+
+ // Ensure that we're allocating a valid stack.
+ stack_size = Common::AlignUp(stack_size, PageSize);
+ // R_UNLESS(stack_size + image_size <= m_max_process_memory, ResultOutOfMemory);
+ R_UNLESS(stack_size + image_size >= image_size, ResultOutOfMemory);
+
+ // Place a tentative reservation of memory for our new stack.
+ KScopedResourceReservation mem_reservation(this, Svc::LimitableResource::PhysicalMemoryMax,
+ stack_size);
+ R_UNLESS(mem_reservation.Succeeded(), ResultLimitReached);
+
+ // Allocate and map our stack.
+ if (stack_size) {
+ KProcessAddress stack_bottom;
+ R_TRY(page_table.MapPages(std::addressof(stack_bottom), stack_size / PageSize,
+ KMemoryState::Stack, KMemoryPermission::UserReadWrite));
+
+ main_thread_stack_top = stack_bottom + stack_size;
+ main_thread_stack_size = stack_size;
+ }
- main_thread_stack_top += main_thread_stack_size;
+ // We succeeded! Commit our memory reservation.
+ mem_reservation.Commit();
R_SUCCEED();
}
diff --git a/src/core/hle/kernel/k_scheduler.cpp b/src/core/hle/kernel/k_scheduler.cpp
index d6676904b..d6c214237 100644
--- a/src/core/hle/kernel/k_scheduler.cpp
+++ b/src/core/hle/kernel/k_scheduler.cpp
@@ -328,7 +328,7 @@ u64 KScheduler::UpdateHighestPriorityThreadsImpl(KernelCore& kernel) {
}
void KScheduler::SwitchThread(KThread* next_thread) {
- KProcess* const cur_process = kernel.CurrentProcess();
+ KProcess* const cur_process = GetCurrentProcessPointer(kernel);
KThread* const cur_thread = GetCurrentThreadPointer(kernel);
// We never want to schedule a null thread, so use the idle thread if we don't have a next.
@@ -689,11 +689,11 @@ void KScheduler::RotateScheduledQueue(KernelCore& kernel, s32 core_id, s32 prior
void KScheduler::YieldWithoutCoreMigration(KernelCore& kernel) {
// Validate preconditions.
ASSERT(CanSchedule(kernel));
- ASSERT(kernel.CurrentProcess() != nullptr);
+ ASSERT(GetCurrentProcessPointer(kernel) != nullptr);
// Get the current thread and process.
KThread& cur_thread = GetCurrentThread(kernel);
- KProcess& cur_process = *kernel.CurrentProcess();
+ KProcess& cur_process = GetCurrentProcess(kernel);
// If the thread's yield count matches, there's nothing for us to do.
if (cur_thread.GetYieldScheduleCount() == cur_process.GetScheduledCount()) {
@@ -728,11 +728,11 @@ void KScheduler::YieldWithoutCoreMigration(KernelCore& kernel) {
void KScheduler::YieldWithCoreMigration(KernelCore& kernel) {
// Validate preconditions.
ASSERT(CanSchedule(kernel));
- ASSERT(kernel.CurrentProcess() != nullptr);
+ ASSERT(GetCurrentProcessPointer(kernel) != nullptr);
// Get the current thread and process.
KThread& cur_thread = GetCurrentThread(kernel);
- KProcess& cur_process = *kernel.CurrentProcess();
+ KProcess& cur_process = GetCurrentProcess(kernel);
// If the thread's yield count matches, there's nothing for us to do.
if (cur_thread.GetYieldScheduleCount() == cur_process.GetScheduledCount()) {
@@ -816,11 +816,11 @@ void KScheduler::YieldWithCoreMigration(KernelCore& kernel) {
void KScheduler::YieldToAnyThread(KernelCore& kernel) {
// Validate preconditions.
ASSERT(CanSchedule(kernel));
- ASSERT(kernel.CurrentProcess() != nullptr);
+ ASSERT(GetCurrentProcessPointer(kernel) != nullptr);
// Get the current thread and process.
KThread& cur_thread = GetCurrentThread(kernel);
- KProcess& cur_process = *kernel.CurrentProcess();
+ KProcess& cur_process = GetCurrentProcess(kernel);
// If the thread's yield count matches, there's nothing for us to do.
if (cur_thread.GetYieldScheduleCount() == cur_process.GetScheduledCount()) {
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 @@
namespace Kernel {
template <typename T>
-concept KLockable = !std::is_reference_v<T> && requires(T & t) {
- { t.Lock() } -> std::same_as<void>;
- { t.Unlock() } -> std::same_as<void>;
-};
+concept KLockable = !
+std::is_reference_v<T>&& requires(T& t) {
+ { t.Lock() } -> std::same_as<void>;
+ { t.Unlock() } -> std::same_as<void>;
+ };
template <typename T>
-requires KLockable<T>
+ requires KLockable<T>
class [[nodiscard]] KScopedLock {
public:
explicit KScopedLock(T* l) : lock_ptr(l) {
diff --git a/src/core/hle/kernel/k_session.cpp b/src/core/hle/kernel/k_session.cpp
index b6f6fe9d9..7e677c028 100644
--- a/src/core/hle/kernel/k_session.cpp
+++ b/src/core/hle/kernel/k_session.cpp
@@ -33,7 +33,8 @@ void KSession::Initialize(KClientPort* port_, const std::string& name_) {
name = name_;
// Set our owner process.
- process = kernel.CurrentProcess();
+ //! FIXME: this is the wrong process!
+ process = kernel.ApplicationProcess();
process->Open();
// Set our port.
diff --git a/src/core/hle/kernel/k_shared_memory.cpp b/src/core/hle/kernel/k_shared_memory.cpp
index 0aa68103c..df505edfe 100644
--- a/src/core/hle/kernel/k_shared_memory.cpp
+++ b/src/core/hle/kernel/k_shared_memory.cpp
@@ -13,10 +13,7 @@
namespace Kernel {
KSharedMemory::KSharedMemory(KernelCore& kernel_) : KAutoObjectWithSlabHeapAndContainer{kernel_} {}
-
-KSharedMemory::~KSharedMemory() {
- kernel.GetSystemResourceLimit()->Release(LimitableResource::PhysicalMemoryMax, size);
-}
+KSharedMemory::~KSharedMemory() = default;
Result KSharedMemory::Initialize(Core::DeviceMemory& device_memory_, KProcess* owner_process_,
Svc::MemoryPermission owner_permission_,
@@ -49,7 +46,8 @@ Result KSharedMemory::Initialize(Core::DeviceMemory& device_memory_, KProcess* o
R_UNLESS(physical_address != 0, ResultOutOfMemory);
//! Insert the result into our page group.
- page_group.emplace(physical_address, num_pages);
+ page_group.emplace(kernel, &kernel.GetSystemSystemResource().GetBlockInfoManager());
+ page_group->AddBlock(physical_address, num_pages);
// Commit our reservation.
memory_reservation.Commit();
@@ -62,7 +60,7 @@ Result KSharedMemory::Initialize(Core::DeviceMemory& device_memory_, KProcess* o
is_initialized = true;
// Clear all pages in the memory.
- for (const auto& block : page_group->Nodes()) {
+ for (const auto& block : *page_group) {
std::memset(device_memory_.GetPointer<void>(block.GetAddress()), 0, block.GetSize());
}
@@ -71,13 +69,8 @@ Result KSharedMemory::Initialize(Core::DeviceMemory& device_memory_, KProcess* o
void KSharedMemory::Finalize() {
// Close and finalize the page group.
- // page_group->Close();
- // page_group->Finalize();
-
- //! HACK: Manually close.
- for (const auto& block : page_group->Nodes()) {
- kernel.MemoryManager().Close(block.GetAddress(), block.GetNumPages());
- }
+ page_group->Close();
+ page_group->Finalize();
// Release the memory reservation.
resource_limit->Release(LimitableResource::PhysicalMemoryMax, size);
@@ -101,15 +94,15 @@ Result KSharedMemory::Map(KProcess& target_process, VAddr address, std::size_t m
R_UNLESS(map_perm == test_perm, ResultInvalidNewMemoryPermission);
}
- return target_process.PageTable().MapPages(address, *page_group, KMemoryState::Shared,
- ConvertToKMemoryPermission(map_perm));
+ return target_process.PageTable().MapPageGroup(address, *page_group, KMemoryState::Shared,
+ ConvertToKMemoryPermission(map_perm));
}
Result KSharedMemory::Unmap(KProcess& target_process, VAddr address, std::size_t unmap_size) {
// Validate the size.
R_UNLESS(size == unmap_size, ResultInvalidSize);
- return target_process.PageTable().UnmapPages(address, *page_group, KMemoryState::Shared);
+ return target_process.PageTable().UnmapPageGroup(address, *page_group, KMemoryState::Shared);
}
} // namespace Kernel
diff --git a/src/core/hle/kernel/k_thread.cpp b/src/core/hle/kernel/k_thread.cpp
index 21207fe99..2d3da9d66 100644
--- a/src/core/hle/kernel/k_thread.cpp
+++ b/src/core/hle/kernel/k_thread.cpp
@@ -330,7 +330,7 @@ void KThread::Finalize() {
KThread* const waiter = std::addressof(*it);
// The thread shouldn't be a kernel waiter.
- ASSERT(!IsKernelAddressKey(waiter->GetAddressKey()));
+ ASSERT(!waiter->GetAddressKeyIsKernel());
// Clear the lock owner.
waiter->SetLockOwner(nullptr);
@@ -763,19 +763,6 @@ void KThread::Continue() {
KScheduler::OnThreadStateChanged(kernel, this, old_state);
}
-void KThread::WaitUntilSuspended() {
- // Make sure we have a suspend requested.
- ASSERT(IsSuspendRequested());
-
- // Loop until the thread is not executing on any core.
- for (std::size_t i = 0; i < static_cast<std::size_t>(Core::Hardware::NUM_CPU_CORES); ++i) {
- KThread* core_thread{};
- do {
- core_thread = kernel.Scheduler(i).GetSchedulerCurrentThread();
- } while (core_thread == this);
- }
-}
-
Result KThread::SetActivity(Svc::ThreadActivity activity) {
// Lock ourselves.
KScopedLightLock lk(activity_pause_lock);
@@ -897,7 +884,7 @@ void KThread::AddWaiterImpl(KThread* thread) {
}
// Keep track of how many kernel waiters we have.
- if (IsKernelAddressKey(thread->GetAddressKey())) {
+ if (thread->GetAddressKeyIsKernel()) {
ASSERT((num_kernel_waiters++) >= 0);
KScheduler::SetSchedulerUpdateNeeded(kernel);
}
@@ -911,7 +898,7 @@ void KThread::RemoveWaiterImpl(KThread* thread) {
ASSERT(kernel.GlobalSchedulerContext().IsLocked());
// Keep track of how many kernel waiters we have.
- if (IsKernelAddressKey(thread->GetAddressKey())) {
+ if (thread->GetAddressKeyIsKernel()) {
ASSERT((num_kernel_waiters--) > 0);
KScheduler::SetSchedulerUpdateNeeded(kernel);
}
@@ -987,7 +974,7 @@ KThread* KThread::RemoveWaiterByKey(s32* out_num_waiters, VAddr key) {
KThread* thread = std::addressof(*it);
// Keep track of how many kernel waiters we have.
- if (IsKernelAddressKey(thread->GetAddressKey())) {
+ if (thread->GetAddressKeyIsKernel()) {
ASSERT((num_kernel_waiters--) > 0);
KScheduler::SetSchedulerUpdateNeeded(kernel);
}
@@ -1279,6 +1266,14 @@ KThread& GetCurrentThread(KernelCore& kernel) {
return *GetCurrentThreadPointer(kernel);
}
+KProcess* GetCurrentProcessPointer(KernelCore& kernel) {
+ return GetCurrentThread(kernel).GetOwnerProcess();
+}
+
+KProcess& GetCurrentProcess(KernelCore& kernel) {
+ return *GetCurrentProcessPointer(kernel);
+}
+
s32 GetCurrentCoreId(KernelCore& kernel) {
return GetCurrentThread(kernel).GetCurrentCore();
}
diff --git a/src/core/hle/kernel/k_thread.h b/src/core/hle/kernel/k_thread.h
index 7cd94a340..ca82ce3b6 100644
--- a/src/core/hle/kernel/k_thread.h
+++ b/src/core/hle/kernel/k_thread.h
@@ -110,6 +110,8 @@ enum class StepState : u32 {
void SetCurrentThread(KernelCore& kernel, KThread* thread);
[[nodiscard]] KThread* GetCurrentThreadPointer(KernelCore& kernel);
[[nodiscard]] KThread& GetCurrentThread(KernelCore& kernel);
+[[nodiscard]] KProcess* GetCurrentProcessPointer(KernelCore& kernel);
+[[nodiscard]] KProcess& GetCurrentProcess(KernelCore& kernel);
[[nodiscard]] s32 GetCurrentCoreId(KernelCore& kernel);
class KThread final : public KAutoObjectWithSlabHeapAndContainer<KThread, KWorkerTask>,
@@ -214,8 +216,6 @@ public:
void Continue();
- void WaitUntilSuspended();
-
constexpr void SetSyncedIndex(s32 index) {
synced_index = index;
}
@@ -607,13 +607,30 @@ public:
return address_key_value;
}
- void SetAddressKey(VAddr key) {
+ [[nodiscard]] bool GetAddressKeyIsKernel() const {
+ return address_key_is_kernel;
+ }
+
+ //! NB: intentional deviation from official kernel.
+ //
+ // Separate SetAddressKey into user and kernel versions
+ // to cope with arbitrary host pointers making their way
+ // into things.
+
+ void SetUserAddressKey(VAddr key) {
address_key = key;
+ address_key_is_kernel = false;
}
- void SetAddressKey(VAddr key, u32 val) {
+ void SetUserAddressKey(VAddr key, u32 val) {
address_key = key;
address_key_value = val;
+ address_key_is_kernel = false;
+ }
+
+ void SetKernelAddressKey(VAddr key) {
+ address_key = key;
+ address_key_is_kernel = true;
}
void ClearWaitQueue() {
@@ -662,7 +679,7 @@ private:
union SyncObjectBuffer {
std::array<KSynchronizationObject*, Svc::ArgumentHandleCountMax> sync_objects{};
std::array<Handle,
- Svc::ArgumentHandleCountMax*(sizeof(KSynchronizationObject*) / sizeof(Handle))>
+ Svc::ArgumentHandleCountMax * (sizeof(KSynchronizationObject*) / sizeof(Handle))>
handles;
constexpr SyncObjectBuffer() {}
};
@@ -683,10 +700,8 @@ private:
};
template <typename T>
- requires(
- std::same_as<T, KThread> ||
- std::same_as<T, RedBlackKeyType>) static constexpr int Compare(const T& lhs,
- const KThread& rhs) {
+ requires(std::same_as<T, KThread> || std::same_as<T, RedBlackKeyType>)
+ static constexpr int Compare(const T& lhs, const KThread& rhs) {
const u64 l_key = lhs.GetConditionVariableKey();
const u64 r_key = rhs.GetConditionVariableKey();
@@ -772,6 +787,7 @@ private:
bool debug_attached{};
s8 priority_inheritance_count{};
bool resource_limit_release_hint{};
+ bool address_key_is_kernel{};
StackParameters stack_parameters{};
Common::SpinLock context_guard{};
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:
}
template <typename T>
- requires(std::same_as<T, KThreadLocalPage> ||
- std::same_as<T, RedBlackKeyType>) static constexpr int Compare(const T& lhs,
- const KThreadLocalPage&
- rhs) {
+ requires(std::same_as<T, KThreadLocalPage> || std::same_as<T, RedBlackKeyType>)
+ static constexpr int Compare(const T& lhs, const KThreadLocalPage& rhs) {
const VAddr lval = GetRedBlackKey(lhs);
const VAddr rval = GetRedBlackKey(rhs);
diff --git a/src/core/hle/kernel/k_transfer_memory.cpp b/src/core/hle/kernel/k_transfer_memory.cpp
index 9f34c2d46..faa5c73b5 100644
--- a/src/core/hle/kernel/k_transfer_memory.cpp
+++ b/src/core/hle/kernel/k_transfer_memory.cpp
@@ -16,7 +16,7 @@ KTransferMemory::~KTransferMemory() = default;
Result KTransferMemory::Initialize(VAddr address_, std::size_t size_,
Svc::MemoryPermission owner_perm_) {
// Set members.
- owner = kernel.CurrentProcess();
+ owner = GetCurrentProcessPointer(kernel);
// TODO(bunnei): Lock for transfer memory
diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp
index 1fb25f221..3a68a5633 100644
--- a/src/core/hle/kernel/kernel.cpp
+++ b/src/core/hle/kernel/kernel.cpp
@@ -29,6 +29,7 @@
#include "core/hle/kernel/k_hardware_timer.h"
#include "core/hle/kernel/k_memory_layout.h"
#include "core/hle/kernel/k_memory_manager.h"
+#include "core/hle/kernel/k_object_name.h"
#include "core/hle/kernel/k_page_buffer.h"
#include "core/hle/kernel/k_process.h"
#include "core/hle/kernel/k_resource_limit.h"
@@ -84,6 +85,7 @@ struct KernelCore::Impl {
InitializeShutdownThreads();
InitializePhysicalCores();
InitializePreemption(kernel);
+ InitializeGlobalData(kernel);
// Initialize the Dynamic Slab Heaps.
{
@@ -102,13 +104,13 @@ struct KernelCore::Impl {
void InitializeCores() {
for (u32 core_id = 0; core_id < Core::Hardware::NUM_CPU_CORES; core_id++) {
- cores[core_id]->Initialize((*current_process).Is64BitProcess());
- system.Memory().SetCurrentPageTable(*current_process, core_id);
+ cores[core_id]->Initialize((*application_process).Is64BitProcess());
+ system.Memory().SetCurrentPageTable(*application_process, core_id);
}
}
- void CloseCurrentProcess() {
- KProcess* old_process = current_process.exchange(nullptr);
+ void CloseApplicationProcess() {
+ KProcess* old_process = application_process.exchange(nullptr);
if (old_process == nullptr) {
return;
}
@@ -182,7 +184,7 @@ struct KernelCore::Impl {
}
}
- CloseCurrentProcess();
+ CloseApplicationProcess();
// Track kernel objects that were not freed on shutdown
{
@@ -194,6 +196,8 @@ struct KernelCore::Impl {
}
}
+ object_name_global_data.reset();
+
// Ensure that the object list container is finalized and properly shutdown.
global_object_list_container->Finalize();
global_object_list_container.reset();
@@ -363,8 +367,12 @@ struct KernelCore::Impl {
}
}
- void MakeCurrentProcess(KProcess* process) {
- current_process = process;
+ void InitializeGlobalData(KernelCore& kernel) {
+ object_name_global_data = std::make_unique<KObjectNameGlobalData>(kernel);
+ }
+
+ void MakeApplicationProcess(KProcess* process) {
+ application_process = process;
}
static inline thread_local u32 host_thread_id = UINT32_MAX;
@@ -821,7 +829,7 @@ struct KernelCore::Impl {
// Lists all processes that exist in the current session.
std::vector<KProcess*> process_list;
- std::atomic<KProcess*> current_process{};
+ std::atomic<KProcess*> application_process{};
std::unique_ptr<Kernel::GlobalSchedulerContext> global_scheduler_context;
std::unique_ptr<Kernel::KHardwareTimer> hardware_timer;
@@ -838,6 +846,8 @@ struct KernelCore::Impl {
std::unique_ptr<KAutoObjectWithListContainer> global_object_list_container;
+ std::unique_ptr<KObjectNameGlobalData> object_name_global_data;
+
/// Map of named ports managed by the kernel, which can be retrieved using
/// the ConnectToPort SVC.
std::unordered_map<std::string, ServiceInterfaceFactory> service_interface_factory;
@@ -941,20 +951,20 @@ void KernelCore::AppendNewProcess(KProcess* process) {
impl->process_list.push_back(process);
}
-void KernelCore::MakeCurrentProcess(KProcess* process) {
- impl->MakeCurrentProcess(process);
+void KernelCore::MakeApplicationProcess(KProcess* process) {
+ impl->MakeApplicationProcess(process);
}
-KProcess* KernelCore::CurrentProcess() {
- return impl->current_process;
+KProcess* KernelCore::ApplicationProcess() {
+ return impl->application_process;
}
-const KProcess* KernelCore::CurrentProcess() const {
- return impl->current_process;
+const KProcess* KernelCore::ApplicationProcess() const {
+ return impl->application_process;
}
-void KernelCore::CloseCurrentProcess() {
- impl->CloseCurrentProcess();
+void KernelCore::CloseApplicationProcess() {
+ impl->CloseApplicationProcess();
}
const std::vector<KProcess*>& KernelCore::GetProcessList() const {
@@ -1138,6 +1148,10 @@ void KernelCore::SetCurrentEmuThread(KThread* thread) {
impl->SetCurrentEmuThread(thread);
}
+KObjectNameGlobalData& KernelCore::ObjectNameGlobalData() {
+ return *impl->object_name_global_data;
+}
+
KMemoryManager& KernelCore::MemoryManager() {
return *impl->memory_manager;
}
@@ -1146,6 +1160,14 @@ const KMemoryManager& KernelCore::MemoryManager() const {
return *impl->memory_manager;
}
+KSystemResource& KernelCore::GetAppSystemResource() {
+ return *impl->app_system_resource;
+}
+
+const KSystemResource& KernelCore::GetAppSystemResource() const {
+ return *impl->app_system_resource;
+}
+
KSystemResource& KernelCore::GetSystemSystemResource() {
return *impl->sys_system_resource;
}
@@ -1194,32 +1216,39 @@ const Kernel::KSharedMemory& KernelCore::GetHidBusSharedMem() const {
return *impl->hidbus_shared_mem;
}
-void KernelCore::Suspend(bool suspended) {
+void KernelCore::SuspendApplication(bool suspended) {
const bool should_suspend{exception_exited || suspended};
const auto activity = should_suspend ? ProcessActivity::Paused : ProcessActivity::Runnable;
- std::vector<KScopedAutoObject<KThread>> process_threads;
- {
- KScopedSchedulerLock sl{*this};
+ // Get the application process.
+ KScopedAutoObject<KProcess> process = ApplicationProcess();
+ if (process.IsNull()) {
+ return;
+ }
- if (auto* process = CurrentProcess(); process != nullptr) {
- process->SetActivity(activity);
+ // Set the new activity.
+ process->SetActivity(activity);
- if (!should_suspend) {
- // Runnable now; no need to wait.
- return;
- }
+ // Wait for process execution to stop.
+ bool must_wait{should_suspend};
- for (auto* thread : process->GetThreadList()) {
- process_threads.emplace_back(thread);
+ // KernelCore::SuspendApplication must be called from locked context,
+ // or we could race another call to SetActivity, interfering with waiting.
+ while (must_wait) {
+ KScopedSchedulerLock sl{*this};
+
+ // Assume that all threads have finished running.
+ must_wait = false;
+
+ for (auto i = 0; i < static_cast<s32>(Core::Hardware::NUM_CPU_CORES); ++i) {
+ if (Scheduler(i).GetSchedulerCurrentThread()->GetOwnerProcess() ==
+ process.GetPointerUnsafe()) {
+ // A thread has not finished running yet.
+ // Continue waiting.
+ must_wait = true;
}
}
}
-
- // Wait for execution to stop.
- for (auto& thread : process_threads) {
- thread->WaitUntilSuspended();
- }
}
void KernelCore::ShutdownCores() {
@@ -1238,9 +1267,9 @@ bool KernelCore::IsShuttingDown() const {
return impl->IsShuttingDown();
}
-void KernelCore::ExceptionalExit() {
+void KernelCore::ExceptionalExitApplication() {
exception_exited = true;
- Suspend(true);
+ SuspendApplication(true);
}
void KernelCore::EnterSVCProfile() {
diff --git a/src/core/hle/kernel/kernel.h b/src/core/hle/kernel/kernel.h
index 8d22f8d2c..6e0668f7f 100644
--- a/src/core/hle/kernel/kernel.h
+++ b/src/core/hle/kernel/kernel.h
@@ -35,6 +35,7 @@ class GlobalSchedulerContext;
class KAutoObjectWithListContainer;
class KClientSession;
class KDebug;
+class KDeviceAddressSpace;
class KDynamicPageManager;
class KEvent;
class KEventInfo;
@@ -43,6 +44,8 @@ class KHardwareTimer;
class KLinkedListNode;
class KMemoryLayout;
class KMemoryManager;
+class KObjectName;
+class KObjectNameGlobalData;
class KPageBuffer;
class KPageBufferSlabHeap;
class KPort;
@@ -130,17 +133,17 @@ public:
/// Adds the given shared pointer to an internal list of active processes.
void AppendNewProcess(KProcess* process);
- /// Makes the given process the new current process.
- void MakeCurrentProcess(KProcess* process);
+ /// Makes the given process the new application process.
+ void MakeApplicationProcess(KProcess* process);
- /// Retrieves a pointer to the current process.
- KProcess* CurrentProcess();
+ /// Retrieves a pointer to the application process.
+ KProcess* ApplicationProcess();
- /// Retrieves a const pointer to the current process.
- const KProcess* CurrentProcess() const;
+ /// Retrieves a const pointer to the application process.
+ const KProcess* ApplicationProcess() const;
- /// Closes the current process.
- void CloseCurrentProcess();
+ /// Closes the application process.
+ void CloseApplicationProcess();
/// Retrieves the list of processes.
const std::vector<KProcess*>& GetProcessList() const;
@@ -239,12 +242,21 @@ public:
/// Register the current thread as a non CPU core thread.
void RegisterHostThread(KThread* existing_thread = nullptr);
+ /// Gets global data for KObjectName.
+ KObjectNameGlobalData& ObjectNameGlobalData();
+
/// Gets the virtual memory manager for the kernel.
KMemoryManager& MemoryManager();
/// Gets the virtual memory manager for the kernel.
const KMemoryManager& MemoryManager() const;
+ /// Gets the application resource manager.
+ KSystemResource& GetAppSystemResource();
+
+ /// Gets the application resource manager.
+ const KSystemResource& GetAppSystemResource() const;
+
/// Gets the system resource manager.
KSystemResource& GetSystemSystemResource();
@@ -281,11 +293,11 @@ public:
/// Gets the shared memory object for HIDBus services.
const Kernel::KSharedMemory& GetHidBusSharedMem() const;
- /// Suspend/unsuspend all processes.
- void Suspend(bool suspend);
+ /// Suspend/unsuspend application process.
+ void SuspendApplication(bool suspend);
- /// Exceptional exit all processes.
- void ExceptionalExit();
+ /// Exceptional exit application process.
+ void ExceptionalExitApplication();
/// Notify emulated CPU cores to shut down.
void ShutdownCores();
@@ -359,10 +371,14 @@ public:
return slab_heap_container->transfer_memory;
} else if constexpr (std::is_same_v<T, KCodeMemory>) {
return slab_heap_container->code_memory;
+ } else if constexpr (std::is_same_v<T, KDeviceAddressSpace>) {
+ return slab_heap_container->device_address_space;
} else if constexpr (std::is_same_v<T, KPageBuffer>) {
return slab_heap_container->page_buffer;
} else if constexpr (std::is_same_v<T, KThreadLocalPage>) {
return slab_heap_container->thread_local_page;
+ } else if constexpr (std::is_same_v<T, KObjectName>) {
+ return slab_heap_container->object_name;
} else if constexpr (std::is_same_v<T, KSessionRequest>) {
return slab_heap_container->session_request;
} else if constexpr (std::is_same_v<T, KSecureSystemResource>) {
@@ -431,8 +447,10 @@ private:
KSlabHeap<KThread> thread;
KSlabHeap<KTransferMemory> transfer_memory;
KSlabHeap<KCodeMemory> code_memory;
+ KSlabHeap<KDeviceAddressSpace> device_address_space;
KSlabHeap<KPageBuffer> page_buffer;
KSlabHeap<KThreadLocalPage> thread_local_page;
+ KSlabHeap<KObjectName> object_name;
KSlabHeap<KSessionRequest> session_request;
KSlabHeap<KSecureSystemResource> secure_system_resource;
KSlabHeap<KEventInfo> event_info;
diff --git a/src/core/hle/kernel/memory_types.h b/src/core/hle/kernel/memory_types.h
index 3975507bd..92b8b37ac 100644
--- a/src/core/hle/kernel/memory_types.h
+++ b/src/core/hle/kernel/memory_types.h
@@ -14,4 +14,7 @@ constexpr std::size_t PageSize{1 << PageBits};
using Page = std::array<u8, PageSize>;
+using KPhysicalAddress = PAddr;
+using KProcessAddress = VAddr;
+
} // namespace Kernel
diff --git a/src/core/hle/kernel/physical_core.h b/src/core/hle/kernel/physical_core.h
index fb2ba4c6b..fb8e7933e 100644
--- a/src/core/hle/kernel/physical_core.h
+++ b/src/core/hle/kernel/physical_core.h
@@ -3,6 +3,7 @@
#pragma once
+#include <condition_variable>
#include <cstddef>
#include <memory>
#include <mutex>
diff --git a/src/core/hle/kernel/svc.cpp b/src/core/hle/kernel/svc.cpp
index 788ee2160..1072da8cc 100644
--- a/src/core/hle/kernel/svc.cpp
+++ b/src/core/hle/kernel/svc.cpp
@@ -1,3129 +1,4435 @@
-// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
-#include <algorithm>
-#include <cinttypes>
-#include <iterator>
-#include <mutex>
-#include <vector>
-
-#include "common/alignment.h"
-#include "common/assert.h"
-#include "common/common_funcs.h"
-#include "common/fiber.h"
-#include "common/logging/log.h"
-#include "common/scope_exit.h"
+// This file is automatically generated using svc_generator.py.
+
+#include <type_traits>
+
+#include "core/arm/arm_interface.h"
#include "core/core.h"
-#include "core/core_timing.h"
-#include "core/debugger/debugger.h"
-#include "core/hle/kernel/k_client_port.h"
-#include "core/hle/kernel/k_client_session.h"
-#include "core/hle/kernel/k_code_memory.h"
-#include "core/hle/kernel/k_event.h"
-#include "core/hle/kernel/k_handle_table.h"
-#include "core/hle/kernel/k_memory_block.h"
-#include "core/hle/kernel/k_memory_layout.h"
-#include "core/hle/kernel/k_page_table.h"
-#include "core/hle/kernel/k_port.h"
#include "core/hle/kernel/k_process.h"
-#include "core/hle/kernel/k_readable_event.h"
-#include "core/hle/kernel/k_resource_limit.h"
-#include "core/hle/kernel/k_scheduler.h"
-#include "core/hle/kernel/k_scoped_resource_reservation.h"
-#include "core/hle/kernel/k_session.h"
-#include "core/hle/kernel/k_shared_memory.h"
-#include "core/hle/kernel/k_synchronization_object.h"
-#include "core/hle/kernel/k_thread.h"
-#include "core/hle/kernel/k_thread_queue.h"
-#include "core/hle/kernel/k_transfer_memory.h"
-#include "core/hle/kernel/kernel.h"
-#include "core/hle/kernel/physical_core.h"
#include "core/hle/kernel/svc.h"
-#include "core/hle/kernel/svc_results.h"
-#include "core/hle/kernel/svc_types.h"
-#include "core/hle/kernel/svc_wrap.h"
-#include "core/hle/result.h"
-#include "core/memory.h"
-#include "core/reporter.h"
namespace Kernel::Svc {
-namespace {
-
-// Checks if address + size is greater than the given address
-// This can return false if the size causes an overflow of a 64-bit type
-// or if the given size is zero.
-constexpr bool IsValidAddressRange(VAddr address, u64 size) {
- return address + size > address;
-}
-
-// Helper function that performs the common sanity checks for svcMapMemory
-// and svcUnmapMemory. This is doable, as both functions perform their sanitizing
-// in the same order.
-Result MapUnmapMemorySanityChecks(const KPageTable& manager, VAddr dst_addr, VAddr src_addr,
- u64 size) {
- if (!Common::Is4KBAligned(dst_addr)) {
- LOG_ERROR(Kernel_SVC, "Destination address is not aligned to 4KB, 0x{:016X}", dst_addr);
- return ResultInvalidAddress;
- }
- if (!Common::Is4KBAligned(src_addr)) {
- LOG_ERROR(Kernel_SVC, "Source address is not aligned to 4KB, 0x{:016X}", src_addr);
- return ResultInvalidSize;
- }
+static uint32_t GetReg32(Core::System& system, int n) {
+ return static_cast<uint32_t>(system.CurrentArmInterface().GetReg(n));
+}
- if (size == 0) {
- LOG_ERROR(Kernel_SVC, "Size is 0");
- return ResultInvalidSize;
- }
+static void SetReg32(Core::System& system, int n, uint32_t result) {
+ system.CurrentArmInterface().SetReg(n, static_cast<uint64_t>(result));
+}
- if (!Common::Is4KBAligned(size)) {
- LOG_ERROR(Kernel_SVC, "Size is not aligned to 4KB, 0x{:016X}", size);
- return ResultInvalidSize;
- }
+static uint64_t GetReg64(Core::System& system, int n) {
+ return system.CurrentArmInterface().GetReg(n);
+}
- if (!IsValidAddressRange(dst_addr, size)) {
- LOG_ERROR(Kernel_SVC,
- "Destination is not a valid address range, addr=0x{:016X}, size=0x{:016X}",
- dst_addr, size);
- return ResultInvalidCurrentMemory;
- }
+static void SetReg64(Core::System& system, int n, uint64_t result) {
+ system.CurrentArmInterface().SetReg(n, result);
+}
- if (!IsValidAddressRange(src_addr, size)) {
- LOG_ERROR(Kernel_SVC, "Source is not a valid address range, addr=0x{:016X}, size=0x{:016X}",
- src_addr, size);
- return ResultInvalidCurrentMemory;
- }
+// Like bit_cast, but handles the case when the source and dest
+// are differently-sized.
+template <typename To, typename From>
+ requires(std::is_trivial_v<To> && std::is_trivially_copyable_v<From>)
+static To Convert(const From& from) {
+ To to{};
- if (!manager.IsInsideAddressSpace(src_addr, size)) {
- LOG_ERROR(Kernel_SVC,
- "Source is not within the address space, addr=0x{:016X}, size=0x{:016X}",
- src_addr, size);
- return ResultInvalidCurrentMemory;
+ if constexpr (sizeof(To) >= sizeof(From)) {
+ std::memcpy(&to, &from, sizeof(From));
+ } else {
+ std::memcpy(&to, &from, sizeof(To));
}
- if (manager.IsOutsideStackRegion(dst_addr, size)) {
- LOG_ERROR(Kernel_SVC,
- "Destination is not within the stack region, addr=0x{:016X}, size=0x{:016X}",
- dst_addr, size);
- return ResultInvalidMemoryRegion;
- }
+ return to;
+}
- if (manager.IsInsideHeapRegion(dst_addr, size)) {
- LOG_ERROR(Kernel_SVC,
- "Destination does not fit within the heap region, addr=0x{:016X}, "
- "size=0x{:016X}",
- dst_addr, size);
- return ResultInvalidMemoryRegion;
- }
+// clang-format off
+static_assert(sizeof(ArbitrationType) == 4);
+static_assert(sizeof(BreakReason) == 4);
+static_assert(sizeof(CodeMemoryOperation) == 4);
+static_assert(sizeof(DebugThreadParam) == 4);
+static_assert(sizeof(DeviceName) == 4);
+static_assert(sizeof(HardwareBreakPointRegisterName) == 4);
+static_assert(sizeof(Handle) == 4);
+static_assert(sizeof(InfoType) == 4);
+static_assert(sizeof(InterruptType) == 4);
+static_assert(sizeof(IoPoolType) == 4);
+static_assert(sizeof(KernelDebugType) == 4);
+static_assert(sizeof(KernelTraceState) == 4);
+static_assert(sizeof(LimitableResource) == 4);
+static_assert(sizeof(MemoryMapping) == 4);
+static_assert(sizeof(MemoryPermission) == 4);
+static_assert(sizeof(PageInfo) == 4);
+static_assert(sizeof(ProcessActivity) == 4);
+static_assert(sizeof(ProcessInfoType) == 4);
+static_assert(sizeof(Result) == 4);
+static_assert(sizeof(SignalType) == 4);
+static_assert(sizeof(SystemInfoType) == 4);
+static_assert(sizeof(ThreadActivity) == 4);
+static_assert(sizeof(ilp32::LastThreadContext) == 16);
+static_assert(sizeof(ilp32::PhysicalMemoryInfo) == 16);
+static_assert(sizeof(ilp32::SecureMonitorArguments) == 32);
+static_assert(sizeof(lp64::LastThreadContext) == 32);
+static_assert(sizeof(lp64::PhysicalMemoryInfo) == 24);
+static_assert(sizeof(lp64::SecureMonitorArguments) == 64);
+static_assert(sizeof(bool) == 1);
+static_assert(sizeof(int32_t) == 4);
+static_assert(sizeof(int64_t) == 8);
+static_assert(sizeof(uint32_t) == 4);
+static_assert(sizeof(uint64_t) == 8);
- if (manager.IsInsideAliasRegion(dst_addr, size)) {
- LOG_ERROR(Kernel_SVC,
- "Destination does not fit within the map region, addr=0x{:016X}, "
- "size=0x{:016X}",
- dst_addr, size);
- return ResultInvalidMemoryRegion;
- }
+static void SvcWrap_SetHeapSize64From32(Core::System& system) {
+ Result ret{};
+
+ uintptr_t out_address{};
+ uint32_t size{};
+
+ size = Convert<uint32_t>(GetReg32(system, 1));
+
+ ret = SetHeapSize64From32(system, &out_address, size);
- return ResultSuccess;
+ SetReg32(system, 0, Convert<uint32_t>(ret));
+ SetReg32(system, 1, Convert<uint32_t>(out_address));
}
-enum class ResourceLimitValueType {
- CurrentValue,
- LimitValue,
- PeakValue,
-};
+static void SvcWrap_SetMemoryPermission64From32(Core::System& system) {
+ Result ret{};
-} // Anonymous namespace
+ uint32_t address{};
+ uint32_t size{};
+ MemoryPermission perm{};
+
+ address = Convert<uint32_t>(GetReg32(system, 0));
+ size = Convert<uint32_t>(GetReg32(system, 1));
+ perm = Convert<MemoryPermission>(GetReg32(system, 2));
+
+ ret = SetMemoryPermission64From32(system, address, size, perm);
+
+ SetReg32(system, 0, Convert<uint32_t>(ret));
+}
-/// Set the process heap to a given Size. It can both extend and shrink the heap.
-static Result SetHeapSize(Core::System& system, VAddr* out_address, u64 size) {
- LOG_TRACE(Kernel_SVC, "called, heap_size=0x{:X}", size);
+static void SvcWrap_SetMemoryAttribute64From32(Core::System& system) {
+ Result ret{};
- // Validate size.
- R_UNLESS(Common::IsAligned(size, HeapSizeAlignment), ResultInvalidSize);
- R_UNLESS(size < MainMemorySizeMax, ResultInvalidSize);
+ uint32_t address{};
+ uint32_t size{};
+ uint32_t mask{};
+ uint32_t attr{};
- // Set the heap size.
- R_TRY(system.Kernel().CurrentProcess()->PageTable().SetHeapSize(out_address, size));
+ address = Convert<uint32_t>(GetReg32(system, 0));
+ size = Convert<uint32_t>(GetReg32(system, 1));
+ mask = Convert<uint32_t>(GetReg32(system, 2));
+ attr = Convert<uint32_t>(GetReg32(system, 3));
- return ResultSuccess;
+ ret = SetMemoryAttribute64From32(system, address, size, mask, attr);
+
+ SetReg32(system, 0, Convert<uint32_t>(ret));
}
-static Result SetHeapSize32(Core::System& system, u32* heap_addr, u32 heap_size) {
- VAddr temp_heap_addr{};
- const Result result{SetHeapSize(system, &temp_heap_addr, heap_size)};
- *heap_addr = static_cast<u32>(temp_heap_addr);
- return result;
+static void SvcWrap_MapMemory64From32(Core::System& system) {
+ Result ret{};
+
+ uint32_t dst_address{};
+ uint32_t src_address{};
+ uint32_t size{};
+
+ dst_address = Convert<uint32_t>(GetReg32(system, 0));
+ src_address = Convert<uint32_t>(GetReg32(system, 1));
+ size = Convert<uint32_t>(GetReg32(system, 2));
+
+ ret = MapMemory64From32(system, dst_address, src_address, size);
+
+ SetReg32(system, 0, Convert<uint32_t>(ret));
}
-constexpr bool IsValidSetMemoryPermission(MemoryPermission perm) {
- switch (perm) {
- case MemoryPermission::None:
- case MemoryPermission::Read:
- case MemoryPermission::ReadWrite:
- return true;
- default:
- return false;
- }
+static void SvcWrap_UnmapMemory64From32(Core::System& system) {
+ Result ret{};
+
+ uint32_t dst_address{};
+ uint32_t src_address{};
+ uint32_t size{};
+
+ dst_address = Convert<uint32_t>(GetReg32(system, 0));
+ src_address = Convert<uint32_t>(GetReg32(system, 1));
+ size = Convert<uint32_t>(GetReg32(system, 2));
+
+ ret = UnmapMemory64From32(system, dst_address, src_address, size);
+
+ SetReg32(system, 0, Convert<uint32_t>(ret));
}
-static Result SetMemoryPermission(Core::System& system, VAddr address, u64 size,
- MemoryPermission perm) {
- LOG_DEBUG(Kernel_SVC, "called, address=0x{:016X}, size=0x{:X}, perm=0x{:08X", address, size,
- perm);
+static void SvcWrap_QueryMemory64From32(Core::System& system) {
+ Result ret{};
- // Validate address / size.
- R_UNLESS(Common::IsAligned(address, PageSize), ResultInvalidAddress);
- R_UNLESS(Common::IsAligned(size, PageSize), ResultInvalidSize);
- R_UNLESS(size > 0, ResultInvalidSize);
- R_UNLESS((address < address + size), ResultInvalidCurrentMemory);
+ PageInfo out_page_info{};
+ uint32_t out_memory_info{};
+ uint32_t address{};
- // Validate the permission.
- R_UNLESS(IsValidSetMemoryPermission(perm), ResultInvalidNewMemoryPermission);
+ out_memory_info = Convert<uint32_t>(GetReg32(system, 0));
+ address = Convert<uint32_t>(GetReg32(system, 2));
- // Validate that the region is in range for the current process.
- auto& page_table = system.Kernel().CurrentProcess()->PageTable();
- R_UNLESS(page_table.Contains(address, size), ResultInvalidCurrentMemory);
+ ret = QueryMemory64From32(system, out_memory_info, &out_page_info, address);
- // Set the memory attribute.
- return page_table.SetMemoryPermission(address, size, perm);
+ SetReg32(system, 0, Convert<uint32_t>(ret));
+ SetReg32(system, 1, Convert<uint32_t>(out_page_info));
}
-static Result SetMemoryAttribute(Core::System& system, VAddr address, u64 size, u32 mask,
- u32 attr) {
- LOG_DEBUG(Kernel_SVC,
- "called, address=0x{:016X}, size=0x{:X}, mask=0x{:08X}, attribute=0x{:08X}", address,
- size, mask, attr);
+static void SvcWrap_ExitProcess64From32(Core::System& system) {
+ ExitProcess64From32(system);
+}
+
+static void SvcWrap_CreateThread64From32(Core::System& system) {
+ Result ret{};
- // Validate address / size.
- R_UNLESS(Common::IsAligned(address, PageSize), ResultInvalidAddress);
- R_UNLESS(Common::IsAligned(size, PageSize), ResultInvalidSize);
- R_UNLESS(size > 0, ResultInvalidSize);
- R_UNLESS((address < address + size), ResultInvalidCurrentMemory);
+ Handle out_handle{};
+ uint32_t func{};
+ uint32_t arg{};
+ uint32_t stack_bottom{};
+ int32_t priority{};
+ int32_t core_id{};
- // Validate the attribute and mask.
- constexpr u32 SupportedMask = static_cast<u32>(MemoryAttribute::Uncached);
- R_UNLESS((mask | attr) == mask, ResultInvalidCombination);
- R_UNLESS((mask | attr | SupportedMask) == SupportedMask, ResultInvalidCombination);
+ func = Convert<uint32_t>(GetReg32(system, 1));
+ arg = Convert<uint32_t>(GetReg32(system, 2));
+ stack_bottom = Convert<uint32_t>(GetReg32(system, 3));
+ priority = Convert<int32_t>(GetReg32(system, 0));
+ core_id = Convert<int32_t>(GetReg32(system, 4));
- // Validate that the region is in range for the current process.
- auto& page_table{system.Kernel().CurrentProcess()->PageTable()};
- R_UNLESS(page_table.Contains(address, size), ResultInvalidCurrentMemory);
+ ret = CreateThread64From32(system, &out_handle, func, arg, stack_bottom, priority, core_id);
- // Set the memory attribute.
- return page_table.SetMemoryAttribute(address, size, mask, attr);
+ SetReg32(system, 0, Convert<uint32_t>(ret));
+ SetReg32(system, 1, Convert<uint32_t>(out_handle));
}
-static Result SetMemoryAttribute32(Core::System& system, u32 address, u32 size, u32 mask,
- u32 attr) {
- return SetMemoryAttribute(system, address, size, mask, attr);
+static void SvcWrap_StartThread64From32(Core::System& system) {
+ Result ret{};
+
+ Handle thread_handle{};
+
+ thread_handle = Convert<Handle>(GetReg32(system, 0));
+
+ ret = StartThread64From32(system, thread_handle);
+
+ SetReg32(system, 0, Convert<uint32_t>(ret));
}
-/// Maps a memory range into a different range.
-static Result MapMemory(Core::System& system, VAddr dst_addr, VAddr src_addr, u64 size) {
- LOG_TRACE(Kernel_SVC, "called, dst_addr=0x{:X}, src_addr=0x{:X}, size=0x{:X}", dst_addr,
- src_addr, size);
+static void SvcWrap_ExitThread64From32(Core::System& system) {
+ ExitThread64From32(system);
+}
- auto& page_table{system.Kernel().CurrentProcess()->PageTable()};
+static void SvcWrap_SleepThread64From32(Core::System& system) {
+ int64_t ns{};
- if (const Result result{MapUnmapMemorySanityChecks(page_table, dst_addr, src_addr, size)};
- result.IsError()) {
- return result;
- }
+ std::array<uint32_t, 2> ns_gather{};
+ ns_gather[0] = GetReg32(system, 0);
+ ns_gather[1] = GetReg32(system, 1);
+ ns = Convert<int64_t>(ns_gather);
- return page_table.MapMemory(dst_addr, src_addr, size);
+ SleepThread64From32(system, ns);
}
-static Result MapMemory32(Core::System& system, u32 dst_addr, u32 src_addr, u32 size) {
- return MapMemory(system, dst_addr, src_addr, size);
+static void SvcWrap_GetThreadPriority64From32(Core::System& system) {
+ Result ret{};
+
+ int32_t out_priority{};
+ Handle thread_handle{};
+
+ thread_handle = Convert<Handle>(GetReg32(system, 1));
+
+ ret = GetThreadPriority64From32(system, &out_priority, thread_handle);
+
+ SetReg32(system, 0, Convert<uint32_t>(ret));
+ SetReg32(system, 1, Convert<uint32_t>(out_priority));
}
-/// Unmaps a region that was previously mapped with svcMapMemory
-static Result UnmapMemory(Core::System& system, VAddr dst_addr, VAddr src_addr, u64 size) {
- LOG_TRACE(Kernel_SVC, "called, dst_addr=0x{:X}, src_addr=0x{:X}, size=0x{:X}", dst_addr,
- src_addr, size);
+static void SvcWrap_SetThreadPriority64From32(Core::System& system) {
+ Result ret{};
- auto& page_table{system.Kernel().CurrentProcess()->PageTable()};
+ Handle thread_handle{};
+ int32_t priority{};
- if (const Result result{MapUnmapMemorySanityChecks(page_table, dst_addr, src_addr, size)};
- result.IsError()) {
- return result;
- }
+ thread_handle = Convert<Handle>(GetReg32(system, 0));
+ priority = Convert<int32_t>(GetReg32(system, 1));
- return page_table.UnmapMemory(dst_addr, src_addr, size);
+ ret = SetThreadPriority64From32(system, thread_handle, priority);
+
+ SetReg32(system, 0, Convert<uint32_t>(ret));
}
-static Result UnmapMemory32(Core::System& system, u32 dst_addr, u32 src_addr, u32 size) {
- return UnmapMemory(system, dst_addr, src_addr, size);
+static void SvcWrap_GetThreadCoreMask64From32(Core::System& system) {
+ Result ret{};
+
+ int32_t out_core_id{};
+ uint64_t out_affinity_mask{};
+ Handle thread_handle{};
+
+ thread_handle = Convert<Handle>(GetReg32(system, 2));
+
+ ret = GetThreadCoreMask64From32(system, &out_core_id, &out_affinity_mask, thread_handle);
+
+ SetReg32(system, 0, Convert<uint32_t>(ret));
+ SetReg32(system, 1, Convert<uint32_t>(out_core_id));
+ auto out_affinity_mask_scatter = Convert<std::array<uint32_t, 2>>(out_affinity_mask);
+ SetReg32(system, 2, out_affinity_mask_scatter[0]);
+ SetReg32(system, 3, out_affinity_mask_scatter[1]);
}
-template <typename T>
-Result CreateSession(Core::System& system, Handle* out_server, Handle* out_client, u64 name) {
- auto& process = *system.CurrentProcess();
- auto& handle_table = process.GetHandleTable();
+static void SvcWrap_SetThreadCoreMask64From32(Core::System& system) {
+ Result ret{};
- // Declare the session we're going to allocate.
- T* session;
+ Handle thread_handle{};
+ int32_t core_id{};
+ uint64_t affinity_mask{};
- // Reserve a new session from the process resource limit.
- // FIXME: LimitableResource_SessionCountMax
- KScopedResourceReservation session_reservation(&process, LimitableResource::SessionCountMax);
- if (session_reservation.Succeeded()) {
- session = T::Create(system.Kernel());
- } else {
- return ResultLimitReached;
-
- // // We couldn't reserve a session. Check that we support dynamically expanding the
- // // resource limit.
- // R_UNLESS(process.GetResourceLimit() ==
- // &system.Kernel().GetSystemResourceLimit(), ResultLimitReached);
- // R_UNLESS(KTargetSystem::IsDynamicResourceLimitsEnabled(), ResultLimitReached());
-
- // // Try to allocate a session from unused slab memory.
- // session = T::CreateFromUnusedSlabMemory();
- // R_UNLESS(session != nullptr, ResultLimitReached);
- // ON_RESULT_FAILURE { session->Close(); };
-
- // // If we're creating a KSession, we want to add two KSessionRequests to the heap, to
- // // prevent request exhaustion.
- // // NOTE: Nintendo checks if session->DynamicCast<KSession *>() != nullptr, but there's
- // // no reason to not do this statically.
- // if constexpr (std::same_as<T, KSession>) {
- // for (size_t i = 0; i < 2; i++) {
- // KSessionRequest* request = KSessionRequest::CreateFromUnusedSlabMemory();
- // R_UNLESS(request != nullptr, ResultLimitReached);
- // request->Close();
- // }
- // }
-
- // We successfully allocated a session, so add the object we allocated to the resource
- // limit.
- // system.Kernel().GetSystemResourceLimit().Reserve(LimitableResource::SessionCountMax, 1);
- }
+ thread_handle = Convert<Handle>(GetReg32(system, 0));
+ core_id = Convert<int32_t>(GetReg32(system, 1));
+ std::array<uint32_t, 2> affinity_mask_gather{};
+ affinity_mask_gather[0] = GetReg32(system, 2);
+ affinity_mask_gather[1] = GetReg32(system, 3);
+ affinity_mask = Convert<uint64_t>(affinity_mask_gather);
- // Check that we successfully created a session.
- R_UNLESS(session != nullptr, ResultOutOfResource);
+ ret = SetThreadCoreMask64From32(system, thread_handle, core_id, affinity_mask);
- // Initialize the session.
- session->Initialize(nullptr, fmt::format("{}", name));
+ SetReg32(system, 0, Convert<uint32_t>(ret));
+}
- // Commit the session reservation.
- session_reservation.Commit();
+static void SvcWrap_GetCurrentProcessorNumber64From32(Core::System& system) {
+ int32_t ret{};
- // Ensure that we clean up the session (and its only references are handle table) on function
- // end.
- SCOPE_EXIT({
- session->GetClientSession().Close();
- session->GetServerSession().Close();
- });
+ ret = GetCurrentProcessorNumber64From32(system);
- // Register the session.
- T::Register(system.Kernel(), session);
+ SetReg32(system, 0, Convert<uint32_t>(ret));
+}
- // Add the server session to the handle table.
- R_TRY(handle_table.Add(out_server, &session->GetServerSession()));
+static void SvcWrap_SignalEvent64From32(Core::System& system) {
+ Result ret{};
- // Add the client session to the handle table.
- const auto result = handle_table.Add(out_client, &session->GetClientSession());
+ Handle event_handle{};
- if (!R_SUCCEEDED(result)) {
- // Ensure that we maintaing a clean handle state on exit.
- handle_table.Remove(*out_server);
- }
+ event_handle = Convert<Handle>(GetReg32(system, 0));
- return result;
+ ret = SignalEvent64From32(system, event_handle);
+
+ SetReg32(system, 0, Convert<uint32_t>(ret));
}
-static Result CreateSession(Core::System& system, Handle* out_server, Handle* out_client,
- u32 is_light, u64 name) {
- if (is_light) {
- // return CreateSession<KLightSession>(system, out_server, out_client, name);
- return ResultUnknown;
- } else {
- return CreateSession<KSession>(system, out_server, out_client, name);
- }
+static void SvcWrap_ClearEvent64From32(Core::System& system) {
+ Result ret{};
+
+ Handle event_handle{};
+
+ event_handle = Convert<Handle>(GetReg32(system, 0));
+
+ ret = ClearEvent64From32(system, event_handle);
+
+ SetReg32(system, 0, Convert<uint32_t>(ret));
}
-/// Connect to an OS service given the port name, returns the handle to the port to out
-static Result ConnectToNamedPort(Core::System& system, Handle* out, VAddr port_name_address) {
- auto& memory = system.Memory();
- if (!memory.IsValidVirtualAddress(port_name_address)) {
- LOG_ERROR(Kernel_SVC,
- "Port Name Address is not a valid virtual address, port_name_address=0x{:016X}",
- port_name_address);
- return ResultNotFound;
- }
+static void SvcWrap_MapSharedMemory64From32(Core::System& system) {
+ Result ret{};
- static constexpr std::size_t PortNameMaxLength = 11;
- // Read 1 char beyond the max allowed port name to detect names that are too long.
- const std::string port_name = memory.ReadCString(port_name_address, PortNameMaxLength + 1);
- if (port_name.size() > PortNameMaxLength) {
- LOG_ERROR(Kernel_SVC, "Port name is too long, expected {} but got {}", PortNameMaxLength,
- port_name.size());
- return ResultOutOfRange;
- }
+ Handle shmem_handle{};
+ uint32_t address{};
+ uint32_t size{};
+ MemoryPermission map_perm{};
- LOG_TRACE(Kernel_SVC, "called port_name={}", port_name);
+ shmem_handle = Convert<Handle>(GetReg32(system, 0));
+ address = Convert<uint32_t>(GetReg32(system, 1));
+ size = Convert<uint32_t>(GetReg32(system, 2));
+ map_perm = Convert<MemoryPermission>(GetReg32(system, 3));
- // Get the current handle table.
- auto& kernel = system.Kernel();
- auto& handle_table = kernel.CurrentProcess()->GetHandleTable();
+ ret = MapSharedMemory64From32(system, shmem_handle, address, size, map_perm);
- // Find the client port.
- auto port = kernel.CreateNamedServicePort(port_name);
- if (!port) {
- LOG_ERROR(Kernel_SVC, "tried to connect to unknown port: {}", port_name);
- return ResultNotFound;
- }
+ SetReg32(system, 0, Convert<uint32_t>(ret));
+}
- // Reserve a handle for the port.
- // NOTE: Nintendo really does write directly to the output handle here.
- R_TRY(handle_table.Reserve(out));
- auto handle_guard = SCOPE_GUARD({ handle_table.Unreserve(*out); });
+static void SvcWrap_UnmapSharedMemory64From32(Core::System& system) {
+ Result ret{};
- // Create a session.
- KClientSession* session{};
- R_TRY(port->CreateSession(std::addressof(session)));
+ Handle shmem_handle{};
+ uint32_t address{};
+ uint32_t size{};
- kernel.RegisterNamedServiceHandler(port_name, &port->GetParent()->GetServerPort());
+ shmem_handle = Convert<Handle>(GetReg32(system, 0));
+ address = Convert<uint32_t>(GetReg32(system, 1));
+ size = Convert<uint32_t>(GetReg32(system, 2));
- // Register the session in the table, close the extra reference.
- handle_table.Register(*out, session);
- session->Close();
+ ret = UnmapSharedMemory64From32(system, shmem_handle, address, size);
- // We succeeded.
- handle_guard.Cancel();
- return ResultSuccess;
+ SetReg32(system, 0, Convert<uint32_t>(ret));
}
-static Result ConnectToNamedPort32(Core::System& system, Handle* out_handle,
- u32 port_name_address) {
+static void SvcWrap_CreateTransferMemory64From32(Core::System& system) {
+ Result ret{};
+
+ Handle out_handle{};
+ uint32_t address{};
+ uint32_t size{};
+ MemoryPermission map_perm{};
+
+ address = Convert<uint32_t>(GetReg32(system, 1));
+ size = Convert<uint32_t>(GetReg32(system, 2));
+ map_perm = Convert<MemoryPermission>(GetReg32(system, 3));
+
+ ret = CreateTransferMemory64From32(system, &out_handle, address, size, map_perm);
- return ConnectToNamedPort(system, out_handle, port_name_address);
+ SetReg32(system, 0, Convert<uint32_t>(ret));
+ SetReg32(system, 1, Convert<uint32_t>(out_handle));
}
-/// Makes a blocking IPC call to a service.
-static Result SendSyncRequest(Core::System& system, Handle handle) {
- auto& kernel = system.Kernel();
+static void SvcWrap_CloseHandle64From32(Core::System& system) {
+ Result ret{};
- // Create the wait queue.
- KThreadQueue wait_queue(kernel);
+ Handle handle{};
- // Get the client session from its handle.
- KScopedAutoObject session =
- kernel.CurrentProcess()->GetHandleTable().GetObject<KClientSession>(handle);
- R_UNLESS(session.IsNotNull(), ResultInvalidHandle);
+ handle = Convert<Handle>(GetReg32(system, 0));
- LOG_TRACE(Kernel_SVC, "called handle=0x{:08X}({})", handle, session->GetName());
+ ret = CloseHandle64From32(system, handle);
- return session->SendSyncRequest();
+ SetReg32(system, 0, Convert<uint32_t>(ret));
}
-static Result SendSyncRequest32(Core::System& system, Handle handle) {
- return SendSyncRequest(system, handle);
+static void SvcWrap_ResetSignal64From32(Core::System& system) {
+ Result ret{};
+
+ Handle handle{};
+
+ handle = Convert<Handle>(GetReg32(system, 0));
+
+ ret = ResetSignal64From32(system, handle);
+
+ SetReg32(system, 0, Convert<uint32_t>(ret));
}
-static Result ReplyAndReceive(Core::System& system, s32* out_index, Handle* handles,
- s32 num_handles, Handle reply_target, s64 timeout_ns) {
- auto& kernel = system.Kernel();
- auto& handle_table = GetCurrentThread(kernel).GetOwnerProcess()->GetHandleTable();
-
- // Convert handle list to object table.
- std::vector<KSynchronizationObject*> objs(num_handles);
- R_UNLESS(
- handle_table.GetMultipleObjects<KSynchronizationObject>(objs.data(), handles, num_handles),
- ResultInvalidHandle);
-
- // Ensure handles are closed when we're done.
- SCOPE_EXIT({
- for (auto i = 0; i < num_handles; ++i) {
- objs[i]->Close();
- }
- });
-
- // Reply to the target, if one is specified.
- if (reply_target != InvalidHandle) {
- KScopedAutoObject session = handle_table.GetObject<KServerSession>(reply_target);
- R_UNLESS(session.IsNotNull(), ResultInvalidHandle);
-
- // If we fail to reply, we want to set the output index to -1.
- // ON_RESULT_FAILURE { *out_index = -1; };
-
- // Send the reply.
- // R_TRY(session->SendReply());
-
- Result rc = session->SendReply();
- if (!R_SUCCEEDED(rc)) {
- *out_index = -1;
- return rc;
- }
- }
+static void SvcWrap_WaitSynchronization64From32(Core::System& system) {
+ Result ret{};
- // Wait for a message.
- while (true) {
- // Wait for an object.
- s32 index;
- Result result = KSynchronizationObject::Wait(kernel, &index, objs.data(),
- static_cast<s32>(objs.size()), timeout_ns);
- if (result == ResultTimedOut) {
- return result;
- }
-
- // Receive the request.
- if (R_SUCCEEDED(result)) {
- KServerSession* session = objs[index]->DynamicCast<KServerSession*>();
- if (session != nullptr) {
- result = session->ReceiveRequest();
- if (result == ResultNotFound) {
- continue;
- }
- }
- }
-
- *out_index = index;
- return result;
- }
+ int32_t out_index{};
+ uint32_t handles{};
+ int32_t num_handles{};
+ int64_t timeout_ns{};
+
+ handles = Convert<uint32_t>(GetReg32(system, 1));
+ num_handles = Convert<int32_t>(GetReg32(system, 2));
+ std::array<uint32_t, 2> timeout_ns_gather{};
+ timeout_ns_gather[0] = GetReg32(system, 0);
+ timeout_ns_gather[1] = GetReg32(system, 3);
+ timeout_ns = Convert<int64_t>(timeout_ns_gather);
+
+ ret = WaitSynchronization64From32(system, &out_index, handles, num_handles, timeout_ns);
+
+ SetReg32(system, 0, Convert<uint32_t>(ret));
+ SetReg32(system, 1, Convert<uint32_t>(out_index));
}
-/// Get the ID for the specified thread.
-static Result GetThreadId(Core::System& system, u64* out_thread_id, Handle thread_handle) {
- // Get the thread from its handle.
- KScopedAutoObject thread =
- system.Kernel().CurrentProcess()->GetHandleTable().GetObject<KThread>(thread_handle);
- R_UNLESS(thread.IsNotNull(), ResultInvalidHandle);
+static void SvcWrap_CancelSynchronization64From32(Core::System& system) {
+ Result ret{};
- // Get the thread's id.
- *out_thread_id = thread->GetId();
- return ResultSuccess;
+ Handle handle{};
+
+ handle = Convert<Handle>(GetReg32(system, 0));
+
+ ret = CancelSynchronization64From32(system, handle);
+
+ SetReg32(system, 0, Convert<uint32_t>(ret));
}
-static Result GetThreadId32(Core::System& system, u32* out_thread_id_low, u32* out_thread_id_high,
- Handle thread_handle) {
- u64 out_thread_id{};
- const Result result{GetThreadId(system, &out_thread_id, thread_handle)};
+static void SvcWrap_ArbitrateLock64From32(Core::System& system) {
+ Result ret{};
+
+ Handle thread_handle{};
+ uint32_t address{};
+ uint32_t tag{};
- *out_thread_id_low = static_cast<u32>(out_thread_id >> 32);
- *out_thread_id_high = static_cast<u32>(out_thread_id & std::numeric_limits<u32>::max());
+ thread_handle = Convert<Handle>(GetReg32(system, 0));
+ address = Convert<uint32_t>(GetReg32(system, 1));
+ tag = Convert<uint32_t>(GetReg32(system, 2));
- return result;
+ ret = ArbitrateLock64From32(system, thread_handle, address, tag);
+
+ SetReg32(system, 0, Convert<uint32_t>(ret));
}
-/// Gets the ID of the specified process or a specified thread's owning process.
-static Result GetProcessId(Core::System& system, u64* out_process_id, Handle handle) {
- LOG_DEBUG(Kernel_SVC, "called handle=0x{:08X}", handle);
+static void SvcWrap_ArbitrateUnlock64From32(Core::System& system) {
+ Result ret{};
- // Get the object from the handle table.
- KScopedAutoObject obj =
- system.Kernel().CurrentProcess()->GetHandleTable().GetObject<KAutoObject>(
- static_cast<Handle>(handle));
- R_UNLESS(obj.IsNotNull(), ResultInvalidHandle);
+ uint32_t address{};
- // Get the process from the object.
- KProcess* process = nullptr;
- if (KProcess* p = obj->DynamicCast<KProcess*>(); p != nullptr) {
- // The object is a process, so we can use it directly.
- process = p;
- } else if (KThread* t = obj->DynamicCast<KThread*>(); t != nullptr) {
- // The object is a thread, so we want to use its parent.
- process = reinterpret_cast<KThread*>(obj.GetPointerUnsafe())->GetOwnerProcess();
- } else {
- // TODO(bunnei): This should also handle debug objects before returning.
- UNIMPLEMENTED_MSG("Debug objects not implemented");
- }
+ address = Convert<uint32_t>(GetReg32(system, 0));
- // Make sure the target process exists.
- R_UNLESS(process != nullptr, ResultInvalidHandle);
+ ret = ArbitrateUnlock64From32(system, address);
- // Get the process id.
- *out_process_id = process->GetId();
+ SetReg32(system, 0, Convert<uint32_t>(ret));
+}
+
+static void SvcWrap_WaitProcessWideKeyAtomic64From32(Core::System& system) {
+ Result ret{};
+
+ uint32_t address{};
+ uint32_t cv_key{};
+ uint32_t tag{};
+ int64_t timeout_ns{};
+
+ address = Convert<uint32_t>(GetReg32(system, 0));
+ cv_key = Convert<uint32_t>(GetReg32(system, 1));
+ tag = Convert<uint32_t>(GetReg32(system, 2));
+ std::array<uint32_t, 2> timeout_ns_gather{};
+ timeout_ns_gather[0] = GetReg32(system, 3);
+ timeout_ns_gather[1] = GetReg32(system, 4);
+ timeout_ns = Convert<int64_t>(timeout_ns_gather);
- return ResultSuccess;
+ ret = WaitProcessWideKeyAtomic64From32(system, address, cv_key, tag, timeout_ns);
+
+ SetReg32(system, 0, Convert<uint32_t>(ret));
}
-static Result GetProcessId32(Core::System& system, u32* out_process_id_low,
- u32* out_process_id_high, Handle handle) {
- u64 out_process_id{};
- const auto result = GetProcessId(system, &out_process_id, handle);
- *out_process_id_low = static_cast<u32>(out_process_id);
- *out_process_id_high = static_cast<u32>(out_process_id >> 32);
- return result;
+static void SvcWrap_SignalProcessWideKey64From32(Core::System& system) {
+ uint32_t cv_key{};
+ int32_t count{};
+
+ cv_key = Convert<uint32_t>(GetReg32(system, 0));
+ count = Convert<int32_t>(GetReg32(system, 1));
+
+ SignalProcessWideKey64From32(system, cv_key, count);
}
-/// Wait for the given handles to synchronize, timeout after the specified nanoseconds
-static Result WaitSynchronization(Core::System& system, s32* index, VAddr handles_address,
- s32 num_handles, s64 nano_seconds) {
- LOG_TRACE(Kernel_SVC, "called handles_address=0x{:X}, num_handles={}, nano_seconds={}",
- handles_address, num_handles, nano_seconds);
+static void SvcWrap_GetSystemTick64From32(Core::System& system) {
+ int64_t ret{};
- // Ensure number of handles is valid.
- R_UNLESS(0 <= num_handles && num_handles <= ArgumentHandleCountMax, ResultOutOfRange);
+ ret = GetSystemTick64From32(system);
- auto& kernel = system.Kernel();
- std::vector<KSynchronizationObject*> objs(num_handles);
- const auto& handle_table = kernel.CurrentProcess()->GetHandleTable();
- Handle* handles = system.Memory().GetPointer<Handle>(handles_address);
-
- // Copy user handles.
- if (num_handles > 0) {
- // Convert the handles to objects.
- R_UNLESS(handle_table.GetMultipleObjects<KSynchronizationObject>(objs.data(), handles,
- num_handles),
- ResultInvalidHandle);
- for (const auto& obj : objs) {
- kernel.RegisterInUseObject(obj);
- }
- }
+ auto ret_scatter = Convert<std::array<uint32_t, 2>>(ret);
+ SetReg32(system, 0, ret_scatter[0]);
+ SetReg32(system, 1, ret_scatter[1]);
+}
+
+static void SvcWrap_ConnectToNamedPort64From32(Core::System& system) {
+ Result ret{};
+
+ Handle out_handle{};
+ uint32_t name{};
- // Ensure handles are closed when we're done.
- SCOPE_EXIT({
- for (s32 i = 0; i < num_handles; ++i) {
- kernel.UnregisterInUseObject(objs[i]);
- objs[i]->Close();
- }
- });
+ name = Convert<uint32_t>(GetReg32(system, 1));
- return KSynchronizationObject::Wait(kernel, index, objs.data(), static_cast<s32>(objs.size()),
- nano_seconds);
+ ret = ConnectToNamedPort64From32(system, &out_handle, name);
+
+ SetReg32(system, 0, Convert<uint32_t>(ret));
+ SetReg32(system, 1, Convert<uint32_t>(out_handle));
}
-static Result WaitSynchronization32(Core::System& system, u32 timeout_low, u32 handles_address,
- s32 num_handles, u32 timeout_high, s32* index) {
- const s64 nano_seconds{(static_cast<s64>(timeout_high) << 32) | static_cast<s64>(timeout_low)};
- return WaitSynchronization(system, index, handles_address, num_handles, nano_seconds);
+static void SvcWrap_SendSyncRequest64From32(Core::System& system) {
+ Result ret{};
+
+ Handle session_handle{};
+
+ session_handle = Convert<Handle>(GetReg32(system, 0));
+
+ ret = SendSyncRequest64From32(system, session_handle);
+
+ SetReg32(system, 0, Convert<uint32_t>(ret));
}
-/// Resumes a thread waiting on WaitSynchronization
-static Result CancelSynchronization(Core::System& system, Handle handle) {
- LOG_TRACE(Kernel_SVC, "called handle=0x{:X}", handle);
+static void SvcWrap_SendSyncRequestWithUserBuffer64From32(Core::System& system) {
+ Result ret{};
+
+ uint32_t message_buffer{};
+ uint32_t message_buffer_size{};
+ Handle session_handle{};
+
+ message_buffer = Convert<uint32_t>(GetReg32(system, 0));
+ message_buffer_size = Convert<uint32_t>(GetReg32(system, 1));
+ session_handle = Convert<Handle>(GetReg32(system, 2));
- // Get the thread from its handle.
- KScopedAutoObject thread =
- system.Kernel().CurrentProcess()->GetHandleTable().GetObject<KThread>(handle);
- R_UNLESS(thread.IsNotNull(), ResultInvalidHandle);
+ ret = SendSyncRequestWithUserBuffer64From32(system, message_buffer, message_buffer_size, session_handle);
- // Cancel the thread's wait.
- thread->WaitCancel();
- return ResultSuccess;
+ SetReg32(system, 0, Convert<uint32_t>(ret));
}
-static Result CancelSynchronization32(Core::System& system, Handle handle) {
- return CancelSynchronization(system, handle);
+static void SvcWrap_SendAsyncRequestWithUserBuffer64From32(Core::System& system) {
+ Result ret{};
+
+ Handle out_event_handle{};
+ uint32_t message_buffer{};
+ uint32_t message_buffer_size{};
+ Handle session_handle{};
+
+ message_buffer = Convert<uint32_t>(GetReg32(system, 1));
+ message_buffer_size = Convert<uint32_t>(GetReg32(system, 2));
+ session_handle = Convert<Handle>(GetReg32(system, 3));
+
+ ret = SendAsyncRequestWithUserBuffer64From32(system, &out_event_handle, message_buffer, message_buffer_size, session_handle);
+
+ SetReg32(system, 0, Convert<uint32_t>(ret));
+ SetReg32(system, 1, Convert<uint32_t>(out_event_handle));
}
-/// Attempts to locks a mutex
-static Result ArbitrateLock(Core::System& system, Handle thread_handle, VAddr address, u32 tag) {
- LOG_TRACE(Kernel_SVC, "called thread_handle=0x{:08X}, address=0x{:X}, tag=0x{:08X}",
- thread_handle, address, tag);
+static void SvcWrap_GetProcessId64From32(Core::System& system) {
+ Result ret{};
- // Validate the input address.
- if (IsKernelAddress(address)) {
- LOG_ERROR(Kernel_SVC, "Attempting to arbitrate a lock on a kernel address (address={:08X})",
- address);
- return ResultInvalidCurrentMemory;
- }
- if (!Common::IsAligned(address, sizeof(u32))) {
- LOG_ERROR(Kernel_SVC, "Input address must be 4 byte aligned (address: {:08X})", address);
- return ResultInvalidAddress;
- }
+ uint64_t out_process_id{};
+ Handle process_handle{};
+
+ process_handle = Convert<Handle>(GetReg32(system, 1));
+
+ ret = GetProcessId64From32(system, &out_process_id, process_handle);
- return system.Kernel().CurrentProcess()->WaitForAddress(thread_handle, address, tag);
+ SetReg32(system, 0, Convert<uint32_t>(ret));
+ auto out_process_id_scatter = Convert<std::array<uint32_t, 2>>(out_process_id);
+ SetReg32(system, 1, out_process_id_scatter[0]);
+ SetReg32(system, 2, out_process_id_scatter[1]);
}
-static Result ArbitrateLock32(Core::System& system, Handle thread_handle, u32 address, u32 tag) {
- return ArbitrateLock(system, thread_handle, address, tag);
+static void SvcWrap_GetThreadId64From32(Core::System& system) {
+ Result ret{};
+
+ uint64_t out_thread_id{};
+ Handle thread_handle{};
+
+ thread_handle = Convert<Handle>(GetReg32(system, 1));
+
+ ret = GetThreadId64From32(system, &out_thread_id, thread_handle);
+
+ SetReg32(system, 0, Convert<uint32_t>(ret));
+ auto out_thread_id_scatter = Convert<std::array<uint32_t, 2>>(out_thread_id);
+ SetReg32(system, 1, out_thread_id_scatter[0]);
+ SetReg32(system, 2, out_thread_id_scatter[1]);
}
-/// Unlock a mutex
-static Result ArbitrateUnlock(Core::System& system, VAddr address) {
- LOG_TRACE(Kernel_SVC, "called address=0x{:X}", address);
+static void SvcWrap_Break64From32(Core::System& system) {
+ BreakReason break_reason{};
+ uint32_t arg{};
+ uint32_t size{};
- // Validate the input address.
- if (IsKernelAddress(address)) {
- LOG_ERROR(Kernel_SVC,
- "Attempting to arbitrate an unlock on a kernel address (address={:08X})",
- address);
- return ResultInvalidCurrentMemory;
- }
- if (!Common::IsAligned(address, sizeof(u32))) {
- LOG_ERROR(Kernel_SVC, "Input address must be 4 byte aligned (address: {:08X})", address);
- return ResultInvalidAddress;
- }
+ break_reason = Convert<BreakReason>(GetReg32(system, 0));
+ arg = Convert<uint32_t>(GetReg32(system, 1));
+ size = Convert<uint32_t>(GetReg32(system, 2));
- return system.Kernel().CurrentProcess()->SignalToAddress(address);
-}
-
-static Result ArbitrateUnlock32(Core::System& system, u32 address) {
- return ArbitrateUnlock(system, address);
-}
-
-/// Break program execution
-static void Break(Core::System& system, u32 reason, u64 info1, u64 info2) {
- BreakReason break_reason =
- static_cast<BreakReason>(reason & ~static_cast<u32>(BreakReason::NotificationOnlyFlag));
- bool notification_only = (reason & static_cast<u32>(BreakReason::NotificationOnlyFlag)) != 0;
-
- bool has_dumped_buffer{};
- std::vector<u8> debug_buffer;
-
- const auto handle_debug_buffer = [&](VAddr addr, u64 sz) {
- if (sz == 0 || addr == 0 || has_dumped_buffer) {
- return;
- }
-
- auto& memory = system.Memory();
-
- // This typically is an error code so we're going to assume this is the case
- if (sz == sizeof(u32)) {
- LOG_CRITICAL(Debug_Emulated, "debug_buffer_err_code={:X}", memory.Read32(addr));
- } else {
- // We don't know what's in here so we'll hexdump it
- debug_buffer.resize(sz);
- memory.ReadBlock(addr, debug_buffer.data(), sz);
- std::string hexdump;
- for (std::size_t i = 0; i < debug_buffer.size(); i++) {
- hexdump += fmt::format("{:02X} ", debug_buffer[i]);
- if (i != 0 && i % 16 == 0) {
- hexdump += '\n';
- }
- }
- LOG_CRITICAL(Debug_Emulated, "debug_buffer=\n{}", hexdump);
- }
- has_dumped_buffer = true;
- };
- switch (break_reason) {
- case BreakReason::Panic:
- LOG_CRITICAL(Debug_Emulated, "Userspace PANIC! info1=0x{:016X}, info2=0x{:016X}", info1,
- info2);
- handle_debug_buffer(info1, info2);
- break;
- case BreakReason::Assert:
- LOG_CRITICAL(Debug_Emulated, "Userspace Assertion failed! info1=0x{:016X}, info2=0x{:016X}",
- info1, info2);
- handle_debug_buffer(info1, info2);
- break;
- case BreakReason::User:
- LOG_WARNING(Debug_Emulated, "Userspace Break! 0x{:016X} with size 0x{:016X}", info1, info2);
- handle_debug_buffer(info1, info2);
- break;
- case BreakReason::PreLoadDll:
- LOG_INFO(Debug_Emulated,
- "Userspace Attempting to load an NRO at 0x{:016X} with size 0x{:016X}", info1,
- info2);
- break;
- case BreakReason::PostLoadDll:
- LOG_INFO(Debug_Emulated, "Userspace Loaded an NRO at 0x{:016X} with size 0x{:016X}", info1,
- info2);
- break;
- case BreakReason::PreUnloadDll:
- LOG_INFO(Debug_Emulated,
- "Userspace Attempting to unload an NRO at 0x{:016X} with size 0x{:016X}", info1,
- info2);
- break;
- case BreakReason::PostUnloadDll:
- LOG_INFO(Debug_Emulated, "Userspace Unloaded an NRO at 0x{:016X} with size 0x{:016X}",
- info1, info2);
- break;
- case BreakReason::CppException:
- LOG_CRITICAL(Debug_Emulated, "Signalling debugger. Uncaught C++ exception encountered.");
- break;
- default:
- LOG_WARNING(
- Debug_Emulated,
- "Signalling debugger, Unknown break reason {:#X}, info1=0x{:016X}, info2=0x{:016X}",
- reason, info1, info2);
- handle_debug_buffer(info1, info2);
- break;
- }
+ Break64From32(system, break_reason, arg, size);
+}
- system.GetReporter().SaveSvcBreakReport(reason, notification_only, info1, info2,
- has_dumped_buffer ? std::make_optional(debug_buffer)
- : std::nullopt);
+static void SvcWrap_OutputDebugString64From32(Core::System& system) {
+ Result ret{};
- if (!notification_only) {
- LOG_CRITICAL(
- Debug_Emulated,
- "Emulated program broke execution! reason=0x{:016X}, info1=0x{:016X}, info2=0x{:016X}",
- reason, info1, info2);
+ uint32_t debug_str{};
+ uint32_t len{};
- handle_debug_buffer(info1, info2);
+ debug_str = Convert<uint32_t>(GetReg32(system, 0));
+ len = Convert<uint32_t>(GetReg32(system, 1));
- auto* const current_thread = GetCurrentThreadPointer(system.Kernel());
- const auto thread_processor_id = current_thread->GetActiveCore();
- system.ArmInterface(static_cast<std::size_t>(thread_processor_id)).LogBacktrace();
- }
+ ret = OutputDebugString64From32(system, debug_str, len);
- if (system.DebuggerEnabled()) {
- auto* thread = system.Kernel().GetCurrentEmuThread();
- system.GetDebugger().NotifyThreadStopped(thread);
- thread->RequestSuspend(Kernel::SuspendType::Debug);
- }
+ SetReg32(system, 0, Convert<uint32_t>(ret));
}
-static void Break32(Core::System& system, u32 reason, u32 info1, u32 info2) {
- Break(system, reason, info1, info2);
+static void SvcWrap_ReturnFromException64From32(Core::System& system) {
+ Result result{};
+
+ result = Convert<Result>(GetReg32(system, 0));
+
+ ReturnFromException64From32(system, result);
}
-/// Used to output a message on a debug hardware unit - does nothing on a retail unit
-static void OutputDebugString(Core::System& system, VAddr address, u64 len) {
- if (len == 0) {
- return;
- }
+static void SvcWrap_GetInfo64From32(Core::System& system) {
+ Result ret{};
- std::string str(len, '\0');
- system.Memory().ReadBlock(address, str.data(), str.size());
- LOG_DEBUG(Debug_Emulated, "{}", str);
-}
-
-static void OutputDebugString32(Core::System& system, u32 address, u32 len) {
- OutputDebugString(system, address, len);
-}
-
-/// Gets system/memory information for the current process
-static Result GetInfo(Core::System& system, u64* result, u64 info_id, Handle handle,
- u64 info_sub_id) {
- LOG_TRACE(Kernel_SVC, "called info_id=0x{:X}, info_sub_id=0x{:X}, handle=0x{:08X}", info_id,
- info_sub_id, handle);
-
- const auto info_id_type = static_cast<InfoType>(info_id);
-
- switch (info_id_type) {
- case InfoType::CoreMask:
- case InfoType::PriorityMask:
- case InfoType::AliasRegionAddress:
- case InfoType::AliasRegionSize:
- case InfoType::HeapRegionAddress:
- case InfoType::HeapRegionSize:
- case InfoType::AslrRegionAddress:
- case InfoType::AslrRegionSize:
- case InfoType::StackRegionAddress:
- case InfoType::StackRegionSize:
- case InfoType::TotalMemorySize:
- case InfoType::UsedMemorySize:
- case InfoType::SystemResourceSizeTotal:
- case InfoType::SystemResourceSizeUsed:
- case InfoType::ProgramId:
- case InfoType::UserExceptionContextAddress:
- case InfoType::TotalNonSystemMemorySize:
- case InfoType::UsedNonSystemMemorySize:
- case InfoType::IsApplication:
- case InfoType::FreeThreadCount: {
- if (info_sub_id != 0) {
- LOG_ERROR(Kernel_SVC, "Info sub id is non zero! info_id={}, info_sub_id={}", info_id,
- info_sub_id);
- return ResultInvalidEnumValue;
- }
-
- const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable();
- KScopedAutoObject process = handle_table.GetObject<KProcess>(handle);
- if (process.IsNull()) {
- LOG_ERROR(Kernel_SVC, "Process is not valid! info_id={}, info_sub_id={}, handle={:08X}",
- info_id, info_sub_id, handle);
- return ResultInvalidHandle;
- }
-
- switch (info_id_type) {
- case InfoType::CoreMask:
- *result = process->GetCoreMask();
- return ResultSuccess;
-
- case InfoType::PriorityMask:
- *result = process->GetPriorityMask();
- return ResultSuccess;
-
- case InfoType::AliasRegionAddress:
- *result = process->PageTable().GetAliasRegionStart();
- return ResultSuccess;
-
- case InfoType::AliasRegionSize:
- *result = process->PageTable().GetAliasRegionSize();
- return ResultSuccess;
-
- case InfoType::HeapRegionAddress:
- *result = process->PageTable().GetHeapRegionStart();
- return ResultSuccess;
-
- case InfoType::HeapRegionSize:
- *result = process->PageTable().GetHeapRegionSize();
- return ResultSuccess;
-
- case InfoType::AslrRegionAddress:
- *result = process->PageTable().GetAliasCodeRegionStart();
- return ResultSuccess;
-
- case InfoType::AslrRegionSize:
- *result = process->PageTable().GetAliasCodeRegionSize();
- return ResultSuccess;
-
- case InfoType::StackRegionAddress:
- *result = process->PageTable().GetStackRegionStart();
- return ResultSuccess;
-
- case InfoType::StackRegionSize:
- *result = process->PageTable().GetStackRegionSize();
- return ResultSuccess;
-
- case InfoType::TotalMemorySize:
- *result = process->GetTotalPhysicalMemoryAvailable();
- return ResultSuccess;
-
- case InfoType::UsedMemorySize:
- *result = process->GetTotalPhysicalMemoryUsed();
- return ResultSuccess;
-
- case InfoType::SystemResourceSizeTotal:
- *result = process->GetSystemResourceSize();
- return ResultSuccess;
-
- case InfoType::SystemResourceSizeUsed:
- LOG_WARNING(Kernel_SVC, "(STUBBED) Attempted to query system resource usage");
- *result = process->GetSystemResourceUsage();
- return ResultSuccess;
-
- case InfoType::ProgramId:
- *result = process->GetProgramID();
- return ResultSuccess;
-
- case InfoType::UserExceptionContextAddress:
- *result = process->GetProcessLocalRegionAddress();
- return ResultSuccess;
-
- case InfoType::TotalNonSystemMemorySize:
- *result = process->GetTotalPhysicalMemoryAvailableWithoutSystemResource();
- return ResultSuccess;
-
- case InfoType::UsedNonSystemMemorySize:
- *result = process->GetTotalPhysicalMemoryUsedWithoutSystemResource();
- return ResultSuccess;
-
- case InfoType::FreeThreadCount:
- *result = process->GetFreeThreadCount();
- return ResultSuccess;
-
- default:
- break;
- }
-
- LOG_ERROR(Kernel_SVC, "Unimplemented svcGetInfo id=0x{:016X}", info_id);
- return ResultInvalidEnumValue;
- }
+ uint64_t out{};
+ InfoType info_type{};
+ Handle handle{};
+ uint64_t info_subtype{};
- case InfoType::DebuggerAttached:
- *result = 0;
- return ResultSuccess;
-
- case InfoType::ResourceLimit: {
- if (handle != 0) {
- LOG_ERROR(Kernel, "Handle is non zero! handle={:08X}", handle);
- return ResultInvalidHandle;
- }
-
- if (info_sub_id != 0) {
- LOG_ERROR(Kernel, "Info sub id is non zero! info_id={}, info_sub_id={}", info_id,
- info_sub_id);
- return ResultInvalidCombination;
- }
-
- KProcess* const current_process = system.Kernel().CurrentProcess();
- KHandleTable& handle_table = current_process->GetHandleTable();
- const auto resource_limit = current_process->GetResourceLimit();
- if (!resource_limit) {
- *result = Svc::InvalidHandle;
- // Yes, the kernel considers this a successful operation.
- return ResultSuccess;
- }
-
- Handle resource_handle{};
- R_TRY(handle_table.Add(&resource_handle, resource_limit));
-
- *result = resource_handle;
- return ResultSuccess;
- }
+ info_type = Convert<InfoType>(GetReg32(system, 1));
+ handle = Convert<Handle>(GetReg32(system, 2));
+ std::array<uint32_t, 2> info_subtype_gather{};
+ info_subtype_gather[0] = GetReg32(system, 0);
+ info_subtype_gather[1] = GetReg32(system, 3);
+ info_subtype = Convert<uint64_t>(info_subtype_gather);
- case InfoType::RandomEntropy:
- if (handle != 0) {
- LOG_ERROR(Kernel_SVC, "Process Handle is non zero, expected 0 result but got {:016X}",
- handle);
- return ResultInvalidHandle;
- }
-
- if (info_sub_id >= KProcess::RANDOM_ENTROPY_SIZE) {
- LOG_ERROR(Kernel_SVC, "Entropy size is out of range, expected {} but got {}",
- KProcess::RANDOM_ENTROPY_SIZE, info_sub_id);
- return ResultInvalidCombination;
- }
-
- *result = system.Kernel().CurrentProcess()->GetRandomEntropy(info_sub_id);
- return ResultSuccess;
-
- case InfoType::InitialProcessIdRange:
- LOG_WARNING(Kernel_SVC,
- "(STUBBED) Attempted to query privileged process id bounds, returned 0");
- *result = 0;
- return ResultSuccess;
-
- case InfoType::ThreadTickCount: {
- constexpr u64 num_cpus = 4;
- if (info_sub_id != 0xFFFFFFFFFFFFFFFF && info_sub_id >= num_cpus) {
- LOG_ERROR(Kernel_SVC, "Core count is out of range, expected {} but got {}", num_cpus,
- info_sub_id);
- return ResultInvalidCombination;
- }
-
- KScopedAutoObject thread =
- system.Kernel().CurrentProcess()->GetHandleTable().GetObject<KThread>(
- static_cast<Handle>(handle));
- if (thread.IsNull()) {
- LOG_ERROR(Kernel_SVC, "Thread handle does not exist, handle=0x{:08X}",
- static_cast<Handle>(handle));
- return ResultInvalidHandle;
- }
-
- const auto& core_timing = system.CoreTiming();
- const auto& scheduler = *system.Kernel().CurrentScheduler();
- const auto* const current_thread = GetCurrentThreadPointer(system.Kernel());
- const bool same_thread = current_thread == thread.GetPointerUnsafe();
-
- const u64 prev_ctx_ticks = scheduler.GetLastContextSwitchTime();
- u64 out_ticks = 0;
- if (same_thread && info_sub_id == 0xFFFFFFFFFFFFFFFF) {
- const u64 thread_ticks = current_thread->GetCpuTime();
-
- out_ticks = thread_ticks + (core_timing.GetCPUTicks() - prev_ctx_ticks);
- } else if (same_thread && info_sub_id == system.Kernel().CurrentPhysicalCoreIndex()) {
- out_ticks = core_timing.GetCPUTicks() - prev_ctx_ticks;
- }
-
- *result = out_ticks;
- return ResultSuccess;
- }
- case InfoType::IdleTickCount: {
- // Verify the input handle is invalid.
- R_UNLESS(handle == InvalidHandle, ResultInvalidHandle);
-
- // Verify the requested core is valid.
- const bool core_valid =
- (info_sub_id == 0xFFFFFFFFFFFFFFFF) ||
- (info_sub_id == static_cast<u64>(system.Kernel().CurrentPhysicalCoreIndex()));
- R_UNLESS(core_valid, ResultInvalidCombination);
-
- // Get the idle tick count.
- *result = system.Kernel().CurrentScheduler()->GetIdleThread()->GetCpuTime();
- return ResultSuccess;
- }
- case InfoType::MesosphereCurrentProcess: {
- // Verify the input handle is invalid.
- R_UNLESS(handle == InvalidHandle, ResultInvalidHandle);
+ ret = GetInfo64From32(system, &out, info_type, handle, info_subtype);
- // Verify the sub-type is valid.
- R_UNLESS(info_sub_id == 0, ResultInvalidCombination);
+ SetReg32(system, 0, Convert<uint32_t>(ret));
+ auto out_scatter = Convert<std::array<uint32_t, 2>>(out);
+ SetReg32(system, 1, out_scatter[0]);
+ SetReg32(system, 2, out_scatter[1]);
+}
- // Get the handle table.
- KProcess* current_process = system.Kernel().CurrentProcess();
- KHandleTable& handle_table = current_process->GetHandleTable();
+static void SvcWrap_FlushEntireDataCache64From32(Core::System& system) {
+ FlushEntireDataCache64From32(system);
+}
- // Get a new handle for the current process.
- Handle tmp;
- R_TRY(handle_table.Add(&tmp, current_process));
+static void SvcWrap_FlushDataCache64From32(Core::System& system) {
+ Result ret{};
- // Set the output.
- *result = tmp;
+ uint32_t address{};
+ uint32_t size{};
- // We succeeded.
- return ResultSuccess;
- }
- default:
- LOG_ERROR(Kernel_SVC, "Unimplemented svcGetInfo id=0x{:016X}", info_id);
- return ResultInvalidEnumValue;
- }
+ address = Convert<uint32_t>(GetReg32(system, 0));
+ size = Convert<uint32_t>(GetReg32(system, 1));
+
+ ret = FlushDataCache64From32(system, address, size);
+
+ SetReg32(system, 0, Convert<uint32_t>(ret));
}
-static Result GetInfo32(Core::System& system, u32* result_low, u32* result_high, u32 sub_id_low,
- u32 info_id, u32 handle, u32 sub_id_high) {
- const u64 sub_id{u64{sub_id_low} | (u64{sub_id_high} << 32)};
- u64 res_value{};
+static void SvcWrap_MapPhysicalMemory64From32(Core::System& system) {
+ Result ret{};
+
+ uint32_t address{};
+ uint32_t size{};
- const Result result{GetInfo(system, &res_value, info_id, handle, sub_id)};
- *result_high = static_cast<u32>(res_value >> 32);
- *result_low = static_cast<u32>(res_value & std::numeric_limits<u32>::max());
+ address = Convert<uint32_t>(GetReg32(system, 0));
+ size = Convert<uint32_t>(GetReg32(system, 1));
- return result;
+ ret = MapPhysicalMemory64From32(system, address, size);
+
+ SetReg32(system, 0, Convert<uint32_t>(ret));
}
-/// Maps memory at a desired address
-static Result MapPhysicalMemory(Core::System& system, VAddr addr, u64 size) {
- LOG_DEBUG(Kernel_SVC, "called, addr=0x{:016X}, size=0x{:X}", addr, size);
+static void SvcWrap_UnmapPhysicalMemory64From32(Core::System& system) {
+ Result ret{};
- if (!Common::Is4KBAligned(addr)) {
- LOG_ERROR(Kernel_SVC, "Address is not aligned to 4KB, 0x{:016X}", addr);
- return ResultInvalidAddress;
- }
+ uint32_t address{};
+ uint32_t size{};
- if (!Common::Is4KBAligned(size)) {
- LOG_ERROR(Kernel_SVC, "Size is not aligned to 4KB, 0x{:X}", size);
- return ResultInvalidSize;
- }
+ address = Convert<uint32_t>(GetReg32(system, 0));
+ size = Convert<uint32_t>(GetReg32(system, 1));
- if (size == 0) {
- LOG_ERROR(Kernel_SVC, "Size is zero");
- return ResultInvalidSize;
- }
+ ret = UnmapPhysicalMemory64From32(system, address, size);
- if (!(addr < addr + size)) {
- LOG_ERROR(Kernel_SVC, "Size causes 64-bit overflow of address");
- return ResultInvalidMemoryRegion;
- }
+ SetReg32(system, 0, Convert<uint32_t>(ret));
+}
- KProcess* const current_process{system.Kernel().CurrentProcess()};
- auto& page_table{current_process->PageTable()};
+static void SvcWrap_GetDebugFutureThreadInfo64From32(Core::System& system) {
+ Result ret{};
- if (current_process->GetSystemResourceSize() == 0) {
- LOG_ERROR(Kernel_SVC, "System Resource Size is zero");
- return ResultInvalidState;
- }
+ ilp32::LastThreadContext out_context{};
+ uint64_t out_thread_id{};
+ Handle debug_handle{};
+ int64_t ns{};
- if (!page_table.IsInsideAddressSpace(addr, size)) {
- LOG_ERROR(Kernel_SVC,
- "Address is not within the address space, addr=0x{:016X}, size=0x{:016X}", addr,
- size);
- return ResultInvalidMemoryRegion;
- }
+ debug_handle = Convert<Handle>(GetReg32(system, 2));
+ std::array<uint32_t, 2> ns_gather{};
+ ns_gather[0] = GetReg32(system, 0);
+ ns_gather[1] = GetReg32(system, 1);
+ ns = Convert<int64_t>(ns_gather);
- if (page_table.IsOutsideAliasRegion(addr, size)) {
- LOG_ERROR(Kernel_SVC,
- "Address is not within the alias region, addr=0x{:016X}, size=0x{:016X}", addr,
- size);
- return ResultInvalidMemoryRegion;
- }
+ ret = GetDebugFutureThreadInfo64From32(system, &out_context, &out_thread_id, debug_handle, ns);
- return page_table.MapPhysicalMemory(addr, size);
+ SetReg32(system, 0, Convert<uint32_t>(ret));
+ auto out_context_scatter = Convert<std::array<uint32_t, 4>>(out_context);
+ SetReg32(system, 1, out_context_scatter[0]);
+ SetReg32(system, 2, out_context_scatter[1]);
+ SetReg32(system, 3, out_context_scatter[2]);
+ SetReg32(system, 4, out_context_scatter[3]);
+ auto out_thread_id_scatter = Convert<std::array<uint32_t, 2>>(out_thread_id);
+ SetReg32(system, 5, out_thread_id_scatter[0]);
+ SetReg32(system, 6, out_thread_id_scatter[1]);
}
-static Result MapPhysicalMemory32(Core::System& system, u32 addr, u32 size) {
- return MapPhysicalMemory(system, addr, size);
+static void SvcWrap_GetLastThreadInfo64From32(Core::System& system) {
+ Result ret{};
+
+ ilp32::LastThreadContext out_context{};
+ uintptr_t out_tls_address{};
+ uint32_t out_flags{};
+
+ ret = GetLastThreadInfo64From32(system, &out_context, &out_tls_address, &out_flags);
+
+ SetReg32(system, 0, Convert<uint32_t>(ret));
+ auto out_context_scatter = Convert<std::array<uint32_t, 4>>(out_context);
+ SetReg32(system, 1, out_context_scatter[0]);
+ SetReg32(system, 2, out_context_scatter[1]);
+ SetReg32(system, 3, out_context_scatter[2]);
+ SetReg32(system, 4, out_context_scatter[3]);
+ SetReg32(system, 5, Convert<uint32_t>(out_tls_address));
+ SetReg32(system, 6, Convert<uint32_t>(out_flags));
}
-/// Unmaps memory previously mapped via MapPhysicalMemory
-static Result UnmapPhysicalMemory(Core::System& system, VAddr addr, u64 size) {
- LOG_DEBUG(Kernel_SVC, "called, addr=0x{:016X}, size=0x{:X}", addr, size);
+static void SvcWrap_GetResourceLimitLimitValue64From32(Core::System& system) {
+ Result ret{};
- if (!Common::Is4KBAligned(addr)) {
- LOG_ERROR(Kernel_SVC, "Address is not aligned to 4KB, 0x{:016X}", addr);
- return ResultInvalidAddress;
- }
+ int64_t out_limit_value{};
+ Handle resource_limit_handle{};
+ LimitableResource which{};
- if (!Common::Is4KBAligned(size)) {
- LOG_ERROR(Kernel_SVC, "Size is not aligned to 4KB, 0x{:X}", size);
- return ResultInvalidSize;
- }
+ resource_limit_handle = Convert<Handle>(GetReg32(system, 1));
+ which = Convert<LimitableResource>(GetReg32(system, 2));
- if (size == 0) {
- LOG_ERROR(Kernel_SVC, "Size is zero");
- return ResultInvalidSize;
- }
+ ret = GetResourceLimitLimitValue64From32(system, &out_limit_value, resource_limit_handle, which);
- if (!(addr < addr + size)) {
- LOG_ERROR(Kernel_SVC, "Size causes 64-bit overflow of address");
- return ResultInvalidMemoryRegion;
- }
+ SetReg32(system, 0, Convert<uint32_t>(ret));
+ auto out_limit_value_scatter = Convert<std::array<uint32_t, 2>>(out_limit_value);
+ SetReg32(system, 1, out_limit_value_scatter[0]);
+ SetReg32(system, 2, out_limit_value_scatter[1]);
+}
- KProcess* const current_process{system.Kernel().CurrentProcess()};
- auto& page_table{current_process->PageTable()};
+static void SvcWrap_GetResourceLimitCurrentValue64From32(Core::System& system) {
+ Result ret{};
- if (current_process->GetSystemResourceSize() == 0) {
- LOG_ERROR(Kernel_SVC, "System Resource Size is zero");
- return ResultInvalidState;
- }
+ int64_t out_current_value{};
+ Handle resource_limit_handle{};
+ LimitableResource which{};
- if (!page_table.IsInsideAddressSpace(addr, size)) {
- LOG_ERROR(Kernel_SVC,
- "Address is not within the address space, addr=0x{:016X}, size=0x{:016X}", addr,
- size);
- return ResultInvalidMemoryRegion;
- }
+ resource_limit_handle = Convert<Handle>(GetReg32(system, 1));
+ which = Convert<LimitableResource>(GetReg32(system, 2));
- if (page_table.IsOutsideAliasRegion(addr, size)) {
- LOG_ERROR(Kernel_SVC,
- "Address is not within the alias region, addr=0x{:016X}, size=0x{:016X}", addr,
- size);
- return ResultInvalidMemoryRegion;
- }
+ ret = GetResourceLimitCurrentValue64From32(system, &out_current_value, resource_limit_handle, which);
- return page_table.UnmapPhysicalMemory(addr, size);
+ SetReg32(system, 0, Convert<uint32_t>(ret));
+ auto out_current_value_scatter = Convert<std::array<uint32_t, 2>>(out_current_value);
+ SetReg32(system, 1, out_current_value_scatter[0]);
+ SetReg32(system, 2, out_current_value_scatter[1]);
}
-static Result UnmapPhysicalMemory32(Core::System& system, u32 addr, u32 size) {
- return UnmapPhysicalMemory(system, addr, size);
+static void SvcWrap_SetThreadActivity64From32(Core::System& system) {
+ Result ret{};
+
+ Handle thread_handle{};
+ ThreadActivity thread_activity{};
+
+ thread_handle = Convert<Handle>(GetReg32(system, 0));
+ thread_activity = Convert<ThreadActivity>(GetReg32(system, 1));
+
+ ret = SetThreadActivity64From32(system, thread_handle, thread_activity);
+
+ SetReg32(system, 0, Convert<uint32_t>(ret));
+}
+
+static void SvcWrap_GetThreadContext364From32(Core::System& system) {
+ Result ret{};
+
+ uint32_t out_context{};
+ Handle thread_handle{};
+
+ out_context = Convert<uint32_t>(GetReg32(system, 0));
+ thread_handle = Convert<Handle>(GetReg32(system, 1));
+
+ ret = GetThreadContext364From32(system, out_context, thread_handle);
+
+ SetReg32(system, 0, Convert<uint32_t>(ret));
}
-/// Sets the thread activity
-static Result SetThreadActivity(Core::System& system, Handle thread_handle,
- ThreadActivity thread_activity) {
- LOG_DEBUG(Kernel_SVC, "called, handle=0x{:08X}, activity=0x{:08X}", thread_handle,
- thread_activity);
+static void SvcWrap_WaitForAddress64From32(Core::System& system) {
+ Result ret{};
+
+ uint32_t address{};
+ ArbitrationType arb_type{};
+ int32_t value{};
+ int64_t timeout_ns{};
+
+ address = Convert<uint32_t>(GetReg32(system, 0));
+ arb_type = Convert<ArbitrationType>(GetReg32(system, 1));
+ value = Convert<int32_t>(GetReg32(system, 2));
+ std::array<uint32_t, 2> timeout_ns_gather{};
+ timeout_ns_gather[0] = GetReg32(system, 3);
+ timeout_ns_gather[1] = GetReg32(system, 4);
+ timeout_ns = Convert<int64_t>(timeout_ns_gather);
- // Validate the activity.
- constexpr auto IsValidThreadActivity = [](ThreadActivity activity) {
- return activity == ThreadActivity::Runnable || activity == ThreadActivity::Paused;
- };
- R_UNLESS(IsValidThreadActivity(thread_activity), ResultInvalidEnumValue);
+ ret = WaitForAddress64From32(system, address, arb_type, value, timeout_ns);
- // Get the thread from its handle.
- KScopedAutoObject thread =
- system.Kernel().CurrentProcess()->GetHandleTable().GetObject<KThread>(thread_handle);
- R_UNLESS(thread.IsNotNull(), ResultInvalidHandle);
+ SetReg32(system, 0, Convert<uint32_t>(ret));
+}
+
+static void SvcWrap_SignalToAddress64From32(Core::System& system) {
+ Result ret{};
- // Check that the activity is being set on a non-current thread for the current process.
- R_UNLESS(thread->GetOwnerProcess() == system.Kernel().CurrentProcess(), ResultInvalidHandle);
- R_UNLESS(thread.GetPointerUnsafe() != GetCurrentThreadPointer(system.Kernel()), ResultBusy);
+ uint32_t address{};
+ SignalType signal_type{};
+ int32_t value{};
+ int32_t count{};
- // Set the activity.
- R_TRY(thread->SetActivity(thread_activity));
+ address = Convert<uint32_t>(GetReg32(system, 0));
+ signal_type = Convert<SignalType>(GetReg32(system, 1));
+ value = Convert<int32_t>(GetReg32(system, 2));
+ count = Convert<int32_t>(GetReg32(system, 3));
- return ResultSuccess;
+ ret = SignalToAddress64From32(system, address, signal_type, value, count);
+
+ SetReg32(system, 0, Convert<uint32_t>(ret));
}
-static Result SetThreadActivity32(Core::System& system, Handle thread_handle,
- Svc::ThreadActivity thread_activity) {
- return SetThreadActivity(system, thread_handle, thread_activity);
+static void SvcWrap_SynchronizePreemptionState64From32(Core::System& system) {
+ SynchronizePreemptionState64From32(system);
}
-/// Gets the thread context
-static Result GetThreadContext(Core::System& system, VAddr out_context, Handle thread_handle) {
- LOG_DEBUG(Kernel_SVC, "called, out_context=0x{:08X}, thread_handle=0x{:X}", out_context,
- thread_handle);
+static void SvcWrap_GetResourceLimitPeakValue64From32(Core::System& system) {
+ Result ret{};
- auto& kernel = system.Kernel();
+ int64_t out_peak_value{};
+ Handle resource_limit_handle{};
+ LimitableResource which{};
- // Get the thread from its handle.
- KScopedAutoObject thread =
- kernel.CurrentProcess()->GetHandleTable().GetObject<KThread>(thread_handle);
- R_UNLESS(thread.IsNotNull(), ResultInvalidHandle);
+ resource_limit_handle = Convert<Handle>(GetReg32(system, 1));
+ which = Convert<LimitableResource>(GetReg32(system, 2));
- // Require the handle be to a non-current thread in the current process.
- const auto* current_process = kernel.CurrentProcess();
- R_UNLESS(current_process == thread->GetOwnerProcess(), ResultInvalidId);
+ ret = GetResourceLimitPeakValue64From32(system, &out_peak_value, resource_limit_handle, which);
- // Verify that the thread isn't terminated.
- R_UNLESS(thread->GetState() != ThreadState::Terminated, ResultTerminationRequested);
+ SetReg32(system, 0, Convert<uint32_t>(ret));
+ auto out_peak_value_scatter = Convert<std::array<uint32_t, 2>>(out_peak_value);
+ SetReg32(system, 1, out_peak_value_scatter[0]);
+ SetReg32(system, 2, out_peak_value_scatter[1]);
+}
+
+static void SvcWrap_CreateIoPool64From32(Core::System& system) {
+ Result ret{};
- /// Check that the thread is not the current one.
- /// NOTE: Nintendo does not check this, and thus the following loop will deadlock.
- R_UNLESS(thread.GetPointerUnsafe() != GetCurrentThreadPointer(kernel), ResultInvalidId);
+ Handle out_handle{};
+ IoPoolType which{};
- // Try to get the thread context until the thread isn't current on any core.
- while (true) {
- KScopedSchedulerLock sl{kernel};
+ which = Convert<IoPoolType>(GetReg32(system, 1));
- // TODO(bunnei): Enforce that thread is suspended for debug here.
+ ret = CreateIoPool64From32(system, &out_handle, which);
- // If the thread's raw state isn't runnable, check if it's current on some core.
- if (thread->GetRawState() != ThreadState::Runnable) {
- bool current = false;
- for (auto i = 0; i < static_cast<s32>(Core::Hardware::NUM_CPU_CORES); ++i) {
- if (thread.GetPointerUnsafe() == kernel.Scheduler(i).GetSchedulerCurrentThread()) {
- current = true;
- break;
- }
- }
+ SetReg32(system, 0, Convert<uint32_t>(ret));
+ SetReg32(system, 1, Convert<uint32_t>(out_handle));
+}
- // If the thread is current, retry until it isn't.
- if (current) {
- continue;
- }
- }
+static void SvcWrap_CreateIoRegion64From32(Core::System& system) {
+ Result ret{};
- // Get the thread context.
- std::vector<u8> context;
- R_TRY(thread->GetThreadContext3(context));
+ Handle out_handle{};
+ Handle io_pool{};
+ uint64_t physical_address{};
+ uint32_t size{};
+ MemoryMapping mapping{};
+ MemoryPermission perm{};
- // Copy the thread context to user space.
- system.Memory().WriteBlock(out_context, context.data(), context.size());
+ io_pool = Convert<Handle>(GetReg32(system, 1));
+ std::array<uint32_t, 2> physical_address_gather{};
+ physical_address_gather[0] = GetReg32(system, 2);
+ physical_address_gather[1] = GetReg32(system, 3);
+ physical_address = Convert<uint64_t>(physical_address_gather);
+ size = Convert<uint32_t>(GetReg32(system, 0));
+ mapping = Convert<MemoryMapping>(GetReg32(system, 4));
+ perm = Convert<MemoryPermission>(GetReg32(system, 5));
- return ResultSuccess;
- }
+ ret = CreateIoRegion64From32(system, &out_handle, io_pool, physical_address, size, mapping, perm);
- return ResultSuccess;
+ SetReg32(system, 0, Convert<uint32_t>(ret));
+ SetReg32(system, 1, Convert<uint32_t>(out_handle));
}
-static Result GetThreadContext32(Core::System& system, u32 out_context, Handle thread_handle) {
- return GetThreadContext(system, out_context, thread_handle);
+static void SvcWrap_KernelDebug64From32(Core::System& system) {
+ KernelDebugType kern_debug_type{};
+ uint64_t arg0{};
+ uint64_t arg1{};
+ uint64_t arg2{};
+
+ kern_debug_type = Convert<KernelDebugType>(GetReg32(system, 0));
+ std::array<uint32_t, 2> arg0_gather{};
+ arg0_gather[0] = GetReg32(system, 2);
+ arg0_gather[1] = GetReg32(system, 3);
+ arg0 = Convert<uint64_t>(arg0_gather);
+ std::array<uint32_t, 2> arg1_gather{};
+ arg1_gather[0] = GetReg32(system, 1);
+ arg1_gather[1] = GetReg32(system, 4);
+ arg1 = Convert<uint64_t>(arg1_gather);
+ std::array<uint32_t, 2> arg2_gather{};
+ arg2_gather[0] = GetReg32(system, 5);
+ arg2_gather[1] = GetReg32(system, 6);
+ arg2 = Convert<uint64_t>(arg2_gather);
+
+ KernelDebug64From32(system, kern_debug_type, arg0, arg1, arg2);
}
-/// Gets the priority for the specified thread
-static Result GetThreadPriority(Core::System& system, u32* out_priority, Handle handle) {
- LOG_TRACE(Kernel_SVC, "called");
+static void SvcWrap_ChangeKernelTraceState64From32(Core::System& system) {
+ KernelTraceState kern_trace_state{};
- // Get the thread from its handle.
- KScopedAutoObject thread =
- system.Kernel().CurrentProcess()->GetHandleTable().GetObject<KThread>(handle);
- R_UNLESS(thread.IsNotNull(), ResultInvalidHandle);
+ kern_trace_state = Convert<KernelTraceState>(GetReg32(system, 0));
- // Get the thread's priority.
- *out_priority = thread->GetPriority();
- return ResultSuccess;
+ ChangeKernelTraceState64From32(system, kern_trace_state);
}
-static Result GetThreadPriority32(Core::System& system, u32* out_priority, Handle handle) {
- return GetThreadPriority(system, out_priority, handle);
+static void SvcWrap_CreateSession64From32(Core::System& system) {
+ Result ret{};
+
+ Handle out_server_session_handle{};
+ Handle out_client_session_handle{};
+ bool is_light{};
+ uint32_t name{};
+
+ is_light = Convert<bool>(GetReg32(system, 2));
+ name = Convert<uint32_t>(GetReg32(system, 3));
+
+ ret = CreateSession64From32(system, &out_server_session_handle, &out_client_session_handle, is_light, name);
+
+ SetReg32(system, 0, Convert<uint32_t>(ret));
+ SetReg32(system, 1, Convert<uint32_t>(out_server_session_handle));
+ SetReg32(system, 2, Convert<uint32_t>(out_client_session_handle));
}
-/// Sets the priority for the specified thread
-static Result SetThreadPriority(Core::System& system, Handle thread_handle, u32 priority) {
- // Get the current process.
- KProcess& process = *system.Kernel().CurrentProcess();
+static void SvcWrap_AcceptSession64From32(Core::System& system) {
+ Result ret{};
+
+ Handle out_handle{};
+ Handle port{};
- // Validate the priority.
- R_UNLESS(HighestThreadPriority <= priority && priority <= LowestThreadPriority,
- ResultInvalidPriority);
- R_UNLESS(process.CheckThreadPriority(priority), ResultInvalidPriority);
+ port = Convert<Handle>(GetReg32(system, 1));
- // Get the thread from its handle.
- KScopedAutoObject thread = process.GetHandleTable().GetObject<KThread>(thread_handle);
- R_UNLESS(thread.IsNotNull(), ResultInvalidHandle);
+ ret = AcceptSession64From32(system, &out_handle, port);
- // Set the thread priority.
- thread->SetBasePriority(priority);
- return ResultSuccess;
+ SetReg32(system, 0, Convert<uint32_t>(ret));
+ SetReg32(system, 1, Convert<uint32_t>(out_handle));
}
-static Result SetThreadPriority32(Core::System& system, Handle thread_handle, u32 priority) {
- return SetThreadPriority(system, thread_handle, priority);
+static void SvcWrap_ReplyAndReceive64From32(Core::System& system) {
+ Result ret{};
+
+ int32_t out_index{};
+ uint32_t handles{};
+ int32_t num_handles{};
+ Handle reply_target{};
+ int64_t timeout_ns{};
+
+ handles = Convert<uint32_t>(GetReg32(system, 1));
+ num_handles = Convert<int32_t>(GetReg32(system, 2));
+ reply_target = Convert<Handle>(GetReg32(system, 3));
+ std::array<uint32_t, 2> timeout_ns_gather{};
+ timeout_ns_gather[0] = GetReg32(system, 0);
+ timeout_ns_gather[1] = GetReg32(system, 4);
+ timeout_ns = Convert<int64_t>(timeout_ns_gather);
+
+ ret = ReplyAndReceive64From32(system, &out_index, handles, num_handles, reply_target, timeout_ns);
+
+ SetReg32(system, 0, Convert<uint32_t>(ret));
+ SetReg32(system, 1, Convert<uint32_t>(out_index));
}
-/// Get which CPU core is executing the current thread
-static u32 GetCurrentProcessorNumber(Core::System& system) {
- LOG_TRACE(Kernel_SVC, "called");
- return static_cast<u32>(system.CurrentPhysicalCore().CoreIndex());
+static void SvcWrap_ReplyAndReceiveWithUserBuffer64From32(Core::System& system) {
+ Result ret{};
+
+ int32_t out_index{};
+ uint32_t message_buffer{};
+ uint32_t message_buffer_size{};
+ uint32_t handles{};
+ int32_t num_handles{};
+ Handle reply_target{};
+ int64_t timeout_ns{};
+
+ message_buffer = Convert<uint32_t>(GetReg32(system, 1));
+ message_buffer_size = Convert<uint32_t>(GetReg32(system, 2));
+ handles = Convert<uint32_t>(GetReg32(system, 3));
+ num_handles = Convert<int32_t>(GetReg32(system, 0));
+ reply_target = Convert<Handle>(GetReg32(system, 4));
+ std::array<uint32_t, 2> timeout_ns_gather{};
+ timeout_ns_gather[0] = GetReg32(system, 5);
+ timeout_ns_gather[1] = GetReg32(system, 6);
+ timeout_ns = Convert<int64_t>(timeout_ns_gather);
+
+ ret = ReplyAndReceiveWithUserBuffer64From32(system, &out_index, message_buffer, message_buffer_size, handles, num_handles, reply_target, timeout_ns);
+
+ SetReg32(system, 0, Convert<uint32_t>(ret));
+ SetReg32(system, 1, Convert<uint32_t>(out_index));
}
-static u32 GetCurrentProcessorNumber32(Core::System& system) {
- return GetCurrentProcessorNumber(system);
+static void SvcWrap_CreateEvent64From32(Core::System& system) {
+ Result ret{};
+
+ Handle out_write_handle{};
+ Handle out_read_handle{};
+
+ ret = CreateEvent64From32(system, &out_write_handle, &out_read_handle);
+
+ SetReg32(system, 0, Convert<uint32_t>(ret));
+ SetReg32(system, 1, Convert<uint32_t>(out_write_handle));
+ SetReg32(system, 2, Convert<uint32_t>(out_read_handle));
}
-namespace {
+static void SvcWrap_MapIoRegion64From32(Core::System& system) {
+ Result ret{};
-constexpr bool IsValidSharedMemoryPermission(Svc::MemoryPermission perm) {
- switch (perm) {
- case Svc::MemoryPermission::Read:
- case Svc::MemoryPermission::ReadWrite:
- return true;
- default:
- return false;
- }
+ Handle io_region{};
+ uint32_t address{};
+ uint32_t size{};
+ MemoryPermission perm{};
+
+ io_region = Convert<Handle>(GetReg32(system, 0));
+ address = Convert<uint32_t>(GetReg32(system, 1));
+ size = Convert<uint32_t>(GetReg32(system, 2));
+ perm = Convert<MemoryPermission>(GetReg32(system, 3));
+
+ ret = MapIoRegion64From32(system, io_region, address, size, perm);
+
+ SetReg32(system, 0, Convert<uint32_t>(ret));
}
-[[maybe_unused]] constexpr bool IsValidRemoteSharedMemoryPermission(Svc::MemoryPermission perm) {
- return IsValidSharedMemoryPermission(perm) || perm == Svc::MemoryPermission::DontCare;
+static void SvcWrap_UnmapIoRegion64From32(Core::System& system) {
+ Result ret{};
+
+ Handle io_region{};
+ uint32_t address{};
+ uint32_t size{};
+
+ io_region = Convert<Handle>(GetReg32(system, 0));
+ address = Convert<uint32_t>(GetReg32(system, 1));
+ size = Convert<uint32_t>(GetReg32(system, 2));
+
+ ret = UnmapIoRegion64From32(system, io_region, address, size);
+
+ SetReg32(system, 0, Convert<uint32_t>(ret));
}
-constexpr bool IsValidProcessMemoryPermission(Svc::MemoryPermission perm) {
- switch (perm) {
- case Svc::MemoryPermission::None:
- case Svc::MemoryPermission::Read:
- case Svc::MemoryPermission::ReadWrite:
- case Svc::MemoryPermission::ReadExecute:
- return true;
- default:
- return false;
- }
+static void SvcWrap_MapPhysicalMemoryUnsafe64From32(Core::System& system) {
+ Result ret{};
+
+ uint32_t address{};
+ uint32_t size{};
+
+ address = Convert<uint32_t>(GetReg32(system, 0));
+ size = Convert<uint32_t>(GetReg32(system, 1));
+
+ ret = MapPhysicalMemoryUnsafe64From32(system, address, size);
+
+ SetReg32(system, 0, Convert<uint32_t>(ret));
}
-constexpr bool IsValidMapCodeMemoryPermission(Svc::MemoryPermission perm) {
- return perm == Svc::MemoryPermission::ReadWrite;
+static void SvcWrap_UnmapPhysicalMemoryUnsafe64From32(Core::System& system) {
+ Result ret{};
+
+ uint32_t address{};
+ uint32_t size{};
+
+ address = Convert<uint32_t>(GetReg32(system, 0));
+ size = Convert<uint32_t>(GetReg32(system, 1));
+
+ ret = UnmapPhysicalMemoryUnsafe64From32(system, address, size);
+
+ SetReg32(system, 0, Convert<uint32_t>(ret));
}
-constexpr bool IsValidMapToOwnerCodeMemoryPermission(Svc::MemoryPermission perm) {
- return perm == Svc::MemoryPermission::Read || perm == Svc::MemoryPermission::ReadExecute;
+static void SvcWrap_SetUnsafeLimit64From32(Core::System& system) {
+ Result ret{};
+
+ uint32_t limit{};
+
+ limit = Convert<uint32_t>(GetReg32(system, 0));
+
+ ret = SetUnsafeLimit64From32(system, limit);
+
+ SetReg32(system, 0, Convert<uint32_t>(ret));
}
-constexpr bool IsValidUnmapCodeMemoryPermission(Svc::MemoryPermission perm) {
- return perm == Svc::MemoryPermission::None;
+static void SvcWrap_CreateCodeMemory64From32(Core::System& system) {
+ Result ret{};
+
+ Handle out_handle{};
+ uint32_t address{};
+ uint32_t size{};
+
+ address = Convert<uint32_t>(GetReg32(system, 1));
+ size = Convert<uint32_t>(GetReg32(system, 2));
+
+ ret = CreateCodeMemory64From32(system, &out_handle, address, size);
+
+ SetReg32(system, 0, Convert<uint32_t>(ret));
+ SetReg32(system, 1, Convert<uint32_t>(out_handle));
}
-constexpr bool IsValidUnmapFromOwnerCodeMemoryPermission(Svc::MemoryPermission perm) {
- return perm == Svc::MemoryPermission::None;
+static void SvcWrap_ControlCodeMemory64From32(Core::System& system) {
+ Result ret{};
+
+ Handle code_memory_handle{};
+ CodeMemoryOperation operation{};
+ uint64_t address{};
+ uint64_t size{};
+ MemoryPermission perm{};
+
+ code_memory_handle = Convert<Handle>(GetReg32(system, 0));
+ operation = Convert<CodeMemoryOperation>(GetReg32(system, 1));
+ std::array<uint32_t, 2> address_gather{};
+ address_gather[0] = GetReg32(system, 2);
+ address_gather[1] = GetReg32(system, 3);
+ address = Convert<uint64_t>(address_gather);
+ std::array<uint32_t, 2> size_gather{};
+ size_gather[0] = GetReg32(system, 4);
+ size_gather[1] = GetReg32(system, 5);
+ size = Convert<uint64_t>(size_gather);
+ perm = Convert<MemoryPermission>(GetReg32(system, 6));
+
+ ret = ControlCodeMemory64From32(system, code_memory_handle, operation, address, size, perm);
+
+ SetReg32(system, 0, Convert<uint32_t>(ret));
}
-} // Anonymous namespace
+static void SvcWrap_SleepSystem64From32(Core::System& system) {
+ SleepSystem64From32(system);
+}
-static Result MapSharedMemory(Core::System& system, Handle shmem_handle, VAddr address, u64 size,
- Svc::MemoryPermission map_perm) {
- LOG_TRACE(Kernel_SVC,
- "called, shared_memory_handle=0x{:X}, addr=0x{:X}, size=0x{:X}, permissions=0x{:08X}",
- shmem_handle, address, size, map_perm);
+static void SvcWrap_ReadWriteRegister64From32(Core::System& system) {
+ Result ret{};
- // Validate the address/size.
- R_UNLESS(Common::IsAligned(address, PageSize), ResultInvalidAddress);
- R_UNLESS(Common::IsAligned(size, PageSize), ResultInvalidSize);
- R_UNLESS(size > 0, ResultInvalidSize);
- R_UNLESS((address < address + size), ResultInvalidCurrentMemory);
+ uint32_t out_value{};
+ uint64_t address{};
+ uint32_t mask{};
+ uint32_t value{};
- // Validate the permission.
- R_UNLESS(IsValidSharedMemoryPermission(map_perm), ResultInvalidNewMemoryPermission);
+ std::array<uint32_t, 2> address_gather{};
+ address_gather[0] = GetReg32(system, 2);
+ address_gather[1] = GetReg32(system, 3);
+ address = Convert<uint64_t>(address_gather);
+ mask = Convert<uint32_t>(GetReg32(system, 0));
+ value = Convert<uint32_t>(GetReg32(system, 1));
- // Get the current process.
- auto& process = *system.Kernel().CurrentProcess();
- auto& page_table = process.PageTable();
+ ret = ReadWriteRegister64From32(system, &out_value, address, mask, value);
- // Get the shared memory.
- KScopedAutoObject shmem = process.GetHandleTable().GetObject<KSharedMemory>(shmem_handle);
- R_UNLESS(shmem.IsNotNull(), ResultInvalidHandle);
+ SetReg32(system, 0, Convert<uint32_t>(ret));
+ SetReg32(system, 1, Convert<uint32_t>(out_value));
+}
- // Verify that the mapping is in range.
- R_UNLESS(page_table.CanContain(address, size, KMemoryState::Shared), ResultInvalidMemoryRegion);
+static void SvcWrap_SetProcessActivity64From32(Core::System& system) {
+ Result ret{};
- // Add the shared memory to the process.
- R_TRY(process.AddSharedMemory(shmem.GetPointerUnsafe(), address, size));
+ Handle process_handle{};
+ ProcessActivity process_activity{};
- // Ensure that we clean up the shared memory if we fail to map it.
- auto guard =
- SCOPE_GUARD({ process.RemoveSharedMemory(shmem.GetPointerUnsafe(), address, size); });
+ process_handle = Convert<Handle>(GetReg32(system, 0));
+ process_activity = Convert<ProcessActivity>(GetReg32(system, 1));
- // Map the shared memory.
- R_TRY(shmem->Map(process, address, size, map_perm));
+ ret = SetProcessActivity64From32(system, process_handle, process_activity);
- // We succeeded.
- guard.Cancel();
- return ResultSuccess;
+ SetReg32(system, 0, Convert<uint32_t>(ret));
}
-static Result MapSharedMemory32(Core::System& system, Handle shmem_handle, u32 address, u32 size,
- Svc::MemoryPermission map_perm) {
- return MapSharedMemory(system, shmem_handle, address, size, map_perm);
+static void SvcWrap_CreateSharedMemory64From32(Core::System& system) {
+ Result ret{};
+
+ Handle out_handle{};
+ uint32_t size{};
+ MemoryPermission owner_perm{};
+ MemoryPermission remote_perm{};
+
+ size = Convert<uint32_t>(GetReg32(system, 1));
+ owner_perm = Convert<MemoryPermission>(GetReg32(system, 2));
+ remote_perm = Convert<MemoryPermission>(GetReg32(system, 3));
+
+ ret = CreateSharedMemory64From32(system, &out_handle, size, owner_perm, remote_perm);
+
+ SetReg32(system, 0, Convert<uint32_t>(ret));
+ SetReg32(system, 1, Convert<uint32_t>(out_handle));
}
-static Result UnmapSharedMemory(Core::System& system, Handle shmem_handle, VAddr address,
- u64 size) {
- // Validate the address/size.
- R_UNLESS(Common::IsAligned(address, PageSize), ResultInvalidAddress);
- R_UNLESS(Common::IsAligned(size, PageSize), ResultInvalidSize);
- R_UNLESS(size > 0, ResultInvalidSize);
- R_UNLESS((address < address + size), ResultInvalidCurrentMemory);
+static void SvcWrap_MapTransferMemory64From32(Core::System& system) {
+ Result ret{};
+
+ Handle trmem_handle{};
+ uint32_t address{};
+ uint32_t size{};
+ MemoryPermission owner_perm{};
- // Get the current process.
- auto& process = *system.Kernel().CurrentProcess();
- auto& page_table = process.PageTable();
+ trmem_handle = Convert<Handle>(GetReg32(system, 0));
+ address = Convert<uint32_t>(GetReg32(system, 1));
+ size = Convert<uint32_t>(GetReg32(system, 2));
+ owner_perm = Convert<MemoryPermission>(GetReg32(system, 3));
- // Get the shared memory.
- KScopedAutoObject shmem = process.GetHandleTable().GetObject<KSharedMemory>(shmem_handle);
- R_UNLESS(shmem.IsNotNull(), ResultInvalidHandle);
+ ret = MapTransferMemory64From32(system, trmem_handle, address, size, owner_perm);
- // Verify that the mapping is in range.
- R_UNLESS(page_table.CanContain(address, size, KMemoryState::Shared), ResultInvalidMemoryRegion);
+ SetReg32(system, 0, Convert<uint32_t>(ret));
+}
+
+static void SvcWrap_UnmapTransferMemory64From32(Core::System& system) {
+ Result ret{};
- // Unmap the shared memory.
- R_TRY(shmem->Unmap(process, address, size));
+ Handle trmem_handle{};
+ uint32_t address{};
+ uint32_t size{};
- // Remove the shared memory from the process.
- process.RemoveSharedMemory(shmem.GetPointerUnsafe(), address, size);
+ trmem_handle = Convert<Handle>(GetReg32(system, 0));
+ address = Convert<uint32_t>(GetReg32(system, 1));
+ size = Convert<uint32_t>(GetReg32(system, 2));
- return ResultSuccess;
+ ret = UnmapTransferMemory64From32(system, trmem_handle, address, size);
+
+ SetReg32(system, 0, Convert<uint32_t>(ret));
}
-static Result UnmapSharedMemory32(Core::System& system, Handle shmem_handle, u32 address,
- u32 size) {
- return UnmapSharedMemory(system, shmem_handle, address, size);
+static void SvcWrap_CreateInterruptEvent64From32(Core::System& system) {
+ Result ret{};
+
+ Handle out_read_handle{};
+ int32_t interrupt_id{};
+ InterruptType interrupt_type{};
+
+ interrupt_id = Convert<int32_t>(GetReg32(system, 1));
+ interrupt_type = Convert<InterruptType>(GetReg32(system, 2));
+
+ ret = CreateInterruptEvent64From32(system, &out_read_handle, interrupt_id, interrupt_type);
+
+ SetReg32(system, 0, Convert<uint32_t>(ret));
+ SetReg32(system, 1, Convert<uint32_t>(out_read_handle));
}
-static Result SetProcessMemoryPermission(Core::System& system, Handle process_handle, VAddr address,
- u64 size, Svc::MemoryPermission perm) {
- LOG_TRACE(Kernel_SVC,
- "called, process_handle=0x{:X}, addr=0x{:X}, size=0x{:X}, permissions=0x{:08X}",
- process_handle, address, size, perm);
+static void SvcWrap_QueryPhysicalAddress64From32(Core::System& system) {
+ Result ret{};
- // Validate the address/size.
- R_UNLESS(Common::IsAligned(address, PageSize), ResultInvalidAddress);
- R_UNLESS(Common::IsAligned(size, PageSize), ResultInvalidSize);
- R_UNLESS(size > 0, ResultInvalidSize);
- R_UNLESS((address < address + size), ResultInvalidCurrentMemory);
- R_UNLESS(address == static_cast<uintptr_t>(address), ResultInvalidCurrentMemory);
- R_UNLESS(size == static_cast<size_t>(size), ResultInvalidCurrentMemory);
+ ilp32::PhysicalMemoryInfo out_info{};
+ uint32_t address{};
- // Validate the memory permission.
- R_UNLESS(IsValidProcessMemoryPermission(perm), ResultInvalidNewMemoryPermission);
+ address = Convert<uint32_t>(GetReg32(system, 1));
- // Get the process from its handle.
- KScopedAutoObject process =
- system.CurrentProcess()->GetHandleTable().GetObject<KProcess>(process_handle);
- R_UNLESS(process.IsNotNull(), ResultInvalidHandle);
+ ret = QueryPhysicalAddress64From32(system, &out_info, address);
- // Validate that the address is in range.
- auto& page_table = process->PageTable();
- R_UNLESS(page_table.Contains(address, size), ResultInvalidCurrentMemory);
+ SetReg32(system, 0, Convert<uint32_t>(ret));
+ auto out_info_scatter = Convert<std::array<uint32_t, 4>>(out_info);
+ SetReg32(system, 1, out_info_scatter[0]);
+ SetReg32(system, 2, out_info_scatter[1]);
+ SetReg32(system, 3, out_info_scatter[2]);
+ SetReg32(system, 4, out_info_scatter[3]);
+}
- // Set the memory permission.
- return page_table.SetProcessMemoryPermission(address, size, perm);
+static void SvcWrap_QueryIoMapping64From32(Core::System& system) {
+ Result ret{};
+
+ uintptr_t out_address{};
+ uintptr_t out_size{};
+ uint64_t physical_address{};
+ uint32_t size{};
+
+ std::array<uint32_t, 2> physical_address_gather{};
+ physical_address_gather[0] = GetReg32(system, 2);
+ physical_address_gather[1] = GetReg32(system, 3);
+ physical_address = Convert<uint64_t>(physical_address_gather);
+ size = Convert<uint32_t>(GetReg32(system, 0));
+
+ ret = QueryIoMapping64From32(system, &out_address, &out_size, physical_address, size);
+
+ SetReg32(system, 0, Convert<uint32_t>(ret));
+ SetReg32(system, 1, Convert<uint32_t>(out_address));
+ SetReg32(system, 2, Convert<uint32_t>(out_size));
}
-static Result MapProcessMemory(Core::System& system, VAddr dst_address, Handle process_handle,
- VAddr src_address, u64 size) {
- LOG_TRACE(Kernel_SVC,
- "called, dst_address=0x{:X}, process_handle=0x{:X}, src_address=0x{:X}, size=0x{:X}",
- dst_address, process_handle, src_address, size);
+static void SvcWrap_CreateDeviceAddressSpace64From32(Core::System& system) {
+ Result ret{};
- // Validate the address/size.
- R_UNLESS(Common::IsAligned(dst_address, PageSize), ResultInvalidAddress);
- R_UNLESS(Common::IsAligned(src_address, PageSize), ResultInvalidAddress);
- R_UNLESS(Common::IsAligned(size, PageSize), ResultInvalidSize);
- R_UNLESS(size > 0, ResultInvalidSize);
- R_UNLESS((dst_address < dst_address + size), ResultInvalidCurrentMemory);
- R_UNLESS((src_address < src_address + size), ResultInvalidCurrentMemory);
+ Handle out_handle{};
+ uint64_t das_address{};
+ uint64_t das_size{};
- // Get the processes.
- KProcess* dst_process = system.CurrentProcess();
- KScopedAutoObject src_process =
- dst_process->GetHandleTable().GetObjectWithoutPseudoHandle<KProcess>(process_handle);
- R_UNLESS(src_process.IsNotNull(), ResultInvalidHandle);
+ std::array<uint32_t, 2> das_address_gather{};
+ das_address_gather[0] = GetReg32(system, 2);
+ das_address_gather[1] = GetReg32(system, 3);
+ das_address = Convert<uint64_t>(das_address_gather);
+ std::array<uint32_t, 2> das_size_gather{};
+ das_size_gather[0] = GetReg32(system, 0);
+ das_size_gather[1] = GetReg32(system, 1);
+ das_size = Convert<uint64_t>(das_size_gather);
- // Get the page tables.
- auto& dst_pt = dst_process->PageTable();
- auto& src_pt = src_process->PageTable();
+ ret = CreateDeviceAddressSpace64From32(system, &out_handle, das_address, das_size);
- // Validate that the mapping is in range.
- R_UNLESS(src_pt.Contains(src_address, size), ResultInvalidCurrentMemory);
- R_UNLESS(dst_pt.CanContain(dst_address, size, KMemoryState::SharedCode),
- ResultInvalidMemoryRegion);
+ SetReg32(system, 0, Convert<uint32_t>(ret));
+ SetReg32(system, 1, Convert<uint32_t>(out_handle));
+}
+
+static void SvcWrap_AttachDeviceAddressSpace64From32(Core::System& system) {
+ Result ret{};
- // Create a new page group.
- KPageGroup pg;
- R_TRY(src_pt.MakeAndOpenPageGroup(
- std::addressof(pg), src_address, size / PageSize, KMemoryState::FlagCanMapProcess,
- KMemoryState::FlagCanMapProcess, KMemoryPermission::None, KMemoryPermission::None,
- KMemoryAttribute::All, KMemoryAttribute::None));
+ DeviceName device_name{};
+ Handle das_handle{};
- // Map the group.
- R_TRY(dst_pt.MapPages(dst_address, pg, KMemoryState::SharedCode,
- KMemoryPermission::UserReadWrite));
+ device_name = Convert<DeviceName>(GetReg32(system, 0));
+ das_handle = Convert<Handle>(GetReg32(system, 1));
- return ResultSuccess;
+ ret = AttachDeviceAddressSpace64From32(system, device_name, das_handle);
+
+ SetReg32(system, 0, Convert<uint32_t>(ret));
}
-static Result UnmapProcessMemory(Core::System& system, VAddr dst_address, Handle process_handle,
- VAddr src_address, u64 size) {
- LOG_TRACE(Kernel_SVC,
- "called, dst_address=0x{:X}, process_handle=0x{:X}, src_address=0x{:X}, size=0x{:X}",
- dst_address, process_handle, src_address, size);
+static void SvcWrap_DetachDeviceAddressSpace64From32(Core::System& system) {
+ Result ret{};
- // Validate the address/size.
- R_UNLESS(Common::IsAligned(dst_address, PageSize), ResultInvalidAddress);
- R_UNLESS(Common::IsAligned(src_address, PageSize), ResultInvalidAddress);
- R_UNLESS(Common::IsAligned(size, PageSize), ResultInvalidSize);
- R_UNLESS(size > 0, ResultInvalidSize);
- R_UNLESS((dst_address < dst_address + size), ResultInvalidCurrentMemory);
- R_UNLESS((src_address < src_address + size), ResultInvalidCurrentMemory);
+ DeviceName device_name{};
+ Handle das_handle{};
- // Get the processes.
- KProcess* dst_process = system.CurrentProcess();
- KScopedAutoObject src_process =
- dst_process->GetHandleTable().GetObjectWithoutPseudoHandle<KProcess>(process_handle);
- R_UNLESS(src_process.IsNotNull(), ResultInvalidHandle);
-
- // Get the page tables.
- auto& dst_pt = dst_process->PageTable();
- auto& src_pt = src_process->PageTable();
-
- // Validate that the mapping is in range.
- R_UNLESS(src_pt.Contains(src_address, size), ResultInvalidCurrentMemory);
- R_UNLESS(dst_pt.CanContain(dst_address, size, KMemoryState::SharedCode),
- ResultInvalidMemoryRegion);
-
- // Unmap the memory.
- R_TRY(dst_pt.UnmapProcessMemory(dst_address, size, src_pt, src_address));
-
- return ResultSuccess;
-}
-
-static Result CreateCodeMemory(Core::System& system, Handle* out, VAddr address, size_t size) {
- LOG_TRACE(Kernel_SVC, "called, address=0x{:X}, size=0x{:X}", address, size);
-
- // Get kernel instance.
- auto& kernel = system.Kernel();
+ device_name = Convert<DeviceName>(GetReg32(system, 0));
+ das_handle = Convert<Handle>(GetReg32(system, 1));
- // Validate address / size.
- R_UNLESS(Common::IsAligned(address, PageSize), ResultInvalidAddress);
- R_UNLESS(Common::IsAligned(size, PageSize), ResultInvalidSize);
- R_UNLESS(size > 0, ResultInvalidSize);
- R_UNLESS((address < address + size), ResultInvalidCurrentMemory);
+ ret = DetachDeviceAddressSpace64From32(system, device_name, das_handle);
- // Create the code memory.
+ SetReg32(system, 0, Convert<uint32_t>(ret));
+}
+
+static void SvcWrap_MapDeviceAddressSpaceByForce64From32(Core::System& system) {
+ Result ret{};
- KCodeMemory* code_mem = KCodeMemory::Create(kernel);
- R_UNLESS(code_mem != nullptr, ResultOutOfResource);
+ Handle das_handle{};
+ Handle process_handle{};
+ uint64_t process_address{};
+ uint32_t size{};
+ uint64_t device_address{};
+ uint32_t option{};
- // Verify that the region is in range.
- R_UNLESS(system.CurrentProcess()->PageTable().Contains(address, size),
- ResultInvalidCurrentMemory);
+ das_handle = Convert<Handle>(GetReg32(system, 0));
+ process_handle = Convert<Handle>(GetReg32(system, 1));
+ std::array<uint32_t, 2> process_address_gather{};
+ process_address_gather[0] = GetReg32(system, 2);
+ process_address_gather[1] = GetReg32(system, 3);
+ process_address = Convert<uint64_t>(process_address_gather);
+ size = Convert<uint32_t>(GetReg32(system, 4));
+ std::array<uint32_t, 2> device_address_gather{};
+ device_address_gather[0] = GetReg32(system, 5);
+ device_address_gather[1] = GetReg32(system, 6);
+ device_address = Convert<uint64_t>(device_address_gather);
+ option = Convert<uint32_t>(GetReg32(system, 7));
- // Initialize the code memory.
- R_TRY(code_mem->Initialize(system.DeviceMemory(), address, size));
+ ret = MapDeviceAddressSpaceByForce64From32(system, das_handle, process_handle, process_address, size, device_address, option);
+
+ SetReg32(system, 0, Convert<uint32_t>(ret));
+}
- // Register the code memory.
- KCodeMemory::Register(kernel, code_mem);
+static void SvcWrap_MapDeviceAddressSpaceAligned64From32(Core::System& system) {
+ Result ret{};
- // Add the code memory to the handle table.
- R_TRY(system.CurrentProcess()->GetHandleTable().Add(out, code_mem));
+ Handle das_handle{};
+ Handle process_handle{};
+ uint64_t process_address{};
+ uint32_t size{};
+ uint64_t device_address{};
+ uint32_t option{};
- code_mem->Close();
+ das_handle = Convert<Handle>(GetReg32(system, 0));
+ process_handle = Convert<Handle>(GetReg32(system, 1));
+ std::array<uint32_t, 2> process_address_gather{};
+ process_address_gather[0] = GetReg32(system, 2);
+ process_address_gather[1] = GetReg32(system, 3);
+ process_address = Convert<uint64_t>(process_address_gather);
+ size = Convert<uint32_t>(GetReg32(system, 4));
+ std::array<uint32_t, 2> device_address_gather{};
+ device_address_gather[0] = GetReg32(system, 5);
+ device_address_gather[1] = GetReg32(system, 6);
+ device_address = Convert<uint64_t>(device_address_gather);
+ option = Convert<uint32_t>(GetReg32(system, 7));
- return ResultSuccess;
+ ret = MapDeviceAddressSpaceAligned64From32(system, das_handle, process_handle, process_address, size, device_address, option);
+
+ SetReg32(system, 0, Convert<uint32_t>(ret));
}
-static Result CreateCodeMemory32(Core::System& system, Handle* out, u32 address, u32 size) {
- return CreateCodeMemory(system, out, address, size);
+static void SvcWrap_UnmapDeviceAddressSpace64From32(Core::System& system) {
+ Result ret{};
+
+ Handle das_handle{};
+ Handle process_handle{};
+ uint64_t process_address{};
+ uint32_t size{};
+ uint64_t device_address{};
+
+ das_handle = Convert<Handle>(GetReg32(system, 0));
+ process_handle = Convert<Handle>(GetReg32(system, 1));
+ std::array<uint32_t, 2> process_address_gather{};
+ process_address_gather[0] = GetReg32(system, 2);
+ process_address_gather[1] = GetReg32(system, 3);
+ process_address = Convert<uint64_t>(process_address_gather);
+ size = Convert<uint32_t>(GetReg32(system, 4));
+ std::array<uint32_t, 2> device_address_gather{};
+ device_address_gather[0] = GetReg32(system, 5);
+ device_address_gather[1] = GetReg32(system, 6);
+ device_address = Convert<uint64_t>(device_address_gather);
+
+ ret = UnmapDeviceAddressSpace64From32(system, das_handle, process_handle, process_address, size, device_address);
+
+ SetReg32(system, 0, Convert<uint32_t>(ret));
}
-static Result ControlCodeMemory(Core::System& system, Handle code_memory_handle, u32 operation,
- VAddr address, size_t size, Svc::MemoryPermission perm) {
+static void SvcWrap_InvalidateProcessDataCache64From32(Core::System& system) {
+ Result ret{};
- LOG_TRACE(Kernel_SVC,
- "called, code_memory_handle=0x{:X}, operation=0x{:X}, address=0x{:X}, size=0x{:X}, "
- "permission=0x{:X}",
- code_memory_handle, operation, address, size, perm);
+ Handle process_handle{};
+ uint64_t address{};
+ uint64_t size{};
- // Validate the address / size.
- R_UNLESS(Common::IsAligned(address, PageSize), ResultInvalidAddress);
- R_UNLESS(Common::IsAligned(size, PageSize), ResultInvalidSize);
- R_UNLESS(size > 0, ResultInvalidSize);
- R_UNLESS((address < address + size), ResultInvalidCurrentMemory);
+ process_handle = Convert<Handle>(GetReg32(system, 0));
+ std::array<uint32_t, 2> address_gather{};
+ address_gather[0] = GetReg32(system, 2);
+ address_gather[1] = GetReg32(system, 3);
+ address = Convert<uint64_t>(address_gather);
+ std::array<uint32_t, 2> size_gather{};
+ size_gather[0] = GetReg32(system, 1);
+ size_gather[1] = GetReg32(system, 4);
+ size = Convert<uint64_t>(size_gather);
- // Get the code memory from its handle.
- KScopedAutoObject code_mem =
- system.CurrentProcess()->GetHandleTable().GetObject<KCodeMemory>(code_memory_handle);
- R_UNLESS(code_mem.IsNotNull(), ResultInvalidHandle);
+ ret = InvalidateProcessDataCache64From32(system, process_handle, address, size);
+
+ SetReg32(system, 0, Convert<uint32_t>(ret));
+}
- // NOTE: Here, Atmosphere extends the SVC to allow code memory operations on one's own process.
- // This enables homebrew usage of these SVCs for JIT.
+static void SvcWrap_StoreProcessDataCache64From32(Core::System& system) {
+ Result ret{};
- // Perform the operation.
- switch (static_cast<CodeMemoryOperation>(operation)) {
- case CodeMemoryOperation::Map: {
- // Check that the region is in range.
- R_UNLESS(
- system.CurrentProcess()->PageTable().CanContain(address, size, KMemoryState::CodeOut),
- ResultInvalidMemoryRegion);
+ Handle process_handle{};
+ uint64_t address{};
+ uint64_t size{};
- // Check the memory permission.
- R_UNLESS(IsValidMapCodeMemoryPermission(perm), ResultInvalidNewMemoryPermission);
+ process_handle = Convert<Handle>(GetReg32(system, 0));
+ std::array<uint32_t, 2> address_gather{};
+ address_gather[0] = GetReg32(system, 2);
+ address_gather[1] = GetReg32(system, 3);
+ address = Convert<uint64_t>(address_gather);
+ std::array<uint32_t, 2> size_gather{};
+ size_gather[0] = GetReg32(system, 1);
+ size_gather[1] = GetReg32(system, 4);
+ size = Convert<uint64_t>(size_gather);
- // Map the memory.
- R_TRY(code_mem->Map(address, size));
- } break;
- case CodeMemoryOperation::Unmap: {
- // Check that the region is in range.
- R_UNLESS(
- system.CurrentProcess()->PageTable().CanContain(address, size, KMemoryState::CodeOut),
- ResultInvalidMemoryRegion);
+ ret = StoreProcessDataCache64From32(system, process_handle, address, size);
- // Check the memory permission.
- R_UNLESS(IsValidUnmapCodeMemoryPermission(perm), ResultInvalidNewMemoryPermission);
+ SetReg32(system, 0, Convert<uint32_t>(ret));
+}
- // Unmap the memory.
- R_TRY(code_mem->Unmap(address, size));
- } break;
- case CodeMemoryOperation::MapToOwner: {
- // Check that the region is in range.
- R_UNLESS(code_mem->GetOwner()->PageTable().CanContain(address, size,
- KMemoryState::GeneratedCode),
- ResultInvalidMemoryRegion);
+static void SvcWrap_FlushProcessDataCache64From32(Core::System& system) {
+ Result ret{};
- // Check the memory permission.
- R_UNLESS(IsValidMapToOwnerCodeMemoryPermission(perm), ResultInvalidNewMemoryPermission);
+ Handle process_handle{};
+ uint64_t address{};
+ uint64_t size{};
- // Map the memory to its owner.
- R_TRY(code_mem->MapToOwner(address, size, perm));
- } break;
- case CodeMemoryOperation::UnmapFromOwner: {
- // Check that the region is in range.
- R_UNLESS(code_mem->GetOwner()->PageTable().CanContain(address, size,
- KMemoryState::GeneratedCode),
- ResultInvalidMemoryRegion);
+ process_handle = Convert<Handle>(GetReg32(system, 0));
+ std::array<uint32_t, 2> address_gather{};
+ address_gather[0] = GetReg32(system, 2);
+ address_gather[1] = GetReg32(system, 3);
+ address = Convert<uint64_t>(address_gather);
+ std::array<uint32_t, 2> size_gather{};
+ size_gather[0] = GetReg32(system, 1);
+ size_gather[1] = GetReg32(system, 4);
+ size = Convert<uint64_t>(size_gather);
- // Check the memory permission.
- R_UNLESS(IsValidUnmapFromOwnerCodeMemoryPermission(perm), ResultInvalidNewMemoryPermission);
-
- // Unmap the memory from its owner.
- R_TRY(code_mem->UnmapFromOwner(address, size));
- } break;
- default:
- return ResultInvalidEnumValue;
- }
+ ret = FlushProcessDataCache64From32(system, process_handle, address, size);
- return ResultSuccess;
+ SetReg32(system, 0, Convert<uint32_t>(ret));
}
-static Result ControlCodeMemory32(Core::System& system, Handle code_memory_handle, u32 operation,
- u64 address, u64 size, Svc::MemoryPermission perm) {
- return ControlCodeMemory(system, code_memory_handle, operation, address, size, perm);
+static void SvcWrap_DebugActiveProcess64From32(Core::System& system) {
+ Result ret{};
+
+ Handle out_handle{};
+ uint64_t process_id{};
+
+ std::array<uint32_t, 2> process_id_gather{};
+ process_id_gather[0] = GetReg32(system, 2);
+ process_id_gather[1] = GetReg32(system, 3);
+ process_id = Convert<uint64_t>(process_id_gather);
+
+ ret = DebugActiveProcess64From32(system, &out_handle, process_id);
+
+ SetReg32(system, 0, Convert<uint32_t>(ret));
+ SetReg32(system, 1, Convert<uint32_t>(out_handle));
}
-static Result QueryProcessMemory(Core::System& system, VAddr memory_info_address,
- VAddr page_info_address, Handle process_handle, VAddr address) {
- LOG_TRACE(Kernel_SVC, "called process=0x{:08X} address={:X}", process_handle, address);
- const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable();
- KScopedAutoObject process = handle_table.GetObject<KProcess>(process_handle);
- if (process.IsNull()) {
- LOG_ERROR(Kernel_SVC, "Process handle does not exist, process_handle=0x{:08X}",
- process_handle);
- return ResultInvalidHandle;
- }
+static void SvcWrap_BreakDebugProcess64From32(Core::System& system) {
+ Result ret{};
- auto& memory{system.Memory()};
- const auto memory_info{process->PageTable().QueryInfo(address).GetSvcMemoryInfo()};
+ Handle debug_handle{};
- memory.Write64(memory_info_address + 0x00, memory_info.base_address);
- memory.Write64(memory_info_address + 0x08, memory_info.size);
- memory.Write32(memory_info_address + 0x10, static_cast<u32>(memory_info.state) & 0xff);
- memory.Write32(memory_info_address + 0x14, static_cast<u32>(memory_info.attribute));
- memory.Write32(memory_info_address + 0x18, static_cast<u32>(memory_info.permission));
- memory.Write32(memory_info_address + 0x1c, memory_info.ipc_count);
- memory.Write32(memory_info_address + 0x20, memory_info.device_count);
- memory.Write32(memory_info_address + 0x24, 0);
+ debug_handle = Convert<Handle>(GetReg32(system, 0));
- // Page info appears to be currently unused by the kernel and is always set to zero.
- memory.Write32(page_info_address, 0);
+ ret = BreakDebugProcess64From32(system, debug_handle);
- return ResultSuccess;
+ SetReg32(system, 0, Convert<uint32_t>(ret));
}
-static Result QueryMemory(Core::System& system, VAddr memory_info_address, VAddr page_info_address,
- VAddr query_address) {
- LOG_TRACE(Kernel_SVC,
- "called, memory_info_address=0x{:016X}, page_info_address=0x{:016X}, "
- "query_address=0x{:016X}",
- memory_info_address, page_info_address, query_address);
+static void SvcWrap_TerminateDebugProcess64From32(Core::System& system) {
+ Result ret{};
+
+ Handle debug_handle{};
+
+ debug_handle = Convert<Handle>(GetReg32(system, 0));
+
+ ret = TerminateDebugProcess64From32(system, debug_handle);
- return QueryProcessMemory(system, memory_info_address, page_info_address, CurrentProcess,
- query_address);
+ SetReg32(system, 0, Convert<uint32_t>(ret));
}
-static Result QueryMemory32(Core::System& system, u32 memory_info_address, u32 page_info_address,
- u32 query_address) {
- return QueryMemory(system, memory_info_address, page_info_address, query_address);
+static void SvcWrap_GetDebugEvent64From32(Core::System& system) {
+ Result ret{};
+
+ uint32_t out_info{};
+ Handle debug_handle{};
+
+ out_info = Convert<uint32_t>(GetReg32(system, 0));
+ debug_handle = Convert<Handle>(GetReg32(system, 1));
+
+ ret = GetDebugEvent64From32(system, out_info, debug_handle);
+
+ SetReg32(system, 0, Convert<uint32_t>(ret));
}
-static Result MapProcessCodeMemory(Core::System& system, Handle process_handle, u64 dst_address,
- u64 src_address, u64 size) {
- LOG_DEBUG(Kernel_SVC,
- "called. process_handle=0x{:08X}, dst_address=0x{:016X}, "
- "src_address=0x{:016X}, size=0x{:016X}",
- process_handle, dst_address, src_address, size);
+static void SvcWrap_ContinueDebugEvent64From32(Core::System& system) {
+ Result ret{};
- if (!Common::Is4KBAligned(src_address)) {
- LOG_ERROR(Kernel_SVC, "src_address is not page-aligned (src_address=0x{:016X}).",
- src_address);
- return ResultInvalidAddress;
- }
+ Handle debug_handle{};
+ uint32_t flags{};
+ uint32_t thread_ids{};
+ int32_t num_thread_ids{};
- if (!Common::Is4KBAligned(dst_address)) {
- LOG_ERROR(Kernel_SVC, "dst_address is not page-aligned (dst_address=0x{:016X}).",
- dst_address);
- return ResultInvalidAddress;
- }
+ debug_handle = Convert<Handle>(GetReg32(system, 0));
+ flags = Convert<uint32_t>(GetReg32(system, 1));
+ thread_ids = Convert<uint32_t>(GetReg32(system, 2));
+ num_thread_ids = Convert<int32_t>(GetReg32(system, 3));
- if (size == 0 || !Common::Is4KBAligned(size)) {
- LOG_ERROR(Kernel_SVC, "Size is zero or not page-aligned (size=0x{:016X})", size);
- return ResultInvalidSize;
- }
+ ret = ContinueDebugEvent64From32(system, debug_handle, flags, thread_ids, num_thread_ids);
- if (!IsValidAddressRange(dst_address, size)) {
- LOG_ERROR(Kernel_SVC,
- "Destination address range overflows the address space (dst_address=0x{:016X}, "
- "size=0x{:016X}).",
- dst_address, size);
- return ResultInvalidCurrentMemory;
- }
+ SetReg32(system, 0, Convert<uint32_t>(ret));
+}
- if (!IsValidAddressRange(src_address, size)) {
- LOG_ERROR(Kernel_SVC,
- "Source address range overflows the address space (src_address=0x{:016X}, "
- "size=0x{:016X}).",
- src_address, size);
- return ResultInvalidCurrentMemory;
- }
+static void SvcWrap_GetProcessList64From32(Core::System& system) {
+ Result ret{};
- const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable();
- KScopedAutoObject process = handle_table.GetObject<KProcess>(process_handle);
- if (process.IsNull()) {
- LOG_ERROR(Kernel_SVC, "Invalid process handle specified (handle=0x{:08X}).",
- process_handle);
- return ResultInvalidHandle;
- }
+ int32_t out_num_processes{};
+ uint32_t out_process_ids{};
+ int32_t max_out_count{};
- auto& page_table = process->PageTable();
- if (!page_table.IsInsideAddressSpace(src_address, size)) {
- LOG_ERROR(Kernel_SVC,
- "Source address range is not within the address space (src_address=0x{:016X}, "
- "size=0x{:016X}).",
- src_address, size);
- return ResultInvalidCurrentMemory;
- }
+ out_process_ids = Convert<uint32_t>(GetReg32(system, 1));
+ max_out_count = Convert<int32_t>(GetReg32(system, 2));
- if (!page_table.IsInsideASLRRegion(dst_address, size)) {
- LOG_ERROR(Kernel_SVC,
- "Destination address range is not within the ASLR region (dst_address=0x{:016X}, "
- "size=0x{:016X}).",
- dst_address, size);
- return ResultInvalidMemoryRegion;
- }
+ ret = GetProcessList64From32(system, &out_num_processes, out_process_ids, max_out_count);
- return page_table.MapCodeMemory(dst_address, src_address, size);
+ SetReg32(system, 0, Convert<uint32_t>(ret));
+ SetReg32(system, 1, Convert<uint32_t>(out_num_processes));
}
-static Result UnmapProcessCodeMemory(Core::System& system, Handle process_handle, u64 dst_address,
- u64 src_address, u64 size) {
- LOG_DEBUG(Kernel_SVC,
- "called. process_handle=0x{:08X}, dst_address=0x{:016X}, src_address=0x{:016X}, "
- "size=0x{:016X}",
- process_handle, dst_address, src_address, size);
+static void SvcWrap_GetThreadList64From32(Core::System& system) {
+ Result ret{};
- if (!Common::Is4KBAligned(dst_address)) {
- LOG_ERROR(Kernel_SVC, "dst_address is not page-aligned (dst_address=0x{:016X}).",
- dst_address);
- return ResultInvalidAddress;
- }
+ int32_t out_num_threads{};
+ uint32_t out_thread_ids{};
+ int32_t max_out_count{};
+ Handle debug_handle{};
- if (!Common::Is4KBAligned(src_address)) {
- LOG_ERROR(Kernel_SVC, "src_address is not page-aligned (src_address=0x{:016X}).",
- src_address);
- return ResultInvalidAddress;
- }
+ out_thread_ids = Convert<uint32_t>(GetReg32(system, 1));
+ max_out_count = Convert<int32_t>(GetReg32(system, 2));
+ debug_handle = Convert<Handle>(GetReg32(system, 3));
- if (size == 0 || !Common::Is4KBAligned(size)) {
- LOG_ERROR(Kernel_SVC, "Size is zero or not page-aligned (size=0x{:016X}).", size);
- return ResultInvalidSize;
- }
+ ret = GetThreadList64From32(system, &out_num_threads, out_thread_ids, max_out_count, debug_handle);
- if (!IsValidAddressRange(dst_address, size)) {
- LOG_ERROR(Kernel_SVC,
- "Destination address range overflows the address space (dst_address=0x{:016X}, "
- "size=0x{:016X}).",
- dst_address, size);
- return ResultInvalidCurrentMemory;
- }
+ SetReg32(system, 0, Convert<uint32_t>(ret));
+ SetReg32(system, 1, Convert<uint32_t>(out_num_threads));
+}
- if (!IsValidAddressRange(src_address, size)) {
- LOG_ERROR(Kernel_SVC,
- "Source address range overflows the address space (src_address=0x{:016X}, "
- "size=0x{:016X}).",
- src_address, size);
- return ResultInvalidCurrentMemory;
- }
+static void SvcWrap_GetDebugThreadContext64From32(Core::System& system) {
+ Result ret{};
- const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable();
- KScopedAutoObject process = handle_table.GetObject<KProcess>(process_handle);
- if (process.IsNull()) {
- LOG_ERROR(Kernel_SVC, "Invalid process handle specified (handle=0x{:08X}).",
- process_handle);
- return ResultInvalidHandle;
- }
+ uint32_t out_context{};
+ Handle debug_handle{};
+ uint64_t thread_id{};
+ uint32_t context_flags{};
- auto& page_table = process->PageTable();
- if (!page_table.IsInsideAddressSpace(src_address, size)) {
- LOG_ERROR(Kernel_SVC,
- "Source address range is not within the address space (src_address=0x{:016X}, "
- "size=0x{:016X}).",
- src_address, size);
- return ResultInvalidCurrentMemory;
- }
+ out_context = Convert<uint32_t>(GetReg32(system, 0));
+ debug_handle = Convert<Handle>(GetReg32(system, 1));
+ std::array<uint32_t, 2> thread_id_gather{};
+ thread_id_gather[0] = GetReg32(system, 2);
+ thread_id_gather[1] = GetReg32(system, 3);
+ thread_id = Convert<uint64_t>(thread_id_gather);
+ context_flags = Convert<uint32_t>(GetReg32(system, 4));
- if (!page_table.IsInsideASLRRegion(dst_address, size)) {
- LOG_ERROR(Kernel_SVC,
- "Destination address range is not within the ASLR region (dst_address=0x{:016X}, "
- "size=0x{:016X}).",
- dst_address, size);
- return ResultInvalidMemoryRegion;
- }
+ ret = GetDebugThreadContext64From32(system, out_context, debug_handle, thread_id, context_flags);
- return page_table.UnmapCodeMemory(dst_address, src_address, size,
- KPageTable::ICacheInvalidationStrategy::InvalidateAll);
+ SetReg32(system, 0, Convert<uint32_t>(ret));
}
-/// Exits the current process
-static void ExitProcess(Core::System& system) {
- auto* current_process = system.Kernel().CurrentProcess();
+static void SvcWrap_SetDebugThreadContext64From32(Core::System& system) {
+ Result ret{};
+
+ Handle debug_handle{};
+ uint64_t thread_id{};
+ uint32_t context{};
+ uint32_t context_flags{};
- LOG_INFO(Kernel_SVC, "Process {} exiting", current_process->GetProcessID());
- ASSERT_MSG(current_process->GetState() == KProcess::State::Running,
- "Process has already exited");
+ debug_handle = Convert<Handle>(GetReg32(system, 0));
+ std::array<uint32_t, 2> thread_id_gather{};
+ thread_id_gather[0] = GetReg32(system, 2);
+ thread_id_gather[1] = GetReg32(system, 3);
+ thread_id = Convert<uint64_t>(thread_id_gather);
+ context = Convert<uint32_t>(GetReg32(system, 1));
+ context_flags = Convert<uint32_t>(GetReg32(system, 4));
- system.Exit();
+ ret = SetDebugThreadContext64From32(system, debug_handle, thread_id, context, context_flags);
+
+ SetReg32(system, 0, Convert<uint32_t>(ret));
}
-static void ExitProcess32(Core::System& system) {
- ExitProcess(system);
+static void SvcWrap_QueryDebugProcessMemory64From32(Core::System& system) {
+ Result ret{};
+
+ PageInfo out_page_info{};
+ uint32_t out_memory_info{};
+ Handle process_handle{};
+ uint32_t address{};
+
+ out_memory_info = Convert<uint32_t>(GetReg32(system, 0));
+ process_handle = Convert<Handle>(GetReg32(system, 2));
+ address = Convert<uint32_t>(GetReg32(system, 3));
+
+ ret = QueryDebugProcessMemory64From32(system, out_memory_info, &out_page_info, process_handle, address);
+
+ SetReg32(system, 0, Convert<uint32_t>(ret));
+ SetReg32(system, 1, Convert<uint32_t>(out_page_info));
}
-namespace {
+static void SvcWrap_ReadDebugProcessMemory64From32(Core::System& system) {
+ Result ret{};
+
+ uint32_t buffer{};
+ Handle debug_handle{};
+ uint32_t address{};
+ uint32_t size{};
+
+ buffer = Convert<uint32_t>(GetReg32(system, 0));
+ debug_handle = Convert<Handle>(GetReg32(system, 1));
+ address = Convert<uint32_t>(GetReg32(system, 2));
+ size = Convert<uint32_t>(GetReg32(system, 3));
-constexpr bool IsValidVirtualCoreId(int32_t core_id) {
- return (0 <= core_id && core_id < static_cast<int32_t>(Core::Hardware::NUM_CPU_CORES));
+ ret = ReadDebugProcessMemory64From32(system, buffer, debug_handle, address, size);
+
+ SetReg32(system, 0, Convert<uint32_t>(ret));
}
-} // Anonymous namespace
+static void SvcWrap_WriteDebugProcessMemory64From32(Core::System& system) {
+ Result ret{};
-/// Creates a new thread
-static Result CreateThread(Core::System& system, Handle* out_handle, VAddr entry_point, u64 arg,
- VAddr stack_bottom, u32 priority, s32 core_id) {
- LOG_DEBUG(Kernel_SVC,
- "called entry_point=0x{:08X}, arg=0x{:08X}, stack_bottom=0x{:08X}, "
- "priority=0x{:08X}, core_id=0x{:08X}",
- entry_point, arg, stack_bottom, priority, core_id);
+ Handle debug_handle{};
+ uint32_t buffer{};
+ uint32_t address{};
+ uint32_t size{};
- // Adjust core id, if it's the default magic.
- auto& kernel = system.Kernel();
- auto& process = *kernel.CurrentProcess();
- if (core_id == IdealCoreUseProcessValue) {
- core_id = process.GetIdealCoreId();
- }
+ debug_handle = Convert<Handle>(GetReg32(system, 0));
+ buffer = Convert<uint32_t>(GetReg32(system, 1));
+ address = Convert<uint32_t>(GetReg32(system, 2));
+ size = Convert<uint32_t>(GetReg32(system, 3));
- // Validate arguments.
- if (!IsValidVirtualCoreId(core_id)) {
- LOG_ERROR(Kernel_SVC, "Invalid Core ID specified (id={})", core_id);
- return ResultInvalidCoreId;
- }
- if (((1ULL << core_id) & process.GetCoreMask()) == 0) {
- LOG_ERROR(Kernel_SVC, "Core ID doesn't fall within allowable cores (id={})", core_id);
- return ResultInvalidCoreId;
- }
+ ret = WriteDebugProcessMemory64From32(system, debug_handle, buffer, address, size);
- if (HighestThreadPriority > priority || priority > LowestThreadPriority) {
- LOG_ERROR(Kernel_SVC, "Invalid priority specified (priority={})", priority);
- return ResultInvalidPriority;
- }
- if (!process.CheckThreadPriority(priority)) {
- LOG_ERROR(Kernel_SVC, "Invalid allowable thread priority (priority={})", priority);
- return ResultInvalidPriority;
- }
+ SetReg32(system, 0, Convert<uint32_t>(ret));
+}
- // Reserve a new thread from the process resource limit (waiting up to 100ms).
- KScopedResourceReservation thread_reservation(
- kernel.CurrentProcess(), LimitableResource::ThreadCountMax, 1,
- system.CoreTiming().GetGlobalTimeNs().count() + 100000000);
- if (!thread_reservation.Succeeded()) {
- LOG_ERROR(Kernel_SVC, "Could not reserve a new thread");
- return ResultLimitReached;
- }
+static void SvcWrap_SetHardwareBreakPoint64From32(Core::System& system) {
+ Result ret{};
- // Create the thread.
- KThread* thread = KThread::Create(kernel);
- if (!thread) {
- LOG_ERROR(Kernel_SVC, "Unable to create new threads. Thread creation limit reached.");
- return ResultOutOfResource;
- }
- SCOPE_EXIT({ thread->Close(); });
+ HardwareBreakPointRegisterName name{};
+ uint64_t flags{};
+ uint64_t value{};
- // Initialize the thread.
- {
- KScopedLightLock lk{process.GetStateLock()};
- R_TRY(KThread::InitializeUserThread(system, thread, entry_point, arg, stack_bottom,
- priority, core_id, &process));
- }
+ name = Convert<HardwareBreakPointRegisterName>(GetReg32(system, 0));
+ std::array<uint32_t, 2> flags_gather{};
+ flags_gather[0] = GetReg32(system, 2);
+ flags_gather[1] = GetReg32(system, 3);
+ flags = Convert<uint64_t>(flags_gather);
+ std::array<uint32_t, 2> value_gather{};
+ value_gather[0] = GetReg32(system, 1);
+ value_gather[1] = GetReg32(system, 4);
+ value = Convert<uint64_t>(value_gather);
- // Set the thread name for debugging purposes.
- thread->SetName(fmt::format("thread[entry_point={:X}, handle={:X}]", entry_point, *out_handle));
+ ret = SetHardwareBreakPoint64From32(system, name, flags, value);
- // Commit the thread reservation.
- thread_reservation.Commit();
+ SetReg32(system, 0, Convert<uint32_t>(ret));
+}
- // Register the new thread.
- KThread::Register(kernel, thread);
+static void SvcWrap_GetDebugThreadParam64From32(Core::System& system) {
+ Result ret{};
- // Add the thread to the handle table.
- R_TRY(process.GetHandleTable().Add(out_handle, thread));
+ uint64_t out_64{};
+ uint32_t out_32{};
+ Handle debug_handle{};
+ uint64_t thread_id{};
+ DebugThreadParam param{};
- return ResultSuccess;
+ debug_handle = Convert<Handle>(GetReg32(system, 2));
+ std::array<uint32_t, 2> thread_id_gather{};
+ thread_id_gather[0] = GetReg32(system, 0);
+ thread_id_gather[1] = GetReg32(system, 1);
+ thread_id = Convert<uint64_t>(thread_id_gather);
+ param = Convert<DebugThreadParam>(GetReg32(system, 3));
+
+ ret = GetDebugThreadParam64From32(system, &out_64, &out_32, debug_handle, thread_id, param);
+
+ SetReg32(system, 0, Convert<uint32_t>(ret));
+ auto out_64_scatter = Convert<std::array<uint32_t, 2>>(out_64);
+ SetReg32(system, 1, out_64_scatter[0]);
+ SetReg32(system, 2, out_64_scatter[1]);
+ SetReg32(system, 3, Convert<uint32_t>(out_32));
}
-static Result CreateThread32(Core::System& system, Handle* out_handle, u32 priority,
- u32 entry_point, u32 arg, u32 stack_top, s32 processor_id) {
- return CreateThread(system, out_handle, entry_point, arg, stack_top, priority, processor_id);
+static void SvcWrap_GetSystemInfo64From32(Core::System& system) {
+ Result ret{};
+
+ uint64_t out{};
+ SystemInfoType info_type{};
+ Handle handle{};
+ uint64_t info_subtype{};
+
+ info_type = Convert<SystemInfoType>(GetReg32(system, 1));
+ handle = Convert<Handle>(GetReg32(system, 2));
+ std::array<uint32_t, 2> info_subtype_gather{};
+ info_subtype_gather[0] = GetReg32(system, 0);
+ info_subtype_gather[1] = GetReg32(system, 3);
+ info_subtype = Convert<uint64_t>(info_subtype_gather);
+
+ ret = GetSystemInfo64From32(system, &out, info_type, handle, info_subtype);
+
+ SetReg32(system, 0, Convert<uint32_t>(ret));
+ auto out_scatter = Convert<std::array<uint32_t, 2>>(out);
+ SetReg32(system, 1, out_scatter[0]);
+ SetReg32(system, 2, out_scatter[1]);
}
-/// Starts the thread for the provided handle
-static Result StartThread(Core::System& system, Handle thread_handle) {
- LOG_DEBUG(Kernel_SVC, "called thread=0x{:08X}", thread_handle);
+static void SvcWrap_CreatePort64From32(Core::System& system) {
+ Result ret{};
- // Get the thread from its handle.
- KScopedAutoObject thread =
- system.Kernel().CurrentProcess()->GetHandleTable().GetObject<KThread>(thread_handle);
- R_UNLESS(thread.IsNotNull(), ResultInvalidHandle);
+ Handle out_server_handle{};
+ Handle out_client_handle{};
+ int32_t max_sessions{};
+ bool is_light{};
+ uint32_t name{};
- // Try to start the thread.
- R_TRY(thread->Run());
+ max_sessions = Convert<int32_t>(GetReg32(system, 2));
+ is_light = Convert<bool>(GetReg32(system, 3));
+ name = Convert<uint32_t>(GetReg32(system, 0));
- // If we succeeded, persist a reference to the thread.
- thread->Open();
- system.Kernel().RegisterInUseObject(thread.GetPointerUnsafe());
+ ret = CreatePort64From32(system, &out_server_handle, &out_client_handle, max_sessions, is_light, name);
- return ResultSuccess;
+ SetReg32(system, 0, Convert<uint32_t>(ret));
+ SetReg32(system, 1, Convert<uint32_t>(out_server_handle));
+ SetReg32(system, 2, Convert<uint32_t>(out_client_handle));
}
-static Result StartThread32(Core::System& system, Handle thread_handle) {
- return StartThread(system, thread_handle);
+static void SvcWrap_ManageNamedPort64From32(Core::System& system) {
+ Result ret{};
+
+ Handle out_server_handle{};
+ uint32_t name{};
+ int32_t max_sessions{};
+
+ name = Convert<uint32_t>(GetReg32(system, 1));
+ max_sessions = Convert<int32_t>(GetReg32(system, 2));
+
+ ret = ManageNamedPort64From32(system, &out_server_handle, name, max_sessions);
+
+ SetReg32(system, 0, Convert<uint32_t>(ret));
+ SetReg32(system, 1, Convert<uint32_t>(out_server_handle));
}
-/// Called when a thread exits
-static void ExitThread(Core::System& system) {
- LOG_DEBUG(Kernel_SVC, "called, pc=0x{:08X}", system.CurrentArmInterface().GetPC());
+static void SvcWrap_ConnectToPort64From32(Core::System& system) {
+ Result ret{};
+
+ Handle out_handle{};
+ Handle port{};
- auto* const current_thread = GetCurrentThreadPointer(system.Kernel());
- system.GlobalSchedulerContext().RemoveThread(current_thread);
- current_thread->Exit();
- system.Kernel().UnregisterInUseObject(current_thread);
+ port = Convert<Handle>(GetReg32(system, 1));
+
+ ret = ConnectToPort64From32(system, &out_handle, port);
+
+ SetReg32(system, 0, Convert<uint32_t>(ret));
+ SetReg32(system, 1, Convert<uint32_t>(out_handle));
}
-static void ExitThread32(Core::System& system) {
- ExitThread(system);
+static void SvcWrap_SetProcessMemoryPermission64From32(Core::System& system) {
+ Result ret{};
+
+ Handle process_handle{};
+ uint64_t address{};
+ uint64_t size{};
+ MemoryPermission perm{};
+
+ process_handle = Convert<Handle>(GetReg32(system, 0));
+ std::array<uint32_t, 2> address_gather{};
+ address_gather[0] = GetReg32(system, 2);
+ address_gather[1] = GetReg32(system, 3);
+ address = Convert<uint64_t>(address_gather);
+ std::array<uint32_t, 2> size_gather{};
+ size_gather[0] = GetReg32(system, 1);
+ size_gather[1] = GetReg32(system, 4);
+ size = Convert<uint64_t>(size_gather);
+ perm = Convert<MemoryPermission>(GetReg32(system, 5));
+
+ ret = SetProcessMemoryPermission64From32(system, process_handle, address, size, perm);
+
+ SetReg32(system, 0, Convert<uint32_t>(ret));
}
-/// Sleep the current thread
-static void SleepThread(Core::System& system, s64 nanoseconds) {
- auto& kernel = system.Kernel();
- const auto yield_type = static_cast<Svc::YieldType>(nanoseconds);
-
- LOG_TRACE(Kernel_SVC, "called nanoseconds={}", nanoseconds);
-
- // When the input tick is positive, sleep.
- if (nanoseconds > 0) {
- // Convert the timeout from nanoseconds to ticks.
- // NOTE: Nintendo does not use this conversion logic in WaitSynchronization...
-
- // Sleep.
- // NOTE: Nintendo does not check the result of this sleep.
- static_cast<void>(GetCurrentThread(kernel).Sleep(nanoseconds));
- } else if (yield_type == Svc::YieldType::WithoutCoreMigration) {
- KScheduler::YieldWithoutCoreMigration(kernel);
- } else if (yield_type == Svc::YieldType::WithCoreMigration) {
- KScheduler::YieldWithCoreMigration(kernel);
- } else if (yield_type == Svc::YieldType::ToAnyThread) {
- KScheduler::YieldToAnyThread(kernel);
- } else {
- // Nintendo does nothing at all if an otherwise invalid value is passed.
- ASSERT_MSG(false, "Unimplemented sleep yield type '{:016X}'!", nanoseconds);
- }
+static void SvcWrap_MapProcessMemory64From32(Core::System& system) {
+ Result ret{};
+
+ uint32_t dst_address{};
+ Handle process_handle{};
+ uint64_t src_address{};
+ uint32_t size{};
+
+ dst_address = Convert<uint32_t>(GetReg32(system, 0));
+ process_handle = Convert<Handle>(GetReg32(system, 1));
+ std::array<uint32_t, 2> src_address_gather{};
+ src_address_gather[0] = GetReg32(system, 2);
+ src_address_gather[1] = GetReg32(system, 3);
+ src_address = Convert<uint64_t>(src_address_gather);
+ size = Convert<uint32_t>(GetReg32(system, 4));
+
+ ret = MapProcessMemory64From32(system, dst_address, process_handle, src_address, size);
+
+ SetReg32(system, 0, Convert<uint32_t>(ret));
}
-static void SleepThread32(Core::System& system, u32 nanoseconds_low, u32 nanoseconds_high) {
- const auto nanoseconds = static_cast<s64>(u64{nanoseconds_low} | (u64{nanoseconds_high} << 32));
- SleepThread(system, nanoseconds);
+static void SvcWrap_UnmapProcessMemory64From32(Core::System& system) {
+ Result ret{};
+
+ uint32_t dst_address{};
+ Handle process_handle{};
+ uint64_t src_address{};
+ uint32_t size{};
+
+ dst_address = Convert<uint32_t>(GetReg32(system, 0));
+ process_handle = Convert<Handle>(GetReg32(system, 1));
+ std::array<uint32_t, 2> src_address_gather{};
+ src_address_gather[0] = GetReg32(system, 2);
+ src_address_gather[1] = GetReg32(system, 3);
+ src_address = Convert<uint64_t>(src_address_gather);
+ size = Convert<uint32_t>(GetReg32(system, 4));
+
+ ret = UnmapProcessMemory64From32(system, dst_address, process_handle, src_address, size);
+
+ SetReg32(system, 0, Convert<uint32_t>(ret));
}
-/// Wait process wide key atomic
-static Result WaitProcessWideKeyAtomic(Core::System& system, VAddr address, VAddr cv_key, u32 tag,
- s64 timeout_ns) {
- LOG_TRACE(Kernel_SVC, "called address={:X}, cv_key={:X}, tag=0x{:08X}, timeout_ns={}", address,
- cv_key, tag, timeout_ns);
+static void SvcWrap_QueryProcessMemory64From32(Core::System& system) {
+ Result ret{};
- // Validate input.
- if (IsKernelAddress(address)) {
- LOG_ERROR(Kernel_SVC, "Attempted to wait on kernel address (address={:08X})", address);
- return ResultInvalidCurrentMemory;
- }
- if (!Common::IsAligned(address, sizeof(s32))) {
- LOG_ERROR(Kernel_SVC, "Address must be 4 byte aligned (address={:08X})", address);
- return ResultInvalidAddress;
- }
+ PageInfo out_page_info{};
+ uint32_t out_memory_info{};
+ Handle process_handle{};
+ uint64_t address{};
- // Convert timeout from nanoseconds to ticks.
- s64 timeout{};
- if (timeout_ns > 0) {
- const s64 offset_tick(timeout_ns);
- if (offset_tick > 0) {
- timeout = offset_tick + 2;
- if (timeout <= 0) {
- timeout = std::numeric_limits<s64>::max();
- }
- } else {
- timeout = std::numeric_limits<s64>::max();
- }
- } else {
- timeout = timeout_ns;
- }
+ out_memory_info = Convert<uint32_t>(GetReg32(system, 0));
+ process_handle = Convert<Handle>(GetReg32(system, 2));
+ std::array<uint32_t, 2> address_gather{};
+ address_gather[0] = GetReg32(system, 1);
+ address_gather[1] = GetReg32(system, 3);
+ address = Convert<uint64_t>(address_gather);
+
+ ret = QueryProcessMemory64From32(system, out_memory_info, &out_page_info, process_handle, address);
- // Wait on the condition variable.
- return system.Kernel().CurrentProcess()->WaitConditionVariable(
- address, Common::AlignDown(cv_key, sizeof(u32)), tag, timeout);
+ SetReg32(system, 0, Convert<uint32_t>(ret));
+ SetReg32(system, 1, Convert<uint32_t>(out_page_info));
}
-static Result WaitProcessWideKeyAtomic32(Core::System& system, u32 address, u32 cv_key, u32 tag,
- u32 timeout_ns_low, u32 timeout_ns_high) {
- const auto timeout_ns = static_cast<s64>(timeout_ns_low | (u64{timeout_ns_high} << 32));
- return WaitProcessWideKeyAtomic(system, address, cv_key, tag, timeout_ns);
+static void SvcWrap_MapProcessCodeMemory64From32(Core::System& system) {
+ Result ret{};
+
+ Handle process_handle{};
+ uint64_t dst_address{};
+ uint64_t src_address{};
+ uint64_t size{};
+
+ process_handle = Convert<Handle>(GetReg32(system, 0));
+ std::array<uint32_t, 2> dst_address_gather{};
+ dst_address_gather[0] = GetReg32(system, 2);
+ dst_address_gather[1] = GetReg32(system, 3);
+ dst_address = Convert<uint64_t>(dst_address_gather);
+ std::array<uint32_t, 2> src_address_gather{};
+ src_address_gather[0] = GetReg32(system, 1);
+ src_address_gather[1] = GetReg32(system, 4);
+ src_address = Convert<uint64_t>(src_address_gather);
+ std::array<uint32_t, 2> size_gather{};
+ size_gather[0] = GetReg32(system, 5);
+ size_gather[1] = GetReg32(system, 6);
+ size = Convert<uint64_t>(size_gather);
+
+ ret = MapProcessCodeMemory64From32(system, process_handle, dst_address, src_address, size);
+
+ SetReg32(system, 0, Convert<uint32_t>(ret));
}
-/// Signal process wide key
-static void SignalProcessWideKey(Core::System& system, VAddr cv_key, s32 count) {
- LOG_TRACE(Kernel_SVC, "called, cv_key=0x{:X}, count=0x{:08X}", cv_key, count);
+static void SvcWrap_UnmapProcessCodeMemory64From32(Core::System& system) {
+ Result ret{};
- // Signal the condition variable.
- return system.Kernel().CurrentProcess()->SignalConditionVariable(
- Common::AlignDown(cv_key, sizeof(u32)), count);
+ Handle process_handle{};
+ uint64_t dst_address{};
+ uint64_t src_address{};
+ uint64_t size{};
+
+ process_handle = Convert<Handle>(GetReg32(system, 0));
+ std::array<uint32_t, 2> dst_address_gather{};
+ dst_address_gather[0] = GetReg32(system, 2);
+ dst_address_gather[1] = GetReg32(system, 3);
+ dst_address = Convert<uint64_t>(dst_address_gather);
+ std::array<uint32_t, 2> src_address_gather{};
+ src_address_gather[0] = GetReg32(system, 1);
+ src_address_gather[1] = GetReg32(system, 4);
+ src_address = Convert<uint64_t>(src_address_gather);
+ std::array<uint32_t, 2> size_gather{};
+ size_gather[0] = GetReg32(system, 5);
+ size_gather[1] = GetReg32(system, 6);
+ size = Convert<uint64_t>(size_gather);
+
+ ret = UnmapProcessCodeMemory64From32(system, process_handle, dst_address, src_address, size);
+
+ SetReg32(system, 0, Convert<uint32_t>(ret));
}
-static void SignalProcessWideKey32(Core::System& system, u32 cv_key, s32 count) {
- SignalProcessWideKey(system, cv_key, count);
+static void SvcWrap_CreateProcess64From32(Core::System& system) {
+ Result ret{};
+
+ Handle out_handle{};
+ uint32_t parameters{};
+ uint32_t caps{};
+ int32_t num_caps{};
+
+ parameters = Convert<uint32_t>(GetReg32(system, 1));
+ caps = Convert<uint32_t>(GetReg32(system, 2));
+ num_caps = Convert<int32_t>(GetReg32(system, 3));
+
+ ret = CreateProcess64From32(system, &out_handle, parameters, caps, num_caps);
+
+ SetReg32(system, 0, Convert<uint32_t>(ret));
+ SetReg32(system, 1, Convert<uint32_t>(out_handle));
}
-namespace {
+static void SvcWrap_StartProcess64From32(Core::System& system) {
+ Result ret{};
-constexpr bool IsValidSignalType(Svc::SignalType type) {
- switch (type) {
- case Svc::SignalType::Signal:
- case Svc::SignalType::SignalAndIncrementIfEqual:
- case Svc::SignalType::SignalAndModifyByWaitingCountIfEqual:
- return true;
- default:
- return false;
- }
+ Handle process_handle{};
+ int32_t priority{};
+ int32_t core_id{};
+ uint64_t main_thread_stack_size{};
+
+ process_handle = Convert<Handle>(GetReg32(system, 0));
+ priority = Convert<int32_t>(GetReg32(system, 1));
+ core_id = Convert<int32_t>(GetReg32(system, 2));
+ std::array<uint32_t, 2> main_thread_stack_size_gather{};
+ main_thread_stack_size_gather[0] = GetReg32(system, 3);
+ main_thread_stack_size_gather[1] = GetReg32(system, 4);
+ main_thread_stack_size = Convert<uint64_t>(main_thread_stack_size_gather);
+
+ ret = StartProcess64From32(system, process_handle, priority, core_id, main_thread_stack_size);
+
+ SetReg32(system, 0, Convert<uint32_t>(ret));
}
-constexpr bool IsValidArbitrationType(Svc::ArbitrationType type) {
- switch (type) {
- case Svc::ArbitrationType::WaitIfLessThan:
- case Svc::ArbitrationType::DecrementAndWaitIfLessThan:
- case Svc::ArbitrationType::WaitIfEqual:
- return true;
- default:
- return false;
- }
+static void SvcWrap_TerminateProcess64From32(Core::System& system) {
+ Result ret{};
+
+ Handle process_handle{};
+
+ process_handle = Convert<Handle>(GetReg32(system, 0));
+
+ ret = TerminateProcess64From32(system, process_handle);
+
+ SetReg32(system, 0, Convert<uint32_t>(ret));
}
-} // namespace
+static void SvcWrap_GetProcessInfo64From32(Core::System& system) {
+ Result ret{};
-// Wait for an address (via Address Arbiter)
-static Result WaitForAddress(Core::System& system, VAddr address, Svc::ArbitrationType arb_type,
- s32 value, s64 timeout_ns) {
- LOG_TRACE(Kernel_SVC, "called, address=0x{:X}, arb_type=0x{:X}, value=0x{:X}, timeout_ns={}",
- address, arb_type, value, timeout_ns);
+ int64_t out_info{};
+ Handle process_handle{};
+ ProcessInfoType info_type{};
- // Validate input.
- if (IsKernelAddress(address)) {
- LOG_ERROR(Kernel_SVC, "Attempting to wait on kernel address (address={:08X})", address);
- return ResultInvalidCurrentMemory;
- }
- if (!Common::IsAligned(address, sizeof(s32))) {
- LOG_ERROR(Kernel_SVC, "Wait address must be 4 byte aligned (address={:08X})", address);
- return ResultInvalidAddress;
- }
- if (!IsValidArbitrationType(arb_type)) {
- LOG_ERROR(Kernel_SVC, "Invalid arbitration type specified (type={})", arb_type);
- return ResultInvalidEnumValue;
- }
+ process_handle = Convert<Handle>(GetReg32(system, 1));
+ info_type = Convert<ProcessInfoType>(GetReg32(system, 2));
- // Convert timeout from nanoseconds to ticks.
- s64 timeout{};
- if (timeout_ns > 0) {
- const s64 offset_tick(timeout_ns);
- if (offset_tick > 0) {
- timeout = offset_tick + 2;
- if (timeout <= 0) {
- timeout = std::numeric_limits<s64>::max();
- }
- } else {
- timeout = std::numeric_limits<s64>::max();
- }
- } else {
- timeout = timeout_ns;
- }
+ ret = GetProcessInfo64From32(system, &out_info, process_handle, info_type);
+
+ SetReg32(system, 0, Convert<uint32_t>(ret));
+ auto out_info_scatter = Convert<std::array<uint32_t, 2>>(out_info);
+ SetReg32(system, 1, out_info_scatter[0]);
+ SetReg32(system, 2, out_info_scatter[1]);
+}
+
+static void SvcWrap_CreateResourceLimit64From32(Core::System& system) {
+ Result ret{};
- return system.Kernel().CurrentProcess()->WaitAddressArbiter(address, arb_type, value, timeout);
+ Handle out_handle{};
+
+ ret = CreateResourceLimit64From32(system, &out_handle);
+
+ SetReg32(system, 0, Convert<uint32_t>(ret));
+ SetReg32(system, 1, Convert<uint32_t>(out_handle));
}
-static Result WaitForAddress32(Core::System& system, u32 address, Svc::ArbitrationType arb_type,
- s32 value, u32 timeout_ns_low, u32 timeout_ns_high) {
- const auto timeout = static_cast<s64>(timeout_ns_low | (u64{timeout_ns_high} << 32));
- return WaitForAddress(system, address, arb_type, value, timeout);
+static void SvcWrap_SetResourceLimitLimitValue64From32(Core::System& system) {
+ Result ret{};
+
+ Handle resource_limit_handle{};
+ LimitableResource which{};
+ int64_t limit_value{};
+
+ resource_limit_handle = Convert<Handle>(GetReg32(system, 0));
+ which = Convert<LimitableResource>(GetReg32(system, 1));
+ std::array<uint32_t, 2> limit_value_gather{};
+ limit_value_gather[0] = GetReg32(system, 2);
+ limit_value_gather[1] = GetReg32(system, 3);
+ limit_value = Convert<int64_t>(limit_value_gather);
+
+ ret = SetResourceLimitLimitValue64From32(system, resource_limit_handle, which, limit_value);
+
+ SetReg32(system, 0, Convert<uint32_t>(ret));
}
-// Signals to an address (via Address Arbiter)
-static Result SignalToAddress(Core::System& system, VAddr address, Svc::SignalType signal_type,
- s32 value, s32 count) {
- LOG_TRACE(Kernel_SVC, "called, address=0x{:X}, signal_type=0x{:X}, value=0x{:X}, count=0x{:X}",
- address, signal_type, value, count);
+static void SvcWrap_MapInsecureMemory64From32(Core::System& system) {
+ Result ret{};
- // Validate input.
- if (IsKernelAddress(address)) {
- LOG_ERROR(Kernel_SVC, "Attempting to signal to a kernel address (address={:08X})", address);
- return ResultInvalidCurrentMemory;
- }
- if (!Common::IsAligned(address, sizeof(s32))) {
- LOG_ERROR(Kernel_SVC, "Signaled address must be 4 byte aligned (address={:08X})", address);
- return ResultInvalidAddress;
- }
- if (!IsValidSignalType(signal_type)) {
- LOG_ERROR(Kernel_SVC, "Invalid signal type specified (type={})", signal_type);
- return ResultInvalidEnumValue;
- }
+ uint32_t address{};
+ uint32_t size{};
+
+ address = Convert<uint32_t>(GetReg32(system, 0));
+ size = Convert<uint32_t>(GetReg32(system, 1));
- return system.Kernel().CurrentProcess()->SignalAddressArbiter(address, signal_type, value,
- count);
+ ret = MapInsecureMemory64From32(system, address, size);
+
+ SetReg32(system, 0, Convert<uint32_t>(ret));
}
-static void SynchronizePreemptionState(Core::System& system) {
- auto& kernel = system.Kernel();
+static void SvcWrap_UnmapInsecureMemory64From32(Core::System& system) {
+ Result ret{};
- // Lock the scheduler.
- KScopedSchedulerLock sl{kernel};
+ uint32_t address{};
+ uint32_t size{};
- // If the current thread is pinned, unpin it.
- KProcess* cur_process = system.Kernel().CurrentProcess();
- const auto core_id = GetCurrentCoreId(kernel);
+ address = Convert<uint32_t>(GetReg32(system, 0));
+ size = Convert<uint32_t>(GetReg32(system, 1));
- if (cur_process->GetPinnedThread(core_id) == GetCurrentThreadPointer(kernel)) {
- // Clear the current thread's interrupt flag.
- GetCurrentThread(kernel).ClearInterruptFlag();
+ ret = UnmapInsecureMemory64From32(system, address, size);
- // Unpin the current thread.
- cur_process->UnpinCurrentThread(core_id);
- }
+ SetReg32(system, 0, Convert<uint32_t>(ret));
}
-static Result SignalToAddress32(Core::System& system, u32 address, Svc::SignalType signal_type,
- s32 value, s32 count) {
- return SignalToAddress(system, address, signal_type, value, count);
+static void SvcWrap_SetHeapSize64(Core::System& system) {
+ Result ret{};
+
+ uintptr_t out_address{};
+ uint64_t size{};
+
+ size = Convert<uint64_t>(GetReg64(system, 1));
+
+ ret = SetHeapSize64(system, &out_address, size);
+
+ SetReg64(system, 0, Convert<uint64_t>(ret));
+ SetReg64(system, 1, Convert<uint64_t>(out_address));
}
-static void KernelDebug([[maybe_unused]] Core::System& system,
- [[maybe_unused]] u32 kernel_debug_type, [[maybe_unused]] u64 param1,
- [[maybe_unused]] u64 param2, [[maybe_unused]] u64 param3) {
- // Intentionally do nothing, as this does nothing in released kernel binaries.
+static void SvcWrap_SetMemoryPermission64(Core::System& system) {
+ Result ret{};
+
+ uint64_t address{};
+ uint64_t size{};
+ MemoryPermission perm{};
+
+ address = Convert<uint64_t>(GetReg64(system, 0));
+ size = Convert<uint64_t>(GetReg64(system, 1));
+ perm = Convert<MemoryPermission>(GetReg64(system, 2));
+
+ ret = SetMemoryPermission64(system, address, size, perm);
+
+ SetReg64(system, 0, Convert<uint64_t>(ret));
}
-static void ChangeKernelTraceState([[maybe_unused]] Core::System& system,
- [[maybe_unused]] u32 trace_state) {
- // Intentionally do nothing, as this does nothing in released kernel binaries.
+static void SvcWrap_SetMemoryAttribute64(Core::System& system) {
+ Result ret{};
+
+ uint64_t address{};
+ uint64_t size{};
+ uint32_t mask{};
+ uint32_t attr{};
+
+ address = Convert<uint64_t>(GetReg64(system, 0));
+ size = Convert<uint64_t>(GetReg64(system, 1));
+ mask = Convert<uint32_t>(GetReg64(system, 2));
+ attr = Convert<uint32_t>(GetReg64(system, 3));
+
+ ret = SetMemoryAttribute64(system, address, size, mask, attr);
+
+ SetReg64(system, 0, Convert<uint64_t>(ret));
}
-/// This returns the total CPU ticks elapsed since the CPU was powered-on
-static u64 GetSystemTick(Core::System& system) {
- LOG_TRACE(Kernel_SVC, "called");
+static void SvcWrap_MapMemory64(Core::System& system) {
+ Result ret{};
- auto& core_timing = system.CoreTiming();
+ uint64_t dst_address{};
+ uint64_t src_address{};
+ uint64_t size{};
- // Returns the value of cntpct_el0 (https://switchbrew.org/wiki/SVC#svcGetSystemTick)
- const u64 result{core_timing.GetClockTicks()};
+ dst_address = Convert<uint64_t>(GetReg64(system, 0));
+ src_address = Convert<uint64_t>(GetReg64(system, 1));
+ size = Convert<uint64_t>(GetReg64(system, 2));
- if (!system.Kernel().IsMulticore()) {
- core_timing.AddTicks(400U);
- }
+ ret = MapMemory64(system, dst_address, src_address, size);
- return result;
+ SetReg64(system, 0, Convert<uint64_t>(ret));
}
-static void GetSystemTick32(Core::System& system, u32* time_low, u32* time_high) {
- const auto time = GetSystemTick(system);
- *time_low = static_cast<u32>(time);
- *time_high = static_cast<u32>(time >> 32);
+static void SvcWrap_UnmapMemory64(Core::System& system) {
+ Result ret{};
+
+ uint64_t dst_address{};
+ uint64_t src_address{};
+ uint64_t size{};
+
+ dst_address = Convert<uint64_t>(GetReg64(system, 0));
+ src_address = Convert<uint64_t>(GetReg64(system, 1));
+ size = Convert<uint64_t>(GetReg64(system, 2));
+
+ ret = UnmapMemory64(system, dst_address, src_address, size);
+
+ SetReg64(system, 0, Convert<uint64_t>(ret));
}
-/// Close a handle
-static Result CloseHandle(Core::System& system, Handle handle) {
- LOG_TRACE(Kernel_SVC, "Closing handle 0x{:08X}", handle);
+static void SvcWrap_QueryMemory64(Core::System& system) {
+ Result ret{};
- // Remove the handle.
- R_UNLESS(system.Kernel().CurrentProcess()->GetHandleTable().Remove(handle),
- ResultInvalidHandle);
+ PageInfo out_page_info{};
+ uint64_t out_memory_info{};
+ uint64_t address{};
- return ResultSuccess;
+ out_memory_info = Convert<uint64_t>(GetReg64(system, 0));
+ address = Convert<uint64_t>(GetReg64(system, 2));
+
+ ret = QueryMemory64(system, out_memory_info, &out_page_info, address);
+
+ SetReg64(system, 0, Convert<uint64_t>(ret));
+ SetReg64(system, 1, Convert<uint64_t>(out_page_info));
}
-static Result CloseHandle32(Core::System& system, Handle handle) {
- return CloseHandle(system, handle);
+static void SvcWrap_ExitProcess64(Core::System& system) {
+ ExitProcess64(system);
}
-/// Clears the signaled state of an event or process.
-static Result ResetSignal(Core::System& system, Handle handle) {
- LOG_DEBUG(Kernel_SVC, "called handle 0x{:08X}", handle);
+static void SvcWrap_CreateThread64(Core::System& system) {
+ Result ret{};
- // Get the current handle table.
- const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable();
+ Handle out_handle{};
+ uint64_t func{};
+ uint64_t arg{};
+ uint64_t stack_bottom{};
+ int32_t priority{};
+ int32_t core_id{};
- // Try to reset as readable event.
- {
- KScopedAutoObject readable_event = handle_table.GetObject<KReadableEvent>(handle);
- if (readable_event.IsNotNull()) {
- return readable_event->Reset();
- }
- }
+ func = Convert<uint64_t>(GetReg64(system, 1));
+ arg = Convert<uint64_t>(GetReg64(system, 2));
+ stack_bottom = Convert<uint64_t>(GetReg64(system, 3));
+ priority = Convert<int32_t>(GetReg64(system, 4));
+ core_id = Convert<int32_t>(GetReg64(system, 5));
- // Try to reset as process.
- {
- KScopedAutoObject process = handle_table.GetObject<KProcess>(handle);
- if (process.IsNotNull()) {
- return process->Reset();
- }
- }
+ ret = CreateThread64(system, &out_handle, func, arg, stack_bottom, priority, core_id);
+
+ SetReg64(system, 0, Convert<uint64_t>(ret));
+ SetReg64(system, 1, Convert<uint64_t>(out_handle));
+}
+
+static void SvcWrap_StartThread64(Core::System& system) {
+ Result ret{};
+
+ Handle thread_handle{};
- LOG_ERROR(Kernel_SVC, "invalid handle (0x{:08X})", handle);
+ thread_handle = Convert<Handle>(GetReg64(system, 0));
- return ResultInvalidHandle;
+ ret = StartThread64(system, thread_handle);
+
+ SetReg64(system, 0, Convert<uint64_t>(ret));
}
-static Result ResetSignal32(Core::System& system, Handle handle) {
- return ResetSignal(system, handle);
+static void SvcWrap_ExitThread64(Core::System& system) {
+ ExitThread64(system);
}
-namespace {
+static void SvcWrap_SleepThread64(Core::System& system) {
+ int64_t ns{};
-constexpr bool IsValidTransferMemoryPermission(MemoryPermission perm) {
- switch (perm) {
- case MemoryPermission::None:
- case MemoryPermission::Read:
- case MemoryPermission::ReadWrite:
- return true;
- default:
- return false;
- }
+ ns = Convert<int64_t>(GetReg64(system, 0));
+
+ SleepThread64(system, ns);
}
-} // Anonymous namespace
+static void SvcWrap_GetThreadPriority64(Core::System& system) {
+ Result ret{};
-/// Creates a TransferMemory object
-static Result CreateTransferMemory(Core::System& system, Handle* out, VAddr address, u64 size,
- MemoryPermission map_perm) {
- auto& kernel = system.Kernel();
+ int32_t out_priority{};
+ Handle thread_handle{};
+
+ thread_handle = Convert<Handle>(GetReg64(system, 1));
+
+ ret = GetThreadPriority64(system, &out_priority, thread_handle);
- // Validate the size.
- R_UNLESS(Common::IsAligned(address, PageSize), ResultInvalidAddress);
- R_UNLESS(Common::IsAligned(size, PageSize), ResultInvalidSize);
- R_UNLESS(size > 0, ResultInvalidSize);
- R_UNLESS((address < address + size), ResultInvalidCurrentMemory);
+ SetReg64(system, 0, Convert<uint64_t>(ret));
+ SetReg64(system, 1, Convert<uint64_t>(out_priority));
+}
+
+static void SvcWrap_SetThreadPriority64(Core::System& system) {
+ Result ret{};
+
+ Handle thread_handle{};
+ int32_t priority{};
+
+ thread_handle = Convert<Handle>(GetReg64(system, 0));
+ priority = Convert<int32_t>(GetReg64(system, 1));
- // Validate the permissions.
- R_UNLESS(IsValidTransferMemoryPermission(map_perm), ResultInvalidNewMemoryPermission);
+ ret = SetThreadPriority64(system, thread_handle, priority);
- // Get the current process and handle table.
- auto& process = *kernel.CurrentProcess();
- auto& handle_table = process.GetHandleTable();
+ SetReg64(system, 0, Convert<uint64_t>(ret));
+}
+
+static void SvcWrap_GetThreadCoreMask64(Core::System& system) {
+ Result ret{};
- // Reserve a new transfer memory from the process resource limit.
- KScopedResourceReservation trmem_reservation(kernel.CurrentProcess(),
- LimitableResource::TransferMemoryCountMax);
- R_UNLESS(trmem_reservation.Succeeded(), ResultLimitReached);
+ int32_t out_core_id{};
+ uint64_t out_affinity_mask{};
+ Handle thread_handle{};
- // Create the transfer memory.
- KTransferMemory* trmem = KTransferMemory::Create(kernel);
- R_UNLESS(trmem != nullptr, ResultOutOfResource);
+ thread_handle = Convert<Handle>(GetReg64(system, 2));
- // Ensure the only reference is in the handle table when we're done.
- SCOPE_EXIT({ trmem->Close(); });
+ ret = GetThreadCoreMask64(system, &out_core_id, &out_affinity_mask, thread_handle);
- // Ensure that the region is in range.
- R_UNLESS(process.PageTable().Contains(address, size), ResultInvalidCurrentMemory);
+ SetReg64(system, 0, Convert<uint64_t>(ret));
+ SetReg64(system, 1, Convert<uint64_t>(out_core_id));
+ SetReg64(system, 2, Convert<uint64_t>(out_affinity_mask));
+}
- // Initialize the transfer memory.
- R_TRY(trmem->Initialize(address, size, map_perm));
+static void SvcWrap_SetThreadCoreMask64(Core::System& system) {
+ Result ret{};
- // Commit the reservation.
- trmem_reservation.Commit();
+ Handle thread_handle{};
+ int32_t core_id{};
+ uint64_t affinity_mask{};
- // Register the transfer memory.
- KTransferMemory::Register(kernel, trmem);
+ thread_handle = Convert<Handle>(GetReg64(system, 0));
+ core_id = Convert<int32_t>(GetReg64(system, 1));
+ affinity_mask = Convert<uint64_t>(GetReg64(system, 2));
- // Add the transfer memory to the handle table.
- R_TRY(handle_table.Add(out, trmem));
+ ret = SetThreadCoreMask64(system, thread_handle, core_id, affinity_mask);
- return ResultSuccess;
+ SetReg64(system, 0, Convert<uint64_t>(ret));
}
-static Result CreateTransferMemory32(Core::System& system, Handle* out, u32 address, u32 size,
- MemoryPermission map_perm) {
- return CreateTransferMemory(system, out, address, size, map_perm);
+static void SvcWrap_GetCurrentProcessorNumber64(Core::System& system) {
+ int32_t ret{};
+
+ ret = GetCurrentProcessorNumber64(system);
+
+ SetReg64(system, 0, Convert<uint64_t>(ret));
}
-static Result GetThreadCoreMask(Core::System& system, Handle thread_handle, s32* out_core_id,
- u64* out_affinity_mask) {
- LOG_TRACE(Kernel_SVC, "called, handle=0x{:08X}", thread_handle);
+static void SvcWrap_SignalEvent64(Core::System& system) {
+ Result ret{};
+
+ Handle event_handle{};
- // Get the thread from its handle.
- KScopedAutoObject thread =
- system.Kernel().CurrentProcess()->GetHandleTable().GetObject<KThread>(thread_handle);
- R_UNLESS(thread.IsNotNull(), ResultInvalidHandle);
+ event_handle = Convert<Handle>(GetReg64(system, 0));
- // Get the core mask.
- R_TRY(thread->GetCoreMask(out_core_id, out_affinity_mask));
+ ret = SignalEvent64(system, event_handle);
- return ResultSuccess;
+ SetReg64(system, 0, Convert<uint64_t>(ret));
}
-static Result GetThreadCoreMask32(Core::System& system, Handle thread_handle, s32* out_core_id,
- u32* out_affinity_mask_low, u32* out_affinity_mask_high) {
- u64 out_affinity_mask{};
- const auto result = GetThreadCoreMask(system, thread_handle, out_core_id, &out_affinity_mask);
- *out_affinity_mask_high = static_cast<u32>(out_affinity_mask >> 32);
- *out_affinity_mask_low = static_cast<u32>(out_affinity_mask);
- return result;
+static void SvcWrap_ClearEvent64(Core::System& system) {
+ Result ret{};
+
+ Handle event_handle{};
+
+ event_handle = Convert<Handle>(GetReg64(system, 0));
+
+ ret = ClearEvent64(system, event_handle);
+
+ SetReg64(system, 0, Convert<uint64_t>(ret));
}
-static Result SetThreadCoreMask(Core::System& system, Handle thread_handle, s32 core_id,
- u64 affinity_mask) {
- // Determine the core id/affinity mask.
- if (core_id == IdealCoreUseProcessValue) {
- core_id = system.Kernel().CurrentProcess()->GetIdealCoreId();
- affinity_mask = (1ULL << core_id);
- } else {
- // Validate the affinity mask.
- const u64 process_core_mask = system.Kernel().CurrentProcess()->GetCoreMask();
- R_UNLESS((affinity_mask | process_core_mask) == process_core_mask, ResultInvalidCoreId);
- R_UNLESS(affinity_mask != 0, ResultInvalidCombination);
-
- // Validate the core id.
- if (IsValidVirtualCoreId(core_id)) {
- R_UNLESS(((1ULL << core_id) & affinity_mask) != 0, ResultInvalidCombination);
- } else {
- R_UNLESS(core_id == IdealCoreNoUpdate || core_id == IdealCoreDontCare,
- ResultInvalidCoreId);
- }
- }
+static void SvcWrap_MapSharedMemory64(Core::System& system) {
+ Result ret{};
+
+ Handle shmem_handle{};
+ uint64_t address{};
+ uint64_t size{};
+ MemoryPermission map_perm{};
- // Get the thread from its handle.
- KScopedAutoObject thread =
- system.Kernel().CurrentProcess()->GetHandleTable().GetObject<KThread>(thread_handle);
- R_UNLESS(thread.IsNotNull(), ResultInvalidHandle);
+ shmem_handle = Convert<Handle>(GetReg64(system, 0));
+ address = Convert<uint64_t>(GetReg64(system, 1));
+ size = Convert<uint64_t>(GetReg64(system, 2));
+ map_perm = Convert<MemoryPermission>(GetReg64(system, 3));
- // Set the core mask.
- R_TRY(thread->SetCoreMask(core_id, affinity_mask));
+ ret = MapSharedMemory64(system, shmem_handle, address, size, map_perm);
- return ResultSuccess;
+ SetReg64(system, 0, Convert<uint64_t>(ret));
}
-static Result SetThreadCoreMask32(Core::System& system, Handle thread_handle, s32 core_id,
- u32 affinity_mask_low, u32 affinity_mask_high) {
- const auto affinity_mask = u64{affinity_mask_low} | (u64{affinity_mask_high} << 32);
- return SetThreadCoreMask(system, thread_handle, core_id, affinity_mask);
+static void SvcWrap_UnmapSharedMemory64(Core::System& system) {
+ Result ret{};
+
+ Handle shmem_handle{};
+ uint64_t address{};
+ uint64_t size{};
+
+ shmem_handle = Convert<Handle>(GetReg64(system, 0));
+ address = Convert<uint64_t>(GetReg64(system, 1));
+ size = Convert<uint64_t>(GetReg64(system, 2));
+
+ ret = UnmapSharedMemory64(system, shmem_handle, address, size);
+
+ SetReg64(system, 0, Convert<uint64_t>(ret));
}
-static Result SignalEvent(Core::System& system, Handle event_handle) {
- LOG_DEBUG(Kernel_SVC, "called, event_handle=0x{:08X}", event_handle);
+static void SvcWrap_CreateTransferMemory64(Core::System& system) {
+ Result ret{};
- // Get the current handle table.
- const KHandleTable& handle_table = system.Kernel().CurrentProcess()->GetHandleTable();
+ Handle out_handle{};
+ uint64_t address{};
+ uint64_t size{};
+ MemoryPermission map_perm{};
- // Get the event.
- KScopedAutoObject event = handle_table.GetObject<KEvent>(event_handle);
- R_UNLESS(event.IsNotNull(), ResultInvalidHandle);
+ address = Convert<uint64_t>(GetReg64(system, 1));
+ size = Convert<uint64_t>(GetReg64(system, 2));
+ map_perm = Convert<MemoryPermission>(GetReg64(system, 3));
- return event->Signal();
+ ret = CreateTransferMemory64(system, &out_handle, address, size, map_perm);
+
+ SetReg64(system, 0, Convert<uint64_t>(ret));
+ SetReg64(system, 1, Convert<uint64_t>(out_handle));
}
-static Result SignalEvent32(Core::System& system, Handle event_handle) {
- return SignalEvent(system, event_handle);
+static void SvcWrap_CloseHandle64(Core::System& system) {
+ Result ret{};
+
+ Handle handle{};
+
+ handle = Convert<Handle>(GetReg64(system, 0));
+
+ ret = CloseHandle64(system, handle);
+
+ SetReg64(system, 0, Convert<uint64_t>(ret));
}
-static Result ClearEvent(Core::System& system, Handle event_handle) {
- LOG_TRACE(Kernel_SVC, "called, event_handle=0x{:08X}", event_handle);
+static void SvcWrap_ResetSignal64(Core::System& system) {
+ Result ret{};
- // Get the current handle table.
- const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable();
+ Handle handle{};
- // Try to clear the writable event.
- {
- KScopedAutoObject event = handle_table.GetObject<KEvent>(event_handle);
- if (event.IsNotNull()) {
- return event->Clear();
- }
- }
+ handle = Convert<Handle>(GetReg64(system, 0));
- // Try to clear the readable event.
- {
- KScopedAutoObject readable_event = handle_table.GetObject<KReadableEvent>(event_handle);
- if (readable_event.IsNotNull()) {
- return readable_event->Clear();
- }
- }
+ ret = ResetSignal64(system, handle);
+
+ SetReg64(system, 0, Convert<uint64_t>(ret));
+}
- LOG_ERROR(Kernel_SVC, "Event handle does not exist, event_handle=0x{:08X}", event_handle);
+static void SvcWrap_WaitSynchronization64(Core::System& system) {
+ Result ret{};
- return ResultInvalidHandle;
+ int32_t out_index{};
+ uint64_t handles{};
+ int32_t num_handles{};
+ int64_t timeout_ns{};
+
+ handles = Convert<uint64_t>(GetReg64(system, 1));
+ num_handles = Convert<int32_t>(GetReg64(system, 2));
+ timeout_ns = Convert<int64_t>(GetReg64(system, 3));
+
+ ret = WaitSynchronization64(system, &out_index, handles, num_handles, timeout_ns);
+
+ SetReg64(system, 0, Convert<uint64_t>(ret));
+ SetReg64(system, 1, Convert<uint64_t>(out_index));
}
-static Result ClearEvent32(Core::System& system, Handle event_handle) {
- return ClearEvent(system, event_handle);
+static void SvcWrap_CancelSynchronization64(Core::System& system) {
+ Result ret{};
+
+ Handle handle{};
+
+ handle = Convert<Handle>(GetReg64(system, 0));
+
+ ret = CancelSynchronization64(system, handle);
+
+ SetReg64(system, 0, Convert<uint64_t>(ret));
}
-static Result CreateEvent(Core::System& system, Handle* out_write, Handle* out_read) {
- LOG_DEBUG(Kernel_SVC, "called");
+static void SvcWrap_ArbitrateLock64(Core::System& system) {
+ Result ret{};
- // Get the kernel reference and handle table.
- auto& kernel = system.Kernel();
- auto& handle_table = kernel.CurrentProcess()->GetHandleTable();
+ Handle thread_handle{};
+ uint64_t address{};
+ uint32_t tag{};
- // Reserve a new event from the process resource limit
- KScopedResourceReservation event_reservation(kernel.CurrentProcess(),
- LimitableResource::EventCountMax);
- R_UNLESS(event_reservation.Succeeded(), ResultLimitReached);
+ thread_handle = Convert<Handle>(GetReg64(system, 0));
+ address = Convert<uint64_t>(GetReg64(system, 1));
+ tag = Convert<uint32_t>(GetReg64(system, 2));
- // Create a new event.
- KEvent* event = KEvent::Create(kernel);
- R_UNLESS(event != nullptr, ResultOutOfResource);
+ ret = ArbitrateLock64(system, thread_handle, address, tag);
- // Initialize the event.
- event->Initialize(kernel.CurrentProcess());
+ SetReg64(system, 0, Convert<uint64_t>(ret));
+}
+
+static void SvcWrap_ArbitrateUnlock64(Core::System& system) {
+ Result ret{};
- // Commit the thread reservation.
- event_reservation.Commit();
+ uint64_t address{};
- // Ensure that we clean up the event (and its only references are handle table) on function end.
- SCOPE_EXIT({
- event->GetReadableEvent().Close();
- event->Close();
- });
+ address = Convert<uint64_t>(GetReg64(system, 0));
- // Register the event.
- KEvent::Register(kernel, event);
+ ret = ArbitrateUnlock64(system, address);
+
+ SetReg64(system, 0, Convert<uint64_t>(ret));
+}
- // Add the event to the handle table.
- R_TRY(handle_table.Add(out_write, event));
+static void SvcWrap_WaitProcessWideKeyAtomic64(Core::System& system) {
+ Result ret{};
- // Ensure that we maintaing a clean handle state on exit.
- auto handle_guard = SCOPE_GUARD({ handle_table.Remove(*out_write); });
+ uint64_t address{};
+ uint64_t cv_key{};
+ uint32_t tag{};
+ int64_t timeout_ns{};
- // Add the readable event to the handle table.
- R_TRY(handle_table.Add(out_read, std::addressof(event->GetReadableEvent())));
+ address = Convert<uint64_t>(GetReg64(system, 0));
+ cv_key = Convert<uint64_t>(GetReg64(system, 1));
+ tag = Convert<uint32_t>(GetReg64(system, 2));
+ timeout_ns = Convert<int64_t>(GetReg64(system, 3));
- // We succeeded.
- handle_guard.Cancel();
- return ResultSuccess;
+ ret = WaitProcessWideKeyAtomic64(system, address, cv_key, tag, timeout_ns);
+
+ SetReg64(system, 0, Convert<uint64_t>(ret));
}
-static Result CreateEvent32(Core::System& system, Handle* out_write, Handle* out_read) {
- return CreateEvent(system, out_write, out_read);
+static void SvcWrap_SignalProcessWideKey64(Core::System& system) {
+ uint64_t cv_key{};
+ int32_t count{};
+
+ cv_key = Convert<uint64_t>(GetReg64(system, 0));
+ count = Convert<int32_t>(GetReg64(system, 1));
+
+ SignalProcessWideKey64(system, cv_key, count);
}
-static Result GetProcessInfo(Core::System& system, u64* out, Handle process_handle, u32 type) {
- LOG_DEBUG(Kernel_SVC, "called, handle=0x{:08X}, type=0x{:X}", process_handle, type);
+static void SvcWrap_GetSystemTick64(Core::System& system) {
+ int64_t ret{};
- const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable();
- KScopedAutoObject process = handle_table.GetObject<KProcess>(process_handle);
- if (process.IsNull()) {
- LOG_ERROR(Kernel_SVC, "Process handle does not exist, process_handle=0x{:08X}",
- process_handle);
- return ResultInvalidHandle;
- }
+ ret = GetSystemTick64(system);
- const auto info_type = static_cast<ProcessInfoType>(type);
- if (info_type != ProcessInfoType::ProcessState) {
- LOG_ERROR(Kernel_SVC, "Expected info_type to be ProcessState but got {} instead", type);
- return ResultInvalidEnumValue;
- }
+ SetReg64(system, 0, Convert<uint64_t>(ret));
+}
+
+static void SvcWrap_ConnectToNamedPort64(Core::System& system) {
+ Result ret{};
- *out = static_cast<u64>(process->GetState());
- return ResultSuccess;
+ Handle out_handle{};
+ uint64_t name{};
+
+ name = Convert<uint64_t>(GetReg64(system, 1));
+
+ ret = ConnectToNamedPort64(system, &out_handle, name);
+
+ SetReg64(system, 0, Convert<uint64_t>(ret));
+ SetReg64(system, 1, Convert<uint64_t>(out_handle));
}
-static Result CreateResourceLimit(Core::System& system, Handle* out_handle) {
- LOG_DEBUG(Kernel_SVC, "called");
+static void SvcWrap_SendSyncRequest64(Core::System& system) {
+ Result ret{};
- // Create a new resource limit.
- auto& kernel = system.Kernel();
- KResourceLimit* resource_limit = KResourceLimit::Create(kernel);
- R_UNLESS(resource_limit != nullptr, ResultOutOfResource);
+ Handle session_handle{};
- // Ensure we don't leak a reference to the limit.
- SCOPE_EXIT({ resource_limit->Close(); });
+ session_handle = Convert<Handle>(GetReg64(system, 0));
- // Initialize the resource limit.
- resource_limit->Initialize(&system.CoreTiming());
+ ret = SendSyncRequest64(system, session_handle);
- // Register the limit.
- KResourceLimit::Register(kernel, resource_limit);
+ SetReg64(system, 0, Convert<uint64_t>(ret));
+}
+
+static void SvcWrap_SendSyncRequestWithUserBuffer64(Core::System& system) {
+ Result ret{};
+
+ uint64_t message_buffer{};
+ uint64_t message_buffer_size{};
+ Handle session_handle{};
+
+ message_buffer = Convert<uint64_t>(GetReg64(system, 0));
+ message_buffer_size = Convert<uint64_t>(GetReg64(system, 1));
+ session_handle = Convert<Handle>(GetReg64(system, 2));
- // Add the limit to the handle table.
- R_TRY(kernel.CurrentProcess()->GetHandleTable().Add(out_handle, resource_limit));
+ ret = SendSyncRequestWithUserBuffer64(system, message_buffer, message_buffer_size, session_handle);
- return ResultSuccess;
+ SetReg64(system, 0, Convert<uint64_t>(ret));
}
-static Result GetResourceLimitLimitValue(Core::System& system, u64* out_limit_value,
- Handle resource_limit_handle, LimitableResource which) {
- LOG_DEBUG(Kernel_SVC, "called, resource_limit_handle={:08X}, which={}", resource_limit_handle,
- which);
+static void SvcWrap_SendAsyncRequestWithUserBuffer64(Core::System& system) {
+ Result ret{};
- // Validate the resource.
- R_UNLESS(IsValidResourceType(which), ResultInvalidEnumValue);
+ Handle out_event_handle{};
+ uint64_t message_buffer{};
+ uint64_t message_buffer_size{};
+ Handle session_handle{};
- // Get the resource limit.
- auto& kernel = system.Kernel();
- KScopedAutoObject resource_limit =
- kernel.CurrentProcess()->GetHandleTable().GetObject<KResourceLimit>(resource_limit_handle);
- R_UNLESS(resource_limit.IsNotNull(), ResultInvalidHandle);
+ message_buffer = Convert<uint64_t>(GetReg64(system, 1));
+ message_buffer_size = Convert<uint64_t>(GetReg64(system, 2));
+ session_handle = Convert<Handle>(GetReg64(system, 3));
- // Get the limit value.
- *out_limit_value = resource_limit->GetLimitValue(which);
+ ret = SendAsyncRequestWithUserBuffer64(system, &out_event_handle, message_buffer, message_buffer_size, session_handle);
- return ResultSuccess;
+ SetReg64(system, 0, Convert<uint64_t>(ret));
+ SetReg64(system, 1, Convert<uint64_t>(out_event_handle));
}
-static Result GetResourceLimitCurrentValue(Core::System& system, u64* out_current_value,
- Handle resource_limit_handle, LimitableResource which) {
- LOG_DEBUG(Kernel_SVC, "called, resource_limit_handle={:08X}, which={}", resource_limit_handle,
- which);
+static void SvcWrap_GetProcessId64(Core::System& system) {
+ Result ret{};
- // Validate the resource.
- R_UNLESS(IsValidResourceType(which), ResultInvalidEnumValue);
+ uint64_t out_process_id{};
+ Handle process_handle{};
- // Get the resource limit.
- auto& kernel = system.Kernel();
- KScopedAutoObject resource_limit =
- kernel.CurrentProcess()->GetHandleTable().GetObject<KResourceLimit>(resource_limit_handle);
- R_UNLESS(resource_limit.IsNotNull(), ResultInvalidHandle);
+ process_handle = Convert<Handle>(GetReg64(system, 1));
- // Get the current value.
- *out_current_value = resource_limit->GetCurrentValue(which);
+ ret = GetProcessId64(system, &out_process_id, process_handle);
- return ResultSuccess;
+ SetReg64(system, 0, Convert<uint64_t>(ret));
+ SetReg64(system, 1, Convert<uint64_t>(out_process_id));
}
-static Result SetResourceLimitLimitValue(Core::System& system, Handle resource_limit_handle,
- LimitableResource which, u64 limit_value) {
- LOG_DEBUG(Kernel_SVC, "called, resource_limit_handle={:08X}, which={}, limit_value={}",
- resource_limit_handle, which, limit_value);
+static void SvcWrap_GetThreadId64(Core::System& system) {
+ Result ret{};
- // Validate the resource.
- R_UNLESS(IsValidResourceType(which), ResultInvalidEnumValue);
+ uint64_t out_thread_id{};
+ Handle thread_handle{};
- // Get the resource limit.
- auto& kernel = system.Kernel();
- KScopedAutoObject resource_limit =
- kernel.CurrentProcess()->GetHandleTable().GetObject<KResourceLimit>(resource_limit_handle);
- R_UNLESS(resource_limit.IsNotNull(), ResultInvalidHandle);
+ thread_handle = Convert<Handle>(GetReg64(system, 1));
- // Set the limit value.
- R_TRY(resource_limit->SetLimitValue(which, limit_value));
+ ret = GetThreadId64(system, &out_thread_id, thread_handle);
- return ResultSuccess;
+ SetReg64(system, 0, Convert<uint64_t>(ret));
+ SetReg64(system, 1, Convert<uint64_t>(out_thread_id));
}
-static Result GetProcessList(Core::System& system, u32* out_num_processes, VAddr out_process_ids,
- u32 out_process_ids_size) {
- LOG_DEBUG(Kernel_SVC, "called. out_process_ids=0x{:016X}, out_process_ids_size={}",
- out_process_ids, out_process_ids_size);
+static void SvcWrap_Break64(Core::System& system) {
+ BreakReason break_reason{};
+ uint64_t arg{};
+ uint64_t size{};
- // If the supplied size is negative or greater than INT32_MAX / sizeof(u64), bail.
- if ((out_process_ids_size & 0xF0000000) != 0) {
- LOG_ERROR(Kernel_SVC,
- "Supplied size outside [0, 0x0FFFFFFF] range. out_process_ids_size={}",
- out_process_ids_size);
- return ResultOutOfRange;
- }
+ break_reason = Convert<BreakReason>(GetReg64(system, 0));
+ arg = Convert<uint64_t>(GetReg64(system, 1));
+ size = Convert<uint64_t>(GetReg64(system, 2));
- const auto& kernel = system.Kernel();
- const auto total_copy_size = out_process_ids_size * sizeof(u64);
+ Break64(system, break_reason, arg, size);
+}
- if (out_process_ids_size > 0 && !kernel.CurrentProcess()->PageTable().IsInsideAddressSpace(
- out_process_ids, total_copy_size)) {
- LOG_ERROR(Kernel_SVC, "Address range outside address space. begin=0x{:016X}, end=0x{:016X}",
- out_process_ids, out_process_ids + total_copy_size);
- return ResultInvalidCurrentMemory;
- }
+static void SvcWrap_OutputDebugString64(Core::System& system) {
+ Result ret{};
- auto& memory = system.Memory();
- const auto& process_list = kernel.GetProcessList();
- const auto num_processes = process_list.size();
- const auto copy_amount = std::min(std::size_t{out_process_ids_size}, num_processes);
+ uint64_t debug_str{};
+ uint64_t len{};
- for (std::size_t i = 0; i < copy_amount; ++i) {
- memory.Write64(out_process_ids, process_list[i]->GetProcessID());
- out_process_ids += sizeof(u64);
- }
+ debug_str = Convert<uint64_t>(GetReg64(system, 0));
+ len = Convert<uint64_t>(GetReg64(system, 1));
+
+ ret = OutputDebugString64(system, debug_str, len);
- *out_num_processes = static_cast<u32>(num_processes);
- return ResultSuccess;
+ SetReg64(system, 0, Convert<uint64_t>(ret));
}
-static Result GetThreadList(Core::System& system, u32* out_num_threads, VAddr out_thread_ids,
- u32 out_thread_ids_size, Handle debug_handle) {
- // TODO: Handle this case when debug events are supported.
- UNIMPLEMENTED_IF(debug_handle != InvalidHandle);
+static void SvcWrap_ReturnFromException64(Core::System& system) {
+ Result result{};
- LOG_DEBUG(Kernel_SVC, "called. out_thread_ids=0x{:016X}, out_thread_ids_size={}",
- out_thread_ids, out_thread_ids_size);
+ result = Convert<Result>(GetReg64(system, 0));
- // If the size is negative or larger than INT32_MAX / sizeof(u64)
- if ((out_thread_ids_size & 0xF0000000) != 0) {
- LOG_ERROR(Kernel_SVC, "Supplied size outside [0, 0x0FFFFFFF] range. size={}",
- out_thread_ids_size);
- return ResultOutOfRange;
- }
+ ReturnFromException64(system, result);
+}
- auto* const current_process = system.Kernel().CurrentProcess();
- const auto total_copy_size = out_thread_ids_size * sizeof(u64);
+static void SvcWrap_GetInfo64(Core::System& system) {
+ Result ret{};
- if (out_thread_ids_size > 0 &&
- !current_process->PageTable().IsInsideAddressSpace(out_thread_ids, total_copy_size)) {
- LOG_ERROR(Kernel_SVC, "Address range outside address space. begin=0x{:016X}, end=0x{:016X}",
- out_thread_ids, out_thread_ids + total_copy_size);
- return ResultInvalidCurrentMemory;
- }
+ uint64_t out{};
+ InfoType info_type{};
+ Handle handle{};
+ uint64_t info_subtype{};
- auto& memory = system.Memory();
- const auto& thread_list = current_process->GetThreadList();
- const auto num_threads = thread_list.size();
- const auto copy_amount = std::min(std::size_t{out_thread_ids_size}, num_threads);
+ info_type = Convert<InfoType>(GetReg64(system, 1));
+ handle = Convert<Handle>(GetReg64(system, 2));
+ info_subtype = Convert<uint64_t>(GetReg64(system, 3));
- auto list_iter = thread_list.cbegin();
- for (std::size_t i = 0; i < copy_amount; ++i, ++list_iter) {
- memory.Write64(out_thread_ids, (*list_iter)->GetThreadID());
- out_thread_ids += sizeof(u64);
- }
+ ret = GetInfo64(system, &out, info_type, handle, info_subtype);
+
+ SetReg64(system, 0, Convert<uint64_t>(ret));
+ SetReg64(system, 1, Convert<uint64_t>(out));
+}
+
+static void SvcWrap_FlushEntireDataCache64(Core::System& system) {
+ FlushEntireDataCache64(system);
+}
+
+static void SvcWrap_FlushDataCache64(Core::System& system) {
+ Result ret{};
+
+ uint64_t address{};
+ uint64_t size{};
+
+ address = Convert<uint64_t>(GetReg64(system, 0));
+ size = Convert<uint64_t>(GetReg64(system, 1));
+
+ ret = FlushDataCache64(system, address, size);
+
+ SetReg64(system, 0, Convert<uint64_t>(ret));
+}
+
+static void SvcWrap_MapPhysicalMemory64(Core::System& system) {
+ Result ret{};
+
+ uint64_t address{};
+ uint64_t size{};
+
+ address = Convert<uint64_t>(GetReg64(system, 0));
+ size = Convert<uint64_t>(GetReg64(system, 1));
+
+ ret = MapPhysicalMemory64(system, address, size);
+
+ SetReg64(system, 0, Convert<uint64_t>(ret));
+}
+
+static void SvcWrap_UnmapPhysicalMemory64(Core::System& system) {
+ Result ret{};
+
+ uint64_t address{};
+ uint64_t size{};
+
+ address = Convert<uint64_t>(GetReg64(system, 0));
+ size = Convert<uint64_t>(GetReg64(system, 1));
+
+ ret = UnmapPhysicalMemory64(system, address, size);
+
+ SetReg64(system, 0, Convert<uint64_t>(ret));
+}
+
+static void SvcWrap_GetDebugFutureThreadInfo64(Core::System& system) {
+ Result ret{};
+
+ lp64::LastThreadContext out_context{};
+ uint64_t out_thread_id{};
+ Handle debug_handle{};
+ int64_t ns{};
+
+ debug_handle = Convert<Handle>(GetReg64(system, 2));
+ ns = Convert<int64_t>(GetReg64(system, 3));
+
+ ret = GetDebugFutureThreadInfo64(system, &out_context, &out_thread_id, debug_handle, ns);
+
+ SetReg64(system, 0, Convert<uint64_t>(ret));
+ auto out_context_scatter = Convert<std::array<uint64_t, 4>>(out_context);
+ SetReg64(system, 1, out_context_scatter[0]);
+ SetReg64(system, 2, out_context_scatter[1]);
+ SetReg64(system, 3, out_context_scatter[2]);
+ SetReg64(system, 4, out_context_scatter[3]);
+ SetReg64(system, 5, Convert<uint64_t>(out_thread_id));
+}
+
+static void SvcWrap_GetLastThreadInfo64(Core::System& system) {
+ Result ret{};
+
+ lp64::LastThreadContext out_context{};
+ uintptr_t out_tls_address{};
+ uint32_t out_flags{};
+
+ ret = GetLastThreadInfo64(system, &out_context, &out_tls_address, &out_flags);
+
+ SetReg64(system, 0, Convert<uint64_t>(ret));
+ auto out_context_scatter = Convert<std::array<uint64_t, 4>>(out_context);
+ SetReg64(system, 1, out_context_scatter[0]);
+ SetReg64(system, 2, out_context_scatter[1]);
+ SetReg64(system, 3, out_context_scatter[2]);
+ SetReg64(system, 4, out_context_scatter[3]);
+ SetReg64(system, 5, Convert<uint64_t>(out_tls_address));
+ SetReg64(system, 6, Convert<uint64_t>(out_flags));
+}
+
+static void SvcWrap_GetResourceLimitLimitValue64(Core::System& system) {
+ Result ret{};
+
+ int64_t out_limit_value{};
+ Handle resource_limit_handle{};
+ LimitableResource which{};
+
+ resource_limit_handle = Convert<Handle>(GetReg64(system, 1));
+ which = Convert<LimitableResource>(GetReg64(system, 2));
+
+ ret = GetResourceLimitLimitValue64(system, &out_limit_value, resource_limit_handle, which);
+
+ SetReg64(system, 0, Convert<uint64_t>(ret));
+ SetReg64(system, 1, Convert<uint64_t>(out_limit_value));
+}
+
+static void SvcWrap_GetResourceLimitCurrentValue64(Core::System& system) {
+ Result ret{};
+
+ int64_t out_current_value{};
+ Handle resource_limit_handle{};
+ LimitableResource which{};
+
+ resource_limit_handle = Convert<Handle>(GetReg64(system, 1));
+ which = Convert<LimitableResource>(GetReg64(system, 2));
+
+ ret = GetResourceLimitCurrentValue64(system, &out_current_value, resource_limit_handle, which);
+
+ SetReg64(system, 0, Convert<uint64_t>(ret));
+ SetReg64(system, 1, Convert<uint64_t>(out_current_value));
+}
+
+static void SvcWrap_SetThreadActivity64(Core::System& system) {
+ Result ret{};
+
+ Handle thread_handle{};
+ ThreadActivity thread_activity{};
+
+ thread_handle = Convert<Handle>(GetReg64(system, 0));
+ thread_activity = Convert<ThreadActivity>(GetReg64(system, 1));
+
+ ret = SetThreadActivity64(system, thread_handle, thread_activity);
+
+ SetReg64(system, 0, Convert<uint64_t>(ret));
+}
+
+static void SvcWrap_GetThreadContext364(Core::System& system) {
+ Result ret{};
+
+ uint64_t out_context{};
+ Handle thread_handle{};
+
+ out_context = Convert<uint64_t>(GetReg64(system, 0));
+ thread_handle = Convert<Handle>(GetReg64(system, 1));
+
+ ret = GetThreadContext364(system, out_context, thread_handle);
+
+ SetReg64(system, 0, Convert<uint64_t>(ret));
+}
+
+static void SvcWrap_WaitForAddress64(Core::System& system) {
+ Result ret{};
+
+ uint64_t address{};
+ ArbitrationType arb_type{};
+ int32_t value{};
+ int64_t timeout_ns{};
+
+ address = Convert<uint64_t>(GetReg64(system, 0));
+ arb_type = Convert<ArbitrationType>(GetReg64(system, 1));
+ value = Convert<int32_t>(GetReg64(system, 2));
+ timeout_ns = Convert<int64_t>(GetReg64(system, 3));
+
+ ret = WaitForAddress64(system, address, arb_type, value, timeout_ns);
+
+ SetReg64(system, 0, Convert<uint64_t>(ret));
+}
+
+static void SvcWrap_SignalToAddress64(Core::System& system) {
+ Result ret{};
+
+ uint64_t address{};
+ SignalType signal_type{};
+ int32_t value{};
+ int32_t count{};
+
+ address = Convert<uint64_t>(GetReg64(system, 0));
+ signal_type = Convert<SignalType>(GetReg64(system, 1));
+ value = Convert<int32_t>(GetReg64(system, 2));
+ count = Convert<int32_t>(GetReg64(system, 3));
+
+ ret = SignalToAddress64(system, address, signal_type, value, count);
+
+ SetReg64(system, 0, Convert<uint64_t>(ret));
+}
+
+static void SvcWrap_SynchronizePreemptionState64(Core::System& system) {
+ SynchronizePreemptionState64(system);
+}
+
+static void SvcWrap_GetResourceLimitPeakValue64(Core::System& system) {
+ Result ret{};
+
+ int64_t out_peak_value{};
+ Handle resource_limit_handle{};
+ LimitableResource which{};
+
+ resource_limit_handle = Convert<Handle>(GetReg64(system, 1));
+ which = Convert<LimitableResource>(GetReg64(system, 2));
+
+ ret = GetResourceLimitPeakValue64(system, &out_peak_value, resource_limit_handle, which);
+
+ SetReg64(system, 0, Convert<uint64_t>(ret));
+ SetReg64(system, 1, Convert<uint64_t>(out_peak_value));
+}
+
+static void SvcWrap_CreateIoPool64(Core::System& system) {
+ Result ret{};
+
+ Handle out_handle{};
+ IoPoolType which{};
+
+ which = Convert<IoPoolType>(GetReg64(system, 1));
+
+ ret = CreateIoPool64(system, &out_handle, which);
+
+ SetReg64(system, 0, Convert<uint64_t>(ret));
+ SetReg64(system, 1, Convert<uint64_t>(out_handle));
+}
+
+static void SvcWrap_CreateIoRegion64(Core::System& system) {
+ Result ret{};
+
+ Handle out_handle{};
+ Handle io_pool{};
+ uint64_t physical_address{};
+ uint64_t size{};
+ MemoryMapping mapping{};
+ MemoryPermission perm{};
+
+ io_pool = Convert<Handle>(GetReg64(system, 1));
+ physical_address = Convert<uint64_t>(GetReg64(system, 2));
+ size = Convert<uint64_t>(GetReg64(system, 3));
+ mapping = Convert<MemoryMapping>(GetReg64(system, 4));
+ perm = Convert<MemoryPermission>(GetReg64(system, 5));
+
+ ret = CreateIoRegion64(system, &out_handle, io_pool, physical_address, size, mapping, perm);
+
+ SetReg64(system, 0, Convert<uint64_t>(ret));
+ SetReg64(system, 1, Convert<uint64_t>(out_handle));
+}
+
+static void SvcWrap_KernelDebug64(Core::System& system) {
+ KernelDebugType kern_debug_type{};
+ uint64_t arg0{};
+ uint64_t arg1{};
+ uint64_t arg2{};
+
+ kern_debug_type = Convert<KernelDebugType>(GetReg64(system, 0));
+ arg0 = Convert<uint64_t>(GetReg64(system, 1));
+ arg1 = Convert<uint64_t>(GetReg64(system, 2));
+ arg2 = Convert<uint64_t>(GetReg64(system, 3));
+
+ KernelDebug64(system, kern_debug_type, arg0, arg1, arg2);
+}
+
+static void SvcWrap_ChangeKernelTraceState64(Core::System& system) {
+ KernelTraceState kern_trace_state{};
+
+ kern_trace_state = Convert<KernelTraceState>(GetReg64(system, 0));
+
+ ChangeKernelTraceState64(system, kern_trace_state);
+}
- *out_num_threads = static_cast<u32>(num_threads);
- return ResultSuccess;
-}
-
-static Result FlushProcessDataCache32(Core::System& system, Handle process_handle, u64 address,
- u64 size) {
- // Validate address/size.
- R_UNLESS(size > 0, ResultInvalidSize);
- R_UNLESS(address == static_cast<uintptr_t>(address), ResultInvalidCurrentMemory);
- R_UNLESS(size == static_cast<size_t>(size), ResultInvalidCurrentMemory);
-
- // Get the process from its handle.
- KScopedAutoObject process =
- system.Kernel().CurrentProcess()->GetHandleTable().GetObject<KProcess>(process_handle);
- R_UNLESS(process.IsNotNull(), ResultInvalidHandle);
-
- // Verify the region is within range.
- auto& page_table = process->PageTable();
- R_UNLESS(page_table.Contains(address, size), ResultInvalidCurrentMemory);
-
- // Perform the operation.
- R_RETURN(system.Memory().FlushDataCache(*process, address, size));
-}
-
-namespace {
-struct FunctionDef {
- using Func = void(Core::System&);
-
- u32 id;
- Func* func;
- const char* name;
-};
-} // namespace
-
-static const FunctionDef SVC_Table_32[] = {
- {0x00, nullptr, "Unknown0"},
- {0x01, SvcWrap32<SetHeapSize32>, "SetHeapSize32"},
- {0x02, nullptr, "SetMemoryPermission32"},
- {0x03, SvcWrap32<SetMemoryAttribute32>, "SetMemoryAttribute32"},
- {0x04, SvcWrap32<MapMemory32>, "MapMemory32"},
- {0x05, SvcWrap32<UnmapMemory32>, "UnmapMemory32"},
- {0x06, SvcWrap32<QueryMemory32>, "QueryMemory32"},
- {0x07, SvcWrap32<ExitProcess32>, "ExitProcess32"},
- {0x08, SvcWrap32<CreateThread32>, "CreateThread32"},
- {0x09, SvcWrap32<StartThread32>, "StartThread32"},
- {0x0a, SvcWrap32<ExitThread32>, "ExitThread32"},
- {0x0b, SvcWrap32<SleepThread32>, "SleepThread32"},
- {0x0c, SvcWrap32<GetThreadPriority32>, "GetThreadPriority32"},
- {0x0d, SvcWrap32<SetThreadPriority32>, "SetThreadPriority32"},
- {0x0e, SvcWrap32<GetThreadCoreMask32>, "GetThreadCoreMask32"},
- {0x0f, SvcWrap32<SetThreadCoreMask32>, "SetThreadCoreMask32"},
- {0x10, SvcWrap32<GetCurrentProcessorNumber32>, "GetCurrentProcessorNumber32"},
- {0x11, SvcWrap32<SignalEvent32>, "SignalEvent32"},
- {0x12, SvcWrap32<ClearEvent32>, "ClearEvent32"},
- {0x13, SvcWrap32<MapSharedMemory32>, "MapSharedMemory32"},
- {0x14, SvcWrap32<UnmapSharedMemory32>, "UnmapSharedMemory32"},
- {0x15, SvcWrap32<CreateTransferMemory32>, "CreateTransferMemory32"},
- {0x16, SvcWrap32<CloseHandle32>, "CloseHandle32"},
- {0x17, SvcWrap32<ResetSignal32>, "ResetSignal32"},
- {0x18, SvcWrap32<WaitSynchronization32>, "WaitSynchronization32"},
- {0x19, SvcWrap32<CancelSynchronization32>, "CancelSynchronization32"},
- {0x1a, SvcWrap32<ArbitrateLock32>, "ArbitrateLock32"},
- {0x1b, SvcWrap32<ArbitrateUnlock32>, "ArbitrateUnlock32"},
- {0x1c, SvcWrap32<WaitProcessWideKeyAtomic32>, "WaitProcessWideKeyAtomic32"},
- {0x1d, SvcWrap32<SignalProcessWideKey32>, "SignalProcessWideKey32"},
- {0x1e, SvcWrap32<GetSystemTick32>, "GetSystemTick32"},
- {0x1f, SvcWrap32<ConnectToNamedPort32>, "ConnectToNamedPort32"},
- {0x20, nullptr, "SendSyncRequestLight32"},
- {0x21, SvcWrap32<SendSyncRequest32>, "SendSyncRequest32"},
- {0x22, nullptr, "SendSyncRequestWithUserBuffer32"},
- {0x23, nullptr, "SendAsyncRequestWithUserBuffer32"},
- {0x24, SvcWrap32<GetProcessId32>, "GetProcessId32"},
- {0x25, SvcWrap32<GetThreadId32>, "GetThreadId32"},
- {0x26, SvcWrap32<Break32>, "Break32"},
- {0x27, SvcWrap32<OutputDebugString32>, "OutputDebugString32"},
- {0x28, nullptr, "ReturnFromException32"},
- {0x29, SvcWrap32<GetInfo32>, "GetInfo32"},
- {0x2a, nullptr, "FlushEntireDataCache32"},
- {0x2b, nullptr, "FlushDataCache32"},
- {0x2c, SvcWrap32<MapPhysicalMemory32>, "MapPhysicalMemory32"},
- {0x2d, SvcWrap32<UnmapPhysicalMemory32>, "UnmapPhysicalMemory32"},
- {0x2e, nullptr, "GetDebugFutureThreadInfo32"},
- {0x2f, nullptr, "GetLastThreadInfo32"},
- {0x30, nullptr, "GetResourceLimitLimitValue32"},
- {0x31, nullptr, "GetResourceLimitCurrentValue32"},
- {0x32, SvcWrap32<SetThreadActivity32>, "SetThreadActivity32"},
- {0x33, SvcWrap32<GetThreadContext32>, "GetThreadContext32"},
- {0x34, SvcWrap32<WaitForAddress32>, "WaitForAddress32"},
- {0x35, SvcWrap32<SignalToAddress32>, "SignalToAddress32"},
- {0x36, SvcWrap32<SynchronizePreemptionState>, "SynchronizePreemptionState32"},
- {0x37, nullptr, "GetResourceLimitPeakValue32"},
- {0x38, nullptr, "Unknown38"},
- {0x39, nullptr, "CreateIoPool32"},
- {0x3a, nullptr, "CreateIoRegion32"},
- {0x3b, nullptr, "Unknown3b"},
- {0x3c, nullptr, "KernelDebug32"},
- {0x3d, nullptr, "ChangeKernelTraceState32"},
- {0x3e, nullptr, "Unknown3e"},
- {0x3f, nullptr, "Unknown3f"},
- {0x40, nullptr, "CreateSession32"},
- {0x41, nullptr, "AcceptSession32"},
- {0x42, nullptr, "ReplyAndReceiveLight32"},
- {0x43, nullptr, "ReplyAndReceive32"},
- {0x44, nullptr, "ReplyAndReceiveWithUserBuffer32"},
- {0x45, SvcWrap32<CreateEvent32>, "CreateEvent32"},
- {0x46, nullptr, "MapIoRegion32"},
- {0x47, nullptr, "UnmapIoRegion32"},
- {0x48, nullptr, "MapPhysicalMemoryUnsafe32"},
- {0x49, nullptr, "UnmapPhysicalMemoryUnsafe32"},
- {0x4a, nullptr, "SetUnsafeLimit32"},
- {0x4b, SvcWrap32<CreateCodeMemory32>, "CreateCodeMemory32"},
- {0x4c, SvcWrap32<ControlCodeMemory32>, "ControlCodeMemory32"},
- {0x4d, nullptr, "SleepSystem32"},
- {0x4e, nullptr, "ReadWriteRegister32"},
- {0x4f, nullptr, "SetProcessActivity32"},
- {0x50, nullptr, "CreateSharedMemory32"},
- {0x51, nullptr, "MapTransferMemory32"},
- {0x52, nullptr, "UnmapTransferMemory32"},
- {0x53, nullptr, "CreateInterruptEvent32"},
- {0x54, nullptr, "QueryPhysicalAddress32"},
- {0x55, nullptr, "QueryIoMapping32"},
- {0x56, nullptr, "CreateDeviceAddressSpace32"},
- {0x57, nullptr, "AttachDeviceAddressSpace32"},
- {0x58, nullptr, "DetachDeviceAddressSpace32"},
- {0x59, nullptr, "MapDeviceAddressSpaceByForce32"},
- {0x5a, nullptr, "MapDeviceAddressSpaceAligned32"},
- {0x5b, nullptr, "MapDeviceAddressSpace32"},
- {0x5c, nullptr, "UnmapDeviceAddressSpace32"},
- {0x5d, nullptr, "InvalidateProcessDataCache32"},
- {0x5e, nullptr, "StoreProcessDataCache32"},
- {0x5F, SvcWrap32<FlushProcessDataCache32>, "FlushProcessDataCache32"},
- {0x60, nullptr, "StoreProcessDataCache32"},
- {0x61, nullptr, "BreakDebugProcess32"},
- {0x62, nullptr, "TerminateDebugProcess32"},
- {0x63, nullptr, "GetDebugEvent32"},
- {0x64, nullptr, "ContinueDebugEvent32"},
- {0x65, nullptr, "GetProcessList32"},
- {0x66, nullptr, "GetThreadList"},
- {0x67, nullptr, "GetDebugThreadContext32"},
- {0x68, nullptr, "SetDebugThreadContext32"},
- {0x69, nullptr, "QueryDebugProcessMemory32"},
- {0x6A, nullptr, "ReadDebugProcessMemory32"},
- {0x6B, nullptr, "WriteDebugProcessMemory32"},
- {0x6C, nullptr, "SetHardwareBreakPoint32"},
- {0x6D, nullptr, "GetDebugThreadParam32"},
- {0x6E, nullptr, "Unknown6E"},
- {0x6f, nullptr, "GetSystemInfo32"},
- {0x70, nullptr, "CreatePort32"},
- {0x71, nullptr, "ManageNamedPort32"},
- {0x72, nullptr, "ConnectToPort32"},
- {0x73, nullptr, "SetProcessMemoryPermission32"},
- {0x74, nullptr, "MapProcessMemory32"},
- {0x75, nullptr, "UnmapProcessMemory32"},
- {0x76, nullptr, "QueryProcessMemory32"},
- {0x77, nullptr, "MapProcessCodeMemory32"},
- {0x78, nullptr, "UnmapProcessCodeMemory32"},
- {0x79, nullptr, "CreateProcess32"},
- {0x7A, nullptr, "StartProcess32"},
- {0x7B, nullptr, "TerminateProcess32"},
- {0x7C, nullptr, "GetProcessInfo32"},
- {0x7D, nullptr, "CreateResourceLimit32"},
- {0x7E, nullptr, "SetResourceLimitLimitValue32"},
- {0x7F, nullptr, "CallSecureMonitor32"},
- {0x80, nullptr, "Unknown"},
- {0x81, nullptr, "Unknown"},
- {0x82, nullptr, "Unknown"},
- {0x83, nullptr, "Unknown"},
- {0x84, nullptr, "Unknown"},
- {0x85, nullptr, "Unknown"},
- {0x86, nullptr, "Unknown"},
- {0x87, nullptr, "Unknown"},
- {0x88, nullptr, "Unknown"},
- {0x89, nullptr, "Unknown"},
- {0x8A, nullptr, "Unknown"},
- {0x8B, nullptr, "Unknown"},
- {0x8C, nullptr, "Unknown"},
- {0x8D, nullptr, "Unknown"},
- {0x8E, nullptr, "Unknown"},
- {0x8F, nullptr, "Unknown"},
- {0x90, nullptr, "Unknown"},
- {0x91, nullptr, "Unknown"},
- {0x92, nullptr, "Unknown"},
- {0x93, nullptr, "Unknown"},
- {0x94, nullptr, "Unknown"},
- {0x95, nullptr, "Unknown"},
- {0x96, nullptr, "Unknown"},
- {0x97, nullptr, "Unknown"},
- {0x98, nullptr, "Unknown"},
- {0x99, nullptr, "Unknown"},
- {0x9A, nullptr, "Unknown"},
- {0x9B, nullptr, "Unknown"},
- {0x9C, nullptr, "Unknown"},
- {0x9D, nullptr, "Unknown"},
- {0x9E, nullptr, "Unknown"},
- {0x9F, nullptr, "Unknown"},
- {0xA0, nullptr, "Unknown"},
- {0xA1, nullptr, "Unknown"},
- {0xA2, nullptr, "Unknown"},
- {0xA3, nullptr, "Unknown"},
- {0xA4, nullptr, "Unknown"},
- {0xA5, nullptr, "Unknown"},
- {0xA6, nullptr, "Unknown"},
- {0xA7, nullptr, "Unknown"},
- {0xA8, nullptr, "Unknown"},
- {0xA9, nullptr, "Unknown"},
- {0xAA, nullptr, "Unknown"},
- {0xAB, nullptr, "Unknown"},
- {0xAC, nullptr, "Unknown"},
- {0xAD, nullptr, "Unknown"},
- {0xAE, nullptr, "Unknown"},
- {0xAF, nullptr, "Unknown"},
- {0xB0, nullptr, "Unknown"},
- {0xB1, nullptr, "Unknown"},
- {0xB2, nullptr, "Unknown"},
- {0xB3, nullptr, "Unknown"},
- {0xB4, nullptr, "Unknown"},
- {0xB5, nullptr, "Unknown"},
- {0xB6, nullptr, "Unknown"},
- {0xB7, nullptr, "Unknown"},
- {0xB8, nullptr, "Unknown"},
- {0xB9, nullptr, "Unknown"},
- {0xBA, nullptr, "Unknown"},
- {0xBB, nullptr, "Unknown"},
- {0xBC, nullptr, "Unknown"},
- {0xBD, nullptr, "Unknown"},
- {0xBE, nullptr, "Unknown"},
- {0xBF, nullptr, "Unknown"},
-};
-
-static const FunctionDef SVC_Table_64[] = {
- {0x00, nullptr, "Unknown0"},
- {0x01, SvcWrap64<SetHeapSize>, "SetHeapSize"},
- {0x02, SvcWrap64<SetMemoryPermission>, "SetMemoryPermission"},
- {0x03, SvcWrap64<SetMemoryAttribute>, "SetMemoryAttribute"},
- {0x04, SvcWrap64<MapMemory>, "MapMemory"},
- {0x05, SvcWrap64<UnmapMemory>, "UnmapMemory"},
- {0x06, SvcWrap64<QueryMemory>, "QueryMemory"},
- {0x07, SvcWrap64<ExitProcess>, "ExitProcess"},
- {0x08, SvcWrap64<CreateThread>, "CreateThread"},
- {0x09, SvcWrap64<StartThread>, "StartThread"},
- {0x0A, SvcWrap64<ExitThread>, "ExitThread"},
- {0x0B, SvcWrap64<SleepThread>, "SleepThread"},
- {0x0C, SvcWrap64<GetThreadPriority>, "GetThreadPriority"},
- {0x0D, SvcWrap64<SetThreadPriority>, "SetThreadPriority"},
- {0x0E, SvcWrap64<GetThreadCoreMask>, "GetThreadCoreMask"},
- {0x0F, SvcWrap64<SetThreadCoreMask>, "SetThreadCoreMask"},
- {0x10, SvcWrap64<GetCurrentProcessorNumber>, "GetCurrentProcessorNumber"},
- {0x11, SvcWrap64<SignalEvent>, "SignalEvent"},
- {0x12, SvcWrap64<ClearEvent>, "ClearEvent"},
- {0x13, SvcWrap64<MapSharedMemory>, "MapSharedMemory"},
- {0x14, SvcWrap64<UnmapSharedMemory>, "UnmapSharedMemory"},
- {0x15, SvcWrap64<CreateTransferMemory>, "CreateTransferMemory"},
- {0x16, SvcWrap64<CloseHandle>, "CloseHandle"},
- {0x17, SvcWrap64<ResetSignal>, "ResetSignal"},
- {0x18, SvcWrap64<WaitSynchronization>, "WaitSynchronization"},
- {0x19, SvcWrap64<CancelSynchronization>, "CancelSynchronization"},
- {0x1A, SvcWrap64<ArbitrateLock>, "ArbitrateLock"},
- {0x1B, SvcWrap64<ArbitrateUnlock>, "ArbitrateUnlock"},
- {0x1C, SvcWrap64<WaitProcessWideKeyAtomic>, "WaitProcessWideKeyAtomic"},
- {0x1D, SvcWrap64<SignalProcessWideKey>, "SignalProcessWideKey"},
- {0x1E, SvcWrap64<GetSystemTick>, "GetSystemTick"},
- {0x1F, SvcWrap64<ConnectToNamedPort>, "ConnectToNamedPort"},
- {0x20, nullptr, "SendSyncRequestLight"},
- {0x21, SvcWrap64<SendSyncRequest>, "SendSyncRequest"},
- {0x22, nullptr, "SendSyncRequestWithUserBuffer"},
- {0x23, nullptr, "SendAsyncRequestWithUserBuffer"},
- {0x24, SvcWrap64<GetProcessId>, "GetProcessId"},
- {0x25, SvcWrap64<GetThreadId>, "GetThreadId"},
- {0x26, SvcWrap64<Break>, "Break"},
- {0x27, SvcWrap64<OutputDebugString>, "OutputDebugString"},
- {0x28, nullptr, "ReturnFromException"},
- {0x29, SvcWrap64<GetInfo>, "GetInfo"},
- {0x2A, nullptr, "FlushEntireDataCache"},
- {0x2B, nullptr, "FlushDataCache"},
- {0x2C, SvcWrap64<MapPhysicalMemory>, "MapPhysicalMemory"},
- {0x2D, SvcWrap64<UnmapPhysicalMemory>, "UnmapPhysicalMemory"},
- {0x2E, nullptr, "GetFutureThreadInfo"},
- {0x2F, nullptr, "GetLastThreadInfo"},
- {0x30, SvcWrap64<GetResourceLimitLimitValue>, "GetResourceLimitLimitValue"},
- {0x31, SvcWrap64<GetResourceLimitCurrentValue>, "GetResourceLimitCurrentValue"},
- {0x32, SvcWrap64<SetThreadActivity>, "SetThreadActivity"},
- {0x33, SvcWrap64<GetThreadContext>, "GetThreadContext"},
- {0x34, SvcWrap64<WaitForAddress>, "WaitForAddress"},
- {0x35, SvcWrap64<SignalToAddress>, "SignalToAddress"},
- {0x36, SvcWrap64<SynchronizePreemptionState>, "SynchronizePreemptionState"},
- {0x37, nullptr, "GetResourceLimitPeakValue"},
- {0x38, nullptr, "Unknown38"},
- {0x39, nullptr, "CreateIoPool"},
- {0x3A, nullptr, "CreateIoRegion"},
- {0x3B, nullptr, "Unknown3B"},
- {0x3C, SvcWrap64<KernelDebug>, "KernelDebug"},
- {0x3D, SvcWrap64<ChangeKernelTraceState>, "ChangeKernelTraceState"},
- {0x3E, nullptr, "Unknown3e"},
- {0x3F, nullptr, "Unknown3f"},
- {0x40, SvcWrap64<CreateSession>, "CreateSession"},
- {0x41, nullptr, "AcceptSession"},
- {0x42, nullptr, "ReplyAndReceiveLight"},
- {0x43, SvcWrap64<ReplyAndReceive>, "ReplyAndReceive"},
- {0x44, nullptr, "ReplyAndReceiveWithUserBuffer"},
- {0x45, SvcWrap64<CreateEvent>, "CreateEvent"},
- {0x46, nullptr, "MapIoRegion"},
- {0x47, nullptr, "UnmapIoRegion"},
- {0x48, nullptr, "MapPhysicalMemoryUnsafe"},
- {0x49, nullptr, "UnmapPhysicalMemoryUnsafe"},
- {0x4A, nullptr, "SetUnsafeLimit"},
- {0x4B, SvcWrap64<CreateCodeMemory>, "CreateCodeMemory"},
- {0x4C, SvcWrap64<ControlCodeMemory>, "ControlCodeMemory"},
- {0x4D, nullptr, "SleepSystem"},
- {0x4E, nullptr, "ReadWriteRegister"},
- {0x4F, nullptr, "SetProcessActivity"},
- {0x50, nullptr, "CreateSharedMemory"},
- {0x51, nullptr, "MapTransferMemory"},
- {0x52, nullptr, "UnmapTransferMemory"},
- {0x53, nullptr, "CreateInterruptEvent"},
- {0x54, nullptr, "QueryPhysicalAddress"},
- {0x55, nullptr, "QueryIoMapping"},
- {0x56, nullptr, "CreateDeviceAddressSpace"},
- {0x57, nullptr, "AttachDeviceAddressSpace"},
- {0x58, nullptr, "DetachDeviceAddressSpace"},
- {0x59, nullptr, "MapDeviceAddressSpaceByForce"},
- {0x5A, nullptr, "MapDeviceAddressSpaceAligned"},
- {0x5B, nullptr, "MapDeviceAddressSpace"},
- {0x5C, nullptr, "UnmapDeviceAddressSpace"},
- {0x5D, nullptr, "InvalidateProcessDataCache"},
- {0x5E, nullptr, "StoreProcessDataCache"},
- {0x5F, nullptr, "FlushProcessDataCache"},
- {0x60, nullptr, "DebugActiveProcess"},
- {0x61, nullptr, "BreakDebugProcess"},
- {0x62, nullptr, "TerminateDebugProcess"},
- {0x63, nullptr, "GetDebugEvent"},
- {0x64, nullptr, "ContinueDebugEvent"},
- {0x65, SvcWrap64<GetProcessList>, "GetProcessList"},
- {0x66, SvcWrap64<GetThreadList>, "GetThreadList"},
- {0x67, nullptr, "GetDebugThreadContext"},
- {0x68, nullptr, "SetDebugThreadContext"},
- {0x69, nullptr, "QueryDebugProcessMemory"},
- {0x6A, nullptr, "ReadDebugProcessMemory"},
- {0x6B, nullptr, "WriteDebugProcessMemory"},
- {0x6C, nullptr, "SetHardwareBreakPoint"},
- {0x6D, nullptr, "GetDebugThreadParam"},
- {0x6E, nullptr, "Unknown6E"},
- {0x6F, nullptr, "GetSystemInfo"},
- {0x70, nullptr, "CreatePort"},
- {0x71, nullptr, "ManageNamedPort"},
- {0x72, nullptr, "ConnectToPort"},
- {0x73, SvcWrap64<SetProcessMemoryPermission>, "SetProcessMemoryPermission"},
- {0x74, SvcWrap64<MapProcessMemory>, "MapProcessMemory"},
- {0x75, SvcWrap64<UnmapProcessMemory>, "UnmapProcessMemory"},
- {0x76, SvcWrap64<QueryProcessMemory>, "QueryProcessMemory"},
- {0x77, SvcWrap64<MapProcessCodeMemory>, "MapProcessCodeMemory"},
- {0x78, SvcWrap64<UnmapProcessCodeMemory>, "UnmapProcessCodeMemory"},
- {0x79, nullptr, "CreateProcess"},
- {0x7A, nullptr, "StartProcess"},
- {0x7B, nullptr, "TerminateProcess"},
- {0x7C, SvcWrap64<GetProcessInfo>, "GetProcessInfo"},
- {0x7D, SvcWrap64<CreateResourceLimit>, "CreateResourceLimit"},
- {0x7E, SvcWrap64<SetResourceLimitLimitValue>, "SetResourceLimitLimitValue"},
- {0x7F, nullptr, "CallSecureMonitor"},
- {0x80, nullptr, "Unknown"},
- {0x81, nullptr, "Unknown"},
- {0x82, nullptr, "Unknown"},
- {0x83, nullptr, "Unknown"},
- {0x84, nullptr, "Unknown"},
- {0x85, nullptr, "Unknown"},
- {0x86, nullptr, "Unknown"},
- {0x87, nullptr, "Unknown"},
- {0x88, nullptr, "Unknown"},
- {0x89, nullptr, "Unknown"},
- {0x8A, nullptr, "Unknown"},
- {0x8B, nullptr, "Unknown"},
- {0x8C, nullptr, "Unknown"},
- {0x8D, nullptr, "Unknown"},
- {0x8E, nullptr, "Unknown"},
- {0x8F, nullptr, "Unknown"},
- {0x90, nullptr, "Unknown"},
- {0x91, nullptr, "Unknown"},
- {0x92, nullptr, "Unknown"},
- {0x93, nullptr, "Unknown"},
- {0x94, nullptr, "Unknown"},
- {0x95, nullptr, "Unknown"},
- {0x96, nullptr, "Unknown"},
- {0x97, nullptr, "Unknown"},
- {0x98, nullptr, "Unknown"},
- {0x99, nullptr, "Unknown"},
- {0x9A, nullptr, "Unknown"},
- {0x9B, nullptr, "Unknown"},
- {0x9C, nullptr, "Unknown"},
- {0x9D, nullptr, "Unknown"},
- {0x9E, nullptr, "Unknown"},
- {0x9F, nullptr, "Unknown"},
- {0xA0, nullptr, "Unknown"},
- {0xA1, nullptr, "Unknown"},
- {0xA2, nullptr, "Unknown"},
- {0xA3, nullptr, "Unknown"},
- {0xA4, nullptr, "Unknown"},
- {0xA5, nullptr, "Unknown"},
- {0xA6, nullptr, "Unknown"},
- {0xA7, nullptr, "Unknown"},
- {0xA8, nullptr, "Unknown"},
- {0xA9, nullptr, "Unknown"},
- {0xAA, nullptr, "Unknown"},
- {0xAB, nullptr, "Unknown"},
- {0xAC, nullptr, "Unknown"},
- {0xAD, nullptr, "Unknown"},
- {0xAE, nullptr, "Unknown"},
- {0xAF, nullptr, "Unknown"},
- {0xB0, nullptr, "Unknown"},
- {0xB1, nullptr, "Unknown"},
- {0xB2, nullptr, "Unknown"},
- {0xB3, nullptr, "Unknown"},
- {0xB4, nullptr, "Unknown"},
- {0xB5, nullptr, "Unknown"},
- {0xB6, nullptr, "Unknown"},
- {0xB7, nullptr, "Unknown"},
- {0xB8, nullptr, "Unknown"},
- {0xB9, nullptr, "Unknown"},
- {0xBA, nullptr, "Unknown"},
- {0xBB, nullptr, "Unknown"},
- {0xBC, nullptr, "Unknown"},
- {0xBD, nullptr, "Unknown"},
- {0xBE, nullptr, "Unknown"},
- {0xBF, nullptr, "Unknown"},
-};
-
-static const FunctionDef* GetSVCInfo32(u32 func_num) {
- if (func_num >= std::size(SVC_Table_32)) {
- LOG_ERROR(Kernel_SVC, "Unknown svc=0x{:02X}", func_num);
- return nullptr;
+static void SvcWrap_CreateSession64(Core::System& system) {
+ Result ret{};
+
+ Handle out_server_session_handle{};
+ Handle out_client_session_handle{};
+ bool is_light{};
+ uint64_t name{};
+
+ is_light = Convert<bool>(GetReg64(system, 2));
+ name = Convert<uint64_t>(GetReg64(system, 3));
+
+ ret = CreateSession64(system, &out_server_session_handle, &out_client_session_handle, is_light, name);
+
+ SetReg64(system, 0, Convert<uint64_t>(ret));
+ SetReg64(system, 1, Convert<uint64_t>(out_server_session_handle));
+ SetReg64(system, 2, Convert<uint64_t>(out_client_session_handle));
+}
+
+static void SvcWrap_AcceptSession64(Core::System& system) {
+ Result ret{};
+
+ Handle out_handle{};
+ Handle port{};
+
+ port = Convert<Handle>(GetReg64(system, 1));
+
+ ret = AcceptSession64(system, &out_handle, port);
+
+ SetReg64(system, 0, Convert<uint64_t>(ret));
+ SetReg64(system, 1, Convert<uint64_t>(out_handle));
+}
+
+static void SvcWrap_ReplyAndReceive64(Core::System& system) {
+ Result ret{};
+
+ int32_t out_index{};
+ uint64_t handles{};
+ int32_t num_handles{};
+ Handle reply_target{};
+ int64_t timeout_ns{};
+
+ handles = Convert<uint64_t>(GetReg64(system, 1));
+ num_handles = Convert<int32_t>(GetReg64(system, 2));
+ reply_target = Convert<Handle>(GetReg64(system, 3));
+ timeout_ns = Convert<int64_t>(GetReg64(system, 4));
+
+ ret = ReplyAndReceive64(system, &out_index, handles, num_handles, reply_target, timeout_ns);
+
+ SetReg64(system, 0, Convert<uint64_t>(ret));
+ SetReg64(system, 1, Convert<uint64_t>(out_index));
+}
+
+static void SvcWrap_ReplyAndReceiveWithUserBuffer64(Core::System& system) {
+ Result ret{};
+
+ int32_t out_index{};
+ uint64_t message_buffer{};
+ uint64_t message_buffer_size{};
+ uint64_t handles{};
+ int32_t num_handles{};
+ Handle reply_target{};
+ int64_t timeout_ns{};
+
+ message_buffer = Convert<uint64_t>(GetReg64(system, 1));
+ message_buffer_size = Convert<uint64_t>(GetReg64(system, 2));
+ handles = Convert<uint64_t>(GetReg64(system, 3));
+ num_handles = Convert<int32_t>(GetReg64(system, 4));
+ reply_target = Convert<Handle>(GetReg64(system, 5));
+ timeout_ns = Convert<int64_t>(GetReg64(system, 6));
+
+ ret = ReplyAndReceiveWithUserBuffer64(system, &out_index, message_buffer, message_buffer_size, handles, num_handles, reply_target, timeout_ns);
+
+ SetReg64(system, 0, Convert<uint64_t>(ret));
+ SetReg64(system, 1, Convert<uint64_t>(out_index));
+}
+
+static void SvcWrap_CreateEvent64(Core::System& system) {
+ Result ret{};
+
+ Handle out_write_handle{};
+ Handle out_read_handle{};
+
+ ret = CreateEvent64(system, &out_write_handle, &out_read_handle);
+
+ SetReg64(system, 0, Convert<uint64_t>(ret));
+ SetReg64(system, 1, Convert<uint64_t>(out_write_handle));
+ SetReg64(system, 2, Convert<uint64_t>(out_read_handle));
+}
+
+static void SvcWrap_MapIoRegion64(Core::System& system) {
+ Result ret{};
+
+ Handle io_region{};
+ uint64_t address{};
+ uint64_t size{};
+ MemoryPermission perm{};
+
+ io_region = Convert<Handle>(GetReg64(system, 0));
+ address = Convert<uint64_t>(GetReg64(system, 1));
+ size = Convert<uint64_t>(GetReg64(system, 2));
+ perm = Convert<MemoryPermission>(GetReg64(system, 3));
+
+ ret = MapIoRegion64(system, io_region, address, size, perm);
+
+ SetReg64(system, 0, Convert<uint64_t>(ret));
+}
+
+static void SvcWrap_UnmapIoRegion64(Core::System& system) {
+ Result ret{};
+
+ Handle io_region{};
+ uint64_t address{};
+ uint64_t size{};
+
+ io_region = Convert<Handle>(GetReg64(system, 0));
+ address = Convert<uint64_t>(GetReg64(system, 1));
+ size = Convert<uint64_t>(GetReg64(system, 2));
+
+ ret = UnmapIoRegion64(system, io_region, address, size);
+
+ SetReg64(system, 0, Convert<uint64_t>(ret));
+}
+
+static void SvcWrap_MapPhysicalMemoryUnsafe64(Core::System& system) {
+ Result ret{};
+
+ uint64_t address{};
+ uint64_t size{};
+
+ address = Convert<uint64_t>(GetReg64(system, 0));
+ size = Convert<uint64_t>(GetReg64(system, 1));
+
+ ret = MapPhysicalMemoryUnsafe64(system, address, size);
+
+ SetReg64(system, 0, Convert<uint64_t>(ret));
+}
+
+static void SvcWrap_UnmapPhysicalMemoryUnsafe64(Core::System& system) {
+ Result ret{};
+
+ uint64_t address{};
+ uint64_t size{};
+
+ address = Convert<uint64_t>(GetReg64(system, 0));
+ size = Convert<uint64_t>(GetReg64(system, 1));
+
+ ret = UnmapPhysicalMemoryUnsafe64(system, address, size);
+
+ SetReg64(system, 0, Convert<uint64_t>(ret));
+}
+
+static void SvcWrap_SetUnsafeLimit64(Core::System& system) {
+ Result ret{};
+
+ uint64_t limit{};
+
+ limit = Convert<uint64_t>(GetReg64(system, 0));
+
+ ret = SetUnsafeLimit64(system, limit);
+
+ SetReg64(system, 0, Convert<uint64_t>(ret));
+}
+
+static void SvcWrap_CreateCodeMemory64(Core::System& system) {
+ Result ret{};
+
+ Handle out_handle{};
+ uint64_t address{};
+ uint64_t size{};
+
+ address = Convert<uint64_t>(GetReg64(system, 1));
+ size = Convert<uint64_t>(GetReg64(system, 2));
+
+ ret = CreateCodeMemory64(system, &out_handle, address, size);
+
+ SetReg64(system, 0, Convert<uint64_t>(ret));
+ SetReg64(system, 1, Convert<uint64_t>(out_handle));
+}
+
+static void SvcWrap_ControlCodeMemory64(Core::System& system) {
+ Result ret{};
+
+ Handle code_memory_handle{};
+ CodeMemoryOperation operation{};
+ uint64_t address{};
+ uint64_t size{};
+ MemoryPermission perm{};
+
+ code_memory_handle = Convert<Handle>(GetReg64(system, 0));
+ operation = Convert<CodeMemoryOperation>(GetReg64(system, 1));
+ address = Convert<uint64_t>(GetReg64(system, 2));
+ size = Convert<uint64_t>(GetReg64(system, 3));
+ perm = Convert<MemoryPermission>(GetReg64(system, 4));
+
+ ret = ControlCodeMemory64(system, code_memory_handle, operation, address, size, perm);
+
+ SetReg64(system, 0, Convert<uint64_t>(ret));
+}
+
+static void SvcWrap_SleepSystem64(Core::System& system) {
+ SleepSystem64(system);
+}
+
+static void SvcWrap_ReadWriteRegister64(Core::System& system) {
+ Result ret{};
+
+ uint32_t out_value{};
+ uint64_t address{};
+ uint32_t mask{};
+ uint32_t value{};
+
+ address = Convert<uint64_t>(GetReg64(system, 1));
+ mask = Convert<uint32_t>(GetReg64(system, 2));
+ value = Convert<uint32_t>(GetReg64(system, 3));
+
+ ret = ReadWriteRegister64(system, &out_value, address, mask, value);
+
+ SetReg64(system, 0, Convert<uint64_t>(ret));
+ SetReg64(system, 1, Convert<uint64_t>(out_value));
+}
+
+static void SvcWrap_SetProcessActivity64(Core::System& system) {
+ Result ret{};
+
+ Handle process_handle{};
+ ProcessActivity process_activity{};
+
+ process_handle = Convert<Handle>(GetReg64(system, 0));
+ process_activity = Convert<ProcessActivity>(GetReg64(system, 1));
+
+ ret = SetProcessActivity64(system, process_handle, process_activity);
+
+ SetReg64(system, 0, Convert<uint64_t>(ret));
+}
+
+static void SvcWrap_CreateSharedMemory64(Core::System& system) {
+ Result ret{};
+
+ Handle out_handle{};
+ uint64_t size{};
+ MemoryPermission owner_perm{};
+ MemoryPermission remote_perm{};
+
+ size = Convert<uint64_t>(GetReg64(system, 1));
+ owner_perm = Convert<MemoryPermission>(GetReg64(system, 2));
+ remote_perm = Convert<MemoryPermission>(GetReg64(system, 3));
+
+ ret = CreateSharedMemory64(system, &out_handle, size, owner_perm, remote_perm);
+
+ SetReg64(system, 0, Convert<uint64_t>(ret));
+ SetReg64(system, 1, Convert<uint64_t>(out_handle));
+}
+
+static void SvcWrap_MapTransferMemory64(Core::System& system) {
+ Result ret{};
+
+ Handle trmem_handle{};
+ uint64_t address{};
+ uint64_t size{};
+ MemoryPermission owner_perm{};
+
+ trmem_handle = Convert<Handle>(GetReg64(system, 0));
+ address = Convert<uint64_t>(GetReg64(system, 1));
+ size = Convert<uint64_t>(GetReg64(system, 2));
+ owner_perm = Convert<MemoryPermission>(GetReg64(system, 3));
+
+ ret = MapTransferMemory64(system, trmem_handle, address, size, owner_perm);
+
+ SetReg64(system, 0, Convert<uint64_t>(ret));
+}
+
+static void SvcWrap_UnmapTransferMemory64(Core::System& system) {
+ Result ret{};
+
+ Handle trmem_handle{};
+ uint64_t address{};
+ uint64_t size{};
+
+ trmem_handle = Convert<Handle>(GetReg64(system, 0));
+ address = Convert<uint64_t>(GetReg64(system, 1));
+ size = Convert<uint64_t>(GetReg64(system, 2));
+
+ ret = UnmapTransferMemory64(system, trmem_handle, address, size);
+
+ SetReg64(system, 0, Convert<uint64_t>(ret));
+}
+
+static void SvcWrap_CreateInterruptEvent64(Core::System& system) {
+ Result ret{};
+
+ Handle out_read_handle{};
+ int32_t interrupt_id{};
+ InterruptType interrupt_type{};
+
+ interrupt_id = Convert<int32_t>(GetReg64(system, 1));
+ interrupt_type = Convert<InterruptType>(GetReg64(system, 2));
+
+ ret = CreateInterruptEvent64(system, &out_read_handle, interrupt_id, interrupt_type);
+
+ SetReg64(system, 0, Convert<uint64_t>(ret));
+ SetReg64(system, 1, Convert<uint64_t>(out_read_handle));
+}
+
+static void SvcWrap_QueryPhysicalAddress64(Core::System& system) {
+ Result ret{};
+
+ lp64::PhysicalMemoryInfo out_info{};
+ uint64_t address{};
+
+ address = Convert<uint64_t>(GetReg64(system, 1));
+
+ ret = QueryPhysicalAddress64(system, &out_info, address);
+
+ SetReg64(system, 0, Convert<uint64_t>(ret));
+ auto out_info_scatter = Convert<std::array<uint64_t, 3>>(out_info);
+ SetReg64(system, 1, out_info_scatter[0]);
+ SetReg64(system, 2, out_info_scatter[1]);
+ SetReg64(system, 3, out_info_scatter[2]);
+}
+
+static void SvcWrap_QueryIoMapping64(Core::System& system) {
+ Result ret{};
+
+ uintptr_t out_address{};
+ uintptr_t out_size{};
+ uint64_t physical_address{};
+ uint64_t size{};
+
+ physical_address = Convert<uint64_t>(GetReg64(system, 2));
+ size = Convert<uint64_t>(GetReg64(system, 3));
+
+ ret = QueryIoMapping64(system, &out_address, &out_size, physical_address, size);
+
+ SetReg64(system, 0, Convert<uint64_t>(ret));
+ SetReg64(system, 1, Convert<uint64_t>(out_address));
+ SetReg64(system, 2, Convert<uint64_t>(out_size));
+}
+
+static void SvcWrap_CreateDeviceAddressSpace64(Core::System& system) {
+ Result ret{};
+
+ Handle out_handle{};
+ uint64_t das_address{};
+ uint64_t das_size{};
+
+ das_address = Convert<uint64_t>(GetReg64(system, 1));
+ das_size = Convert<uint64_t>(GetReg64(system, 2));
+
+ ret = CreateDeviceAddressSpace64(system, &out_handle, das_address, das_size);
+
+ SetReg64(system, 0, Convert<uint64_t>(ret));
+ SetReg64(system, 1, Convert<uint64_t>(out_handle));
+}
+
+static void SvcWrap_AttachDeviceAddressSpace64(Core::System& system) {
+ Result ret{};
+
+ DeviceName device_name{};
+ Handle das_handle{};
+
+ device_name = Convert<DeviceName>(GetReg64(system, 0));
+ das_handle = Convert<Handle>(GetReg64(system, 1));
+
+ ret = AttachDeviceAddressSpace64(system, device_name, das_handle);
+
+ SetReg64(system, 0, Convert<uint64_t>(ret));
+}
+
+static void SvcWrap_DetachDeviceAddressSpace64(Core::System& system) {
+ Result ret{};
+
+ DeviceName device_name{};
+ Handle das_handle{};
+
+ device_name = Convert<DeviceName>(GetReg64(system, 0));
+ das_handle = Convert<Handle>(GetReg64(system, 1));
+
+ ret = DetachDeviceAddressSpace64(system, device_name, das_handle);
+
+ SetReg64(system, 0, Convert<uint64_t>(ret));
+}
+
+static void SvcWrap_MapDeviceAddressSpaceByForce64(Core::System& system) {
+ Result ret{};
+
+ Handle das_handle{};
+ Handle process_handle{};
+ uint64_t process_address{};
+ uint64_t size{};
+ uint64_t device_address{};
+ uint32_t option{};
+
+ das_handle = Convert<Handle>(GetReg64(system, 0));
+ process_handle = Convert<Handle>(GetReg64(system, 1));
+ process_address = Convert<uint64_t>(GetReg64(system, 2));
+ size = Convert<uint64_t>(GetReg64(system, 3));
+ device_address = Convert<uint64_t>(GetReg64(system, 4));
+ option = Convert<uint32_t>(GetReg64(system, 5));
+
+ ret = MapDeviceAddressSpaceByForce64(system, das_handle, process_handle, process_address, size, device_address, option);
+
+ SetReg64(system, 0, Convert<uint64_t>(ret));
+}
+
+static void SvcWrap_MapDeviceAddressSpaceAligned64(Core::System& system) {
+ Result ret{};
+
+ Handle das_handle{};
+ Handle process_handle{};
+ uint64_t process_address{};
+ uint64_t size{};
+ uint64_t device_address{};
+ uint32_t option{};
+
+ das_handle = Convert<Handle>(GetReg64(system, 0));
+ process_handle = Convert<Handle>(GetReg64(system, 1));
+ process_address = Convert<uint64_t>(GetReg64(system, 2));
+ size = Convert<uint64_t>(GetReg64(system, 3));
+ device_address = Convert<uint64_t>(GetReg64(system, 4));
+ option = Convert<uint32_t>(GetReg64(system, 5));
+
+ ret = MapDeviceAddressSpaceAligned64(system, das_handle, process_handle, process_address, size, device_address, option);
+
+ SetReg64(system, 0, Convert<uint64_t>(ret));
+}
+
+static void SvcWrap_UnmapDeviceAddressSpace64(Core::System& system) {
+ Result ret{};
+
+ Handle das_handle{};
+ Handle process_handle{};
+ uint64_t process_address{};
+ uint64_t size{};
+ uint64_t device_address{};
+
+ das_handle = Convert<Handle>(GetReg64(system, 0));
+ process_handle = Convert<Handle>(GetReg64(system, 1));
+ process_address = Convert<uint64_t>(GetReg64(system, 2));
+ size = Convert<uint64_t>(GetReg64(system, 3));
+ device_address = Convert<uint64_t>(GetReg64(system, 4));
+
+ ret = UnmapDeviceAddressSpace64(system, das_handle, process_handle, process_address, size, device_address);
+
+ SetReg64(system, 0, Convert<uint64_t>(ret));
+}
+
+static void SvcWrap_InvalidateProcessDataCache64(Core::System& system) {
+ Result ret{};
+
+ Handle process_handle{};
+ uint64_t address{};
+ uint64_t size{};
+
+ process_handle = Convert<Handle>(GetReg64(system, 0));
+ address = Convert<uint64_t>(GetReg64(system, 1));
+ size = Convert<uint64_t>(GetReg64(system, 2));
+
+ ret = InvalidateProcessDataCache64(system, process_handle, address, size);
+
+ SetReg64(system, 0, Convert<uint64_t>(ret));
+}
+
+static void SvcWrap_StoreProcessDataCache64(Core::System& system) {
+ Result ret{};
+
+ Handle process_handle{};
+ uint64_t address{};
+ uint64_t size{};
+
+ process_handle = Convert<Handle>(GetReg64(system, 0));
+ address = Convert<uint64_t>(GetReg64(system, 1));
+ size = Convert<uint64_t>(GetReg64(system, 2));
+
+ ret = StoreProcessDataCache64(system, process_handle, address, size);
+
+ SetReg64(system, 0, Convert<uint64_t>(ret));
+}
+
+static void SvcWrap_FlushProcessDataCache64(Core::System& system) {
+ Result ret{};
+
+ Handle process_handle{};
+ uint64_t address{};
+ uint64_t size{};
+
+ process_handle = Convert<Handle>(GetReg64(system, 0));
+ address = Convert<uint64_t>(GetReg64(system, 1));
+ size = Convert<uint64_t>(GetReg64(system, 2));
+
+ ret = FlushProcessDataCache64(system, process_handle, address, size);
+
+ SetReg64(system, 0, Convert<uint64_t>(ret));
+}
+
+static void SvcWrap_DebugActiveProcess64(Core::System& system) {
+ Result ret{};
+
+ Handle out_handle{};
+ uint64_t process_id{};
+
+ process_id = Convert<uint64_t>(GetReg64(system, 1));
+
+ ret = DebugActiveProcess64(system, &out_handle, process_id);
+
+ SetReg64(system, 0, Convert<uint64_t>(ret));
+ SetReg64(system, 1, Convert<uint64_t>(out_handle));
+}
+
+static void SvcWrap_BreakDebugProcess64(Core::System& system) {
+ Result ret{};
+
+ Handle debug_handle{};
+
+ debug_handle = Convert<Handle>(GetReg64(system, 0));
+
+ ret = BreakDebugProcess64(system, debug_handle);
+
+ SetReg64(system, 0, Convert<uint64_t>(ret));
+}
+
+static void SvcWrap_TerminateDebugProcess64(Core::System& system) {
+ Result ret{};
+
+ Handle debug_handle{};
+
+ debug_handle = Convert<Handle>(GetReg64(system, 0));
+
+ ret = TerminateDebugProcess64(system, debug_handle);
+
+ SetReg64(system, 0, Convert<uint64_t>(ret));
+}
+
+static void SvcWrap_GetDebugEvent64(Core::System& system) {
+ Result ret{};
+
+ uint64_t out_info{};
+ Handle debug_handle{};
+
+ out_info = Convert<uint64_t>(GetReg64(system, 0));
+ debug_handle = Convert<Handle>(GetReg64(system, 1));
+
+ ret = GetDebugEvent64(system, out_info, debug_handle);
+
+ SetReg64(system, 0, Convert<uint64_t>(ret));
+}
+
+static void SvcWrap_ContinueDebugEvent64(Core::System& system) {
+ Result ret{};
+
+ Handle debug_handle{};
+ uint32_t flags{};
+ uint64_t thread_ids{};
+ int32_t num_thread_ids{};
+
+ debug_handle = Convert<Handle>(GetReg64(system, 0));
+ flags = Convert<uint32_t>(GetReg64(system, 1));
+ thread_ids = Convert<uint64_t>(GetReg64(system, 2));
+ num_thread_ids = Convert<int32_t>(GetReg64(system, 3));
+
+ ret = ContinueDebugEvent64(system, debug_handle, flags, thread_ids, num_thread_ids);
+
+ SetReg64(system, 0, Convert<uint64_t>(ret));
+}
+
+static void SvcWrap_GetProcessList64(Core::System& system) {
+ Result ret{};
+
+ int32_t out_num_processes{};
+ uint64_t out_process_ids{};
+ int32_t max_out_count{};
+
+ out_process_ids = Convert<uint64_t>(GetReg64(system, 1));
+ max_out_count = Convert<int32_t>(GetReg64(system, 2));
+
+ ret = GetProcessList64(system, &out_num_processes, out_process_ids, max_out_count);
+
+ SetReg64(system, 0, Convert<uint64_t>(ret));
+ SetReg64(system, 1, Convert<uint64_t>(out_num_processes));
+}
+
+static void SvcWrap_GetThreadList64(Core::System& system) {
+ Result ret{};
+
+ int32_t out_num_threads{};
+ uint64_t out_thread_ids{};
+ int32_t max_out_count{};
+ Handle debug_handle{};
+
+ out_thread_ids = Convert<uint64_t>(GetReg64(system, 1));
+ max_out_count = Convert<int32_t>(GetReg64(system, 2));
+ debug_handle = Convert<Handle>(GetReg64(system, 3));
+
+ ret = GetThreadList64(system, &out_num_threads, out_thread_ids, max_out_count, debug_handle);
+
+ SetReg64(system, 0, Convert<uint64_t>(ret));
+ SetReg64(system, 1, Convert<uint64_t>(out_num_threads));
+}
+
+static void SvcWrap_GetDebugThreadContext64(Core::System& system) {
+ Result ret{};
+
+ uint64_t out_context{};
+ Handle debug_handle{};
+ uint64_t thread_id{};
+ uint32_t context_flags{};
+
+ out_context = Convert<uint64_t>(GetReg64(system, 0));
+ debug_handle = Convert<Handle>(GetReg64(system, 1));
+ thread_id = Convert<uint64_t>(GetReg64(system, 2));
+ context_flags = Convert<uint32_t>(GetReg64(system, 3));
+
+ ret = GetDebugThreadContext64(system, out_context, debug_handle, thread_id, context_flags);
+
+ SetReg64(system, 0, Convert<uint64_t>(ret));
+}
+
+static void SvcWrap_SetDebugThreadContext64(Core::System& system) {
+ Result ret{};
+
+ Handle debug_handle{};
+ uint64_t thread_id{};
+ uint64_t context{};
+ uint32_t context_flags{};
+
+ debug_handle = Convert<Handle>(GetReg64(system, 0));
+ thread_id = Convert<uint64_t>(GetReg64(system, 1));
+ context = Convert<uint64_t>(GetReg64(system, 2));
+ context_flags = Convert<uint32_t>(GetReg64(system, 3));
+
+ ret = SetDebugThreadContext64(system, debug_handle, thread_id, context, context_flags);
+
+ SetReg64(system, 0, Convert<uint64_t>(ret));
+}
+
+static void SvcWrap_QueryDebugProcessMemory64(Core::System& system) {
+ Result ret{};
+
+ PageInfo out_page_info{};
+ uint64_t out_memory_info{};
+ Handle process_handle{};
+ uint64_t address{};
+
+ out_memory_info = Convert<uint64_t>(GetReg64(system, 0));
+ process_handle = Convert<Handle>(GetReg64(system, 2));
+ address = Convert<uint64_t>(GetReg64(system, 3));
+
+ ret = QueryDebugProcessMemory64(system, out_memory_info, &out_page_info, process_handle, address);
+
+ SetReg64(system, 0, Convert<uint64_t>(ret));
+ SetReg64(system, 1, Convert<uint64_t>(out_page_info));
+}
+
+static void SvcWrap_ReadDebugProcessMemory64(Core::System& system) {
+ Result ret{};
+
+ uint64_t buffer{};
+ Handle debug_handle{};
+ uint64_t address{};
+ uint64_t size{};
+
+ buffer = Convert<uint64_t>(GetReg64(system, 0));
+ debug_handle = Convert<Handle>(GetReg64(system, 1));
+ address = Convert<uint64_t>(GetReg64(system, 2));
+ size = Convert<uint64_t>(GetReg64(system, 3));
+
+ ret = ReadDebugProcessMemory64(system, buffer, debug_handle, address, size);
+
+ SetReg64(system, 0, Convert<uint64_t>(ret));
+}
+
+static void SvcWrap_WriteDebugProcessMemory64(Core::System& system) {
+ Result ret{};
+
+ Handle debug_handle{};
+ uint64_t buffer{};
+ uint64_t address{};
+ uint64_t size{};
+
+ debug_handle = Convert<Handle>(GetReg64(system, 0));
+ buffer = Convert<uint64_t>(GetReg64(system, 1));
+ address = Convert<uint64_t>(GetReg64(system, 2));
+ size = Convert<uint64_t>(GetReg64(system, 3));
+
+ ret = WriteDebugProcessMemory64(system, debug_handle, buffer, address, size);
+
+ SetReg64(system, 0, Convert<uint64_t>(ret));
+}
+
+static void SvcWrap_SetHardwareBreakPoint64(Core::System& system) {
+ Result ret{};
+
+ HardwareBreakPointRegisterName name{};
+ uint64_t flags{};
+ uint64_t value{};
+
+ name = Convert<HardwareBreakPointRegisterName>(GetReg64(system, 0));
+ flags = Convert<uint64_t>(GetReg64(system, 1));
+ value = Convert<uint64_t>(GetReg64(system, 2));
+
+ ret = SetHardwareBreakPoint64(system, name, flags, value);
+
+ SetReg64(system, 0, Convert<uint64_t>(ret));
+}
+
+static void SvcWrap_GetDebugThreadParam64(Core::System& system) {
+ Result ret{};
+
+ uint64_t out_64{};
+ uint32_t out_32{};
+ Handle debug_handle{};
+ uint64_t thread_id{};
+ DebugThreadParam param{};
+
+ debug_handle = Convert<Handle>(GetReg64(system, 2));
+ thread_id = Convert<uint64_t>(GetReg64(system, 3));
+ param = Convert<DebugThreadParam>(GetReg64(system, 4));
+
+ ret = GetDebugThreadParam64(system, &out_64, &out_32, debug_handle, thread_id, param);
+
+ SetReg64(system, 0, Convert<uint64_t>(ret));
+ SetReg64(system, 1, Convert<uint64_t>(out_64));
+ SetReg64(system, 2, Convert<uint64_t>(out_32));
+}
+
+static void SvcWrap_GetSystemInfo64(Core::System& system) {
+ Result ret{};
+
+ uint64_t out{};
+ SystemInfoType info_type{};
+ Handle handle{};
+ uint64_t info_subtype{};
+
+ info_type = Convert<SystemInfoType>(GetReg64(system, 1));
+ handle = Convert<Handle>(GetReg64(system, 2));
+ info_subtype = Convert<uint64_t>(GetReg64(system, 3));
+
+ ret = GetSystemInfo64(system, &out, info_type, handle, info_subtype);
+
+ SetReg64(system, 0, Convert<uint64_t>(ret));
+ SetReg64(system, 1, Convert<uint64_t>(out));
+}
+
+static void SvcWrap_CreatePort64(Core::System& system) {
+ Result ret{};
+
+ Handle out_server_handle{};
+ Handle out_client_handle{};
+ int32_t max_sessions{};
+ bool is_light{};
+ uint64_t name{};
+
+ max_sessions = Convert<int32_t>(GetReg64(system, 2));
+ is_light = Convert<bool>(GetReg64(system, 3));
+ name = Convert<uint64_t>(GetReg64(system, 4));
+
+ ret = CreatePort64(system, &out_server_handle, &out_client_handle, max_sessions, is_light, name);
+
+ SetReg64(system, 0, Convert<uint64_t>(ret));
+ SetReg64(system, 1, Convert<uint64_t>(out_server_handle));
+ SetReg64(system, 2, Convert<uint64_t>(out_client_handle));
+}
+
+static void SvcWrap_ManageNamedPort64(Core::System& system) {
+ Result ret{};
+
+ Handle out_server_handle{};
+ uint64_t name{};
+ int32_t max_sessions{};
+
+ name = Convert<uint64_t>(GetReg64(system, 1));
+ max_sessions = Convert<int32_t>(GetReg64(system, 2));
+
+ ret = ManageNamedPort64(system, &out_server_handle, name, max_sessions);
+
+ SetReg64(system, 0, Convert<uint64_t>(ret));
+ SetReg64(system, 1, Convert<uint64_t>(out_server_handle));
+}
+
+static void SvcWrap_ConnectToPort64(Core::System& system) {
+ Result ret{};
+
+ Handle out_handle{};
+ Handle port{};
+
+ port = Convert<Handle>(GetReg64(system, 1));
+
+ ret = ConnectToPort64(system, &out_handle, port);
+
+ SetReg64(system, 0, Convert<uint64_t>(ret));
+ SetReg64(system, 1, Convert<uint64_t>(out_handle));
+}
+
+static void SvcWrap_SetProcessMemoryPermission64(Core::System& system) {
+ Result ret{};
+
+ Handle process_handle{};
+ uint64_t address{};
+ uint64_t size{};
+ MemoryPermission perm{};
+
+ process_handle = Convert<Handle>(GetReg64(system, 0));
+ address = Convert<uint64_t>(GetReg64(system, 1));
+ size = Convert<uint64_t>(GetReg64(system, 2));
+ perm = Convert<MemoryPermission>(GetReg64(system, 3));
+
+ ret = SetProcessMemoryPermission64(system, process_handle, address, size, perm);
+
+ SetReg64(system, 0, Convert<uint64_t>(ret));
+}
+
+static void SvcWrap_MapProcessMemory64(Core::System& system) {
+ Result ret{};
+
+ uint64_t dst_address{};
+ Handle process_handle{};
+ uint64_t src_address{};
+ uint64_t size{};
+
+ dst_address = Convert<uint64_t>(GetReg64(system, 0));
+ process_handle = Convert<Handle>(GetReg64(system, 1));
+ src_address = Convert<uint64_t>(GetReg64(system, 2));
+ size = Convert<uint64_t>(GetReg64(system, 3));
+
+ ret = MapProcessMemory64(system, dst_address, process_handle, src_address, size);
+
+ SetReg64(system, 0, Convert<uint64_t>(ret));
+}
+
+static void SvcWrap_UnmapProcessMemory64(Core::System& system) {
+ Result ret{};
+
+ uint64_t dst_address{};
+ Handle process_handle{};
+ uint64_t src_address{};
+ uint64_t size{};
+
+ dst_address = Convert<uint64_t>(GetReg64(system, 0));
+ process_handle = Convert<Handle>(GetReg64(system, 1));
+ src_address = Convert<uint64_t>(GetReg64(system, 2));
+ size = Convert<uint64_t>(GetReg64(system, 3));
+
+ ret = UnmapProcessMemory64(system, dst_address, process_handle, src_address, size);
+
+ SetReg64(system, 0, Convert<uint64_t>(ret));
+}
+
+static void SvcWrap_QueryProcessMemory64(Core::System& system) {
+ Result ret{};
+
+ PageInfo out_page_info{};
+ uint64_t out_memory_info{};
+ Handle process_handle{};
+ uint64_t address{};
+
+ out_memory_info = Convert<uint64_t>(GetReg64(system, 0));
+ process_handle = Convert<Handle>(GetReg64(system, 2));
+ address = Convert<uint64_t>(GetReg64(system, 3));
+
+ ret = QueryProcessMemory64(system, out_memory_info, &out_page_info, process_handle, address);
+
+ SetReg64(system, 0, Convert<uint64_t>(ret));
+ SetReg64(system, 1, Convert<uint64_t>(out_page_info));
+}
+
+static void SvcWrap_MapProcessCodeMemory64(Core::System& system) {
+ Result ret{};
+
+ Handle process_handle{};
+ uint64_t dst_address{};
+ uint64_t src_address{};
+ uint64_t size{};
+
+ process_handle = Convert<Handle>(GetReg64(system, 0));
+ dst_address = Convert<uint64_t>(GetReg64(system, 1));
+ src_address = Convert<uint64_t>(GetReg64(system, 2));
+ size = Convert<uint64_t>(GetReg64(system, 3));
+
+ ret = MapProcessCodeMemory64(system, process_handle, dst_address, src_address, size);
+
+ SetReg64(system, 0, Convert<uint64_t>(ret));
+}
+
+static void SvcWrap_UnmapProcessCodeMemory64(Core::System& system) {
+ Result ret{};
+
+ Handle process_handle{};
+ uint64_t dst_address{};
+ uint64_t src_address{};
+ uint64_t size{};
+
+ process_handle = Convert<Handle>(GetReg64(system, 0));
+ dst_address = Convert<uint64_t>(GetReg64(system, 1));
+ src_address = Convert<uint64_t>(GetReg64(system, 2));
+ size = Convert<uint64_t>(GetReg64(system, 3));
+
+ ret = UnmapProcessCodeMemory64(system, process_handle, dst_address, src_address, size);
+
+ SetReg64(system, 0, Convert<uint64_t>(ret));
+}
+
+static void SvcWrap_CreateProcess64(Core::System& system) {
+ Result ret{};
+
+ Handle out_handle{};
+ uint64_t parameters{};
+ uint64_t caps{};
+ int32_t num_caps{};
+
+ parameters = Convert<uint64_t>(GetReg64(system, 1));
+ caps = Convert<uint64_t>(GetReg64(system, 2));
+ num_caps = Convert<int32_t>(GetReg64(system, 3));
+
+ ret = CreateProcess64(system, &out_handle, parameters, caps, num_caps);
+
+ SetReg64(system, 0, Convert<uint64_t>(ret));
+ SetReg64(system, 1, Convert<uint64_t>(out_handle));
+}
+
+static void SvcWrap_StartProcess64(Core::System& system) {
+ Result ret{};
+
+ Handle process_handle{};
+ int32_t priority{};
+ int32_t core_id{};
+ uint64_t main_thread_stack_size{};
+
+ process_handle = Convert<Handle>(GetReg64(system, 0));
+ priority = Convert<int32_t>(GetReg64(system, 1));
+ core_id = Convert<int32_t>(GetReg64(system, 2));
+ main_thread_stack_size = Convert<uint64_t>(GetReg64(system, 3));
+
+ ret = StartProcess64(system, process_handle, priority, core_id, main_thread_stack_size);
+
+ SetReg64(system, 0, Convert<uint64_t>(ret));
+}
+
+static void SvcWrap_TerminateProcess64(Core::System& system) {
+ Result ret{};
+
+ Handle process_handle{};
+
+ process_handle = Convert<Handle>(GetReg64(system, 0));
+
+ ret = TerminateProcess64(system, process_handle);
+
+ SetReg64(system, 0, Convert<uint64_t>(ret));
+}
+
+static void SvcWrap_GetProcessInfo64(Core::System& system) {
+ Result ret{};
+
+ int64_t out_info{};
+ Handle process_handle{};
+ ProcessInfoType info_type{};
+
+ process_handle = Convert<Handle>(GetReg64(system, 1));
+ info_type = Convert<ProcessInfoType>(GetReg64(system, 2));
+
+ ret = GetProcessInfo64(system, &out_info, process_handle, info_type);
+
+ SetReg64(system, 0, Convert<uint64_t>(ret));
+ SetReg64(system, 1, Convert<uint64_t>(out_info));
+}
+
+static void SvcWrap_CreateResourceLimit64(Core::System& system) {
+ Result ret{};
+
+ Handle out_handle{};
+
+ ret = CreateResourceLimit64(system, &out_handle);
+
+ SetReg64(system, 0, Convert<uint64_t>(ret));
+ SetReg64(system, 1, Convert<uint64_t>(out_handle));
+}
+
+static void SvcWrap_SetResourceLimitLimitValue64(Core::System& system) {
+ Result ret{};
+
+ Handle resource_limit_handle{};
+ LimitableResource which{};
+ int64_t limit_value{};
+
+ resource_limit_handle = Convert<Handle>(GetReg64(system, 0));
+ which = Convert<LimitableResource>(GetReg64(system, 1));
+ limit_value = Convert<int64_t>(GetReg64(system, 2));
+
+ ret = SetResourceLimitLimitValue64(system, resource_limit_handle, which, limit_value);
+
+ SetReg64(system, 0, Convert<uint64_t>(ret));
+}
+
+static void SvcWrap_MapInsecureMemory64(Core::System& system) {
+ Result ret{};
+
+ uint64_t address{};
+ uint64_t size{};
+
+ address = Convert<uint64_t>(GetReg64(system, 0));
+ size = Convert<uint64_t>(GetReg64(system, 1));
+
+ ret = MapInsecureMemory64(system, address, size);
+
+ SetReg64(system, 0, Convert<uint64_t>(ret));
+}
+
+static void SvcWrap_UnmapInsecureMemory64(Core::System& system) {
+ Result ret{};
+
+ uint64_t address{};
+ uint64_t size{};
+
+ address = Convert<uint64_t>(GetReg64(system, 0));
+ size = Convert<uint64_t>(GetReg64(system, 1));
+
+ ret = UnmapInsecureMemory64(system, address, size);
+
+ SetReg64(system, 0, Convert<uint64_t>(ret));
+}
+
+static void Call32(Core::System& system, u32 imm) {
+ switch (static_cast<SvcId>(imm)) {
+ case SvcId::SetHeapSize:
+ return SvcWrap_SetHeapSize64From32(system);
+ case SvcId::SetMemoryPermission:
+ return SvcWrap_SetMemoryPermission64From32(system);
+ case SvcId::SetMemoryAttribute:
+ return SvcWrap_SetMemoryAttribute64From32(system);
+ case SvcId::MapMemory:
+ return SvcWrap_MapMemory64From32(system);
+ case SvcId::UnmapMemory:
+ return SvcWrap_UnmapMemory64From32(system);
+ case SvcId::QueryMemory:
+ return SvcWrap_QueryMemory64From32(system);
+ case SvcId::ExitProcess:
+ return SvcWrap_ExitProcess64From32(system);
+ case SvcId::CreateThread:
+ return SvcWrap_CreateThread64From32(system);
+ case SvcId::StartThread:
+ return SvcWrap_StartThread64From32(system);
+ case SvcId::ExitThread:
+ return SvcWrap_ExitThread64From32(system);
+ case SvcId::SleepThread:
+ return SvcWrap_SleepThread64From32(system);
+ case SvcId::GetThreadPriority:
+ return SvcWrap_GetThreadPriority64From32(system);
+ case SvcId::SetThreadPriority:
+ return SvcWrap_SetThreadPriority64From32(system);
+ case SvcId::GetThreadCoreMask:
+ return SvcWrap_GetThreadCoreMask64From32(system);
+ case SvcId::SetThreadCoreMask:
+ return SvcWrap_SetThreadCoreMask64From32(system);
+ case SvcId::GetCurrentProcessorNumber:
+ return SvcWrap_GetCurrentProcessorNumber64From32(system);
+ case SvcId::SignalEvent:
+ return SvcWrap_SignalEvent64From32(system);
+ case SvcId::ClearEvent:
+ return SvcWrap_ClearEvent64From32(system);
+ case SvcId::MapSharedMemory:
+ return SvcWrap_MapSharedMemory64From32(system);
+ case SvcId::UnmapSharedMemory:
+ return SvcWrap_UnmapSharedMemory64From32(system);
+ case SvcId::CreateTransferMemory:
+ return SvcWrap_CreateTransferMemory64From32(system);
+ case SvcId::CloseHandle:
+ return SvcWrap_CloseHandle64From32(system);
+ case SvcId::ResetSignal:
+ return SvcWrap_ResetSignal64From32(system);
+ case SvcId::WaitSynchronization:
+ return SvcWrap_WaitSynchronization64From32(system);
+ case SvcId::CancelSynchronization:
+ return SvcWrap_CancelSynchronization64From32(system);
+ case SvcId::ArbitrateLock:
+ return SvcWrap_ArbitrateLock64From32(system);
+ case SvcId::ArbitrateUnlock:
+ return SvcWrap_ArbitrateUnlock64From32(system);
+ case SvcId::WaitProcessWideKeyAtomic:
+ return SvcWrap_WaitProcessWideKeyAtomic64From32(system);
+ case SvcId::SignalProcessWideKey:
+ return SvcWrap_SignalProcessWideKey64From32(system);
+ case SvcId::GetSystemTick:
+ return SvcWrap_GetSystemTick64From32(system);
+ case SvcId::ConnectToNamedPort:
+ return SvcWrap_ConnectToNamedPort64From32(system);
+ case SvcId::SendSyncRequestLight:
+ return SvcWrap_SendSyncRequestLight64From32(system);
+ case SvcId::SendSyncRequest:
+ return SvcWrap_SendSyncRequest64From32(system);
+ case SvcId::SendSyncRequestWithUserBuffer:
+ return SvcWrap_SendSyncRequestWithUserBuffer64From32(system);
+ case SvcId::SendAsyncRequestWithUserBuffer:
+ return SvcWrap_SendAsyncRequestWithUserBuffer64From32(system);
+ case SvcId::GetProcessId:
+ return SvcWrap_GetProcessId64From32(system);
+ case SvcId::GetThreadId:
+ return SvcWrap_GetThreadId64From32(system);
+ case SvcId::Break:
+ return SvcWrap_Break64From32(system);
+ case SvcId::OutputDebugString:
+ return SvcWrap_OutputDebugString64From32(system);
+ case SvcId::ReturnFromException:
+ return SvcWrap_ReturnFromException64From32(system);
+ case SvcId::GetInfo:
+ return SvcWrap_GetInfo64From32(system);
+ case SvcId::FlushEntireDataCache:
+ return SvcWrap_FlushEntireDataCache64From32(system);
+ case SvcId::FlushDataCache:
+ return SvcWrap_FlushDataCache64From32(system);
+ case SvcId::MapPhysicalMemory:
+ return SvcWrap_MapPhysicalMemory64From32(system);
+ case SvcId::UnmapPhysicalMemory:
+ return SvcWrap_UnmapPhysicalMemory64From32(system);
+ case SvcId::GetDebugFutureThreadInfo:
+ return SvcWrap_GetDebugFutureThreadInfo64From32(system);
+ case SvcId::GetLastThreadInfo:
+ return SvcWrap_GetLastThreadInfo64From32(system);
+ case SvcId::GetResourceLimitLimitValue:
+ return SvcWrap_GetResourceLimitLimitValue64From32(system);
+ case SvcId::GetResourceLimitCurrentValue:
+ return SvcWrap_GetResourceLimitCurrentValue64From32(system);
+ case SvcId::SetThreadActivity:
+ return SvcWrap_SetThreadActivity64From32(system);
+ case SvcId::GetThreadContext3:
+ return SvcWrap_GetThreadContext364From32(system);
+ case SvcId::WaitForAddress:
+ return SvcWrap_WaitForAddress64From32(system);
+ case SvcId::SignalToAddress:
+ return SvcWrap_SignalToAddress64From32(system);
+ case SvcId::SynchronizePreemptionState:
+ return SvcWrap_SynchronizePreemptionState64From32(system);
+ case SvcId::GetResourceLimitPeakValue:
+ return SvcWrap_GetResourceLimitPeakValue64From32(system);
+ case SvcId::CreateIoPool:
+ return SvcWrap_CreateIoPool64From32(system);
+ case SvcId::CreateIoRegion:
+ return SvcWrap_CreateIoRegion64From32(system);
+ case SvcId::KernelDebug:
+ return SvcWrap_KernelDebug64From32(system);
+ case SvcId::ChangeKernelTraceState:
+ return SvcWrap_ChangeKernelTraceState64From32(system);
+ case SvcId::CreateSession:
+ return SvcWrap_CreateSession64From32(system);
+ case SvcId::AcceptSession:
+ return SvcWrap_AcceptSession64From32(system);
+ case SvcId::ReplyAndReceiveLight:
+ return SvcWrap_ReplyAndReceiveLight64From32(system);
+ case SvcId::ReplyAndReceive:
+ return SvcWrap_ReplyAndReceive64From32(system);
+ case SvcId::ReplyAndReceiveWithUserBuffer:
+ return SvcWrap_ReplyAndReceiveWithUserBuffer64From32(system);
+ case SvcId::CreateEvent:
+ return SvcWrap_CreateEvent64From32(system);
+ case SvcId::MapIoRegion:
+ return SvcWrap_MapIoRegion64From32(system);
+ case SvcId::UnmapIoRegion:
+ return SvcWrap_UnmapIoRegion64From32(system);
+ case SvcId::MapPhysicalMemoryUnsafe:
+ return SvcWrap_MapPhysicalMemoryUnsafe64From32(system);
+ case SvcId::UnmapPhysicalMemoryUnsafe:
+ return SvcWrap_UnmapPhysicalMemoryUnsafe64From32(system);
+ case SvcId::SetUnsafeLimit:
+ return SvcWrap_SetUnsafeLimit64From32(system);
+ case SvcId::CreateCodeMemory:
+ return SvcWrap_CreateCodeMemory64From32(system);
+ case SvcId::ControlCodeMemory:
+ return SvcWrap_ControlCodeMemory64From32(system);
+ case SvcId::SleepSystem:
+ return SvcWrap_SleepSystem64From32(system);
+ case SvcId::ReadWriteRegister:
+ return SvcWrap_ReadWriteRegister64From32(system);
+ case SvcId::SetProcessActivity:
+ return SvcWrap_SetProcessActivity64From32(system);
+ case SvcId::CreateSharedMemory:
+ return SvcWrap_CreateSharedMemory64From32(system);
+ case SvcId::MapTransferMemory:
+ return SvcWrap_MapTransferMemory64From32(system);
+ case SvcId::UnmapTransferMemory:
+ return SvcWrap_UnmapTransferMemory64From32(system);
+ case SvcId::CreateInterruptEvent:
+ return SvcWrap_CreateInterruptEvent64From32(system);
+ case SvcId::QueryPhysicalAddress:
+ return SvcWrap_QueryPhysicalAddress64From32(system);
+ case SvcId::QueryIoMapping:
+ return SvcWrap_QueryIoMapping64From32(system);
+ case SvcId::CreateDeviceAddressSpace:
+ return SvcWrap_CreateDeviceAddressSpace64From32(system);
+ case SvcId::AttachDeviceAddressSpace:
+ return SvcWrap_AttachDeviceAddressSpace64From32(system);
+ case SvcId::DetachDeviceAddressSpace:
+ return SvcWrap_DetachDeviceAddressSpace64From32(system);
+ case SvcId::MapDeviceAddressSpaceByForce:
+ return SvcWrap_MapDeviceAddressSpaceByForce64From32(system);
+ case SvcId::MapDeviceAddressSpaceAligned:
+ return SvcWrap_MapDeviceAddressSpaceAligned64From32(system);
+ case SvcId::UnmapDeviceAddressSpace:
+ return SvcWrap_UnmapDeviceAddressSpace64From32(system);
+ case SvcId::InvalidateProcessDataCache:
+ return SvcWrap_InvalidateProcessDataCache64From32(system);
+ case SvcId::StoreProcessDataCache:
+ return SvcWrap_StoreProcessDataCache64From32(system);
+ case SvcId::FlushProcessDataCache:
+ return SvcWrap_FlushProcessDataCache64From32(system);
+ case SvcId::DebugActiveProcess:
+ return SvcWrap_DebugActiveProcess64From32(system);
+ case SvcId::BreakDebugProcess:
+ return SvcWrap_BreakDebugProcess64From32(system);
+ case SvcId::TerminateDebugProcess:
+ return SvcWrap_TerminateDebugProcess64From32(system);
+ case SvcId::GetDebugEvent:
+ return SvcWrap_GetDebugEvent64From32(system);
+ case SvcId::ContinueDebugEvent:
+ return SvcWrap_ContinueDebugEvent64From32(system);
+ case SvcId::GetProcessList:
+ return SvcWrap_GetProcessList64From32(system);
+ case SvcId::GetThreadList:
+ return SvcWrap_GetThreadList64From32(system);
+ case SvcId::GetDebugThreadContext:
+ return SvcWrap_GetDebugThreadContext64From32(system);
+ case SvcId::SetDebugThreadContext:
+ return SvcWrap_SetDebugThreadContext64From32(system);
+ case SvcId::QueryDebugProcessMemory:
+ return SvcWrap_QueryDebugProcessMemory64From32(system);
+ case SvcId::ReadDebugProcessMemory:
+ return SvcWrap_ReadDebugProcessMemory64From32(system);
+ case SvcId::WriteDebugProcessMemory:
+ return SvcWrap_WriteDebugProcessMemory64From32(system);
+ case SvcId::SetHardwareBreakPoint:
+ return SvcWrap_SetHardwareBreakPoint64From32(system);
+ case SvcId::GetDebugThreadParam:
+ return SvcWrap_GetDebugThreadParam64From32(system);
+ case SvcId::GetSystemInfo:
+ return SvcWrap_GetSystemInfo64From32(system);
+ case SvcId::CreatePort:
+ return SvcWrap_CreatePort64From32(system);
+ case SvcId::ManageNamedPort:
+ return SvcWrap_ManageNamedPort64From32(system);
+ case SvcId::ConnectToPort:
+ return SvcWrap_ConnectToPort64From32(system);
+ case SvcId::SetProcessMemoryPermission:
+ return SvcWrap_SetProcessMemoryPermission64From32(system);
+ case SvcId::MapProcessMemory:
+ return SvcWrap_MapProcessMemory64From32(system);
+ case SvcId::UnmapProcessMemory:
+ return SvcWrap_UnmapProcessMemory64From32(system);
+ case SvcId::QueryProcessMemory:
+ return SvcWrap_QueryProcessMemory64From32(system);
+ case SvcId::MapProcessCodeMemory:
+ return SvcWrap_MapProcessCodeMemory64From32(system);
+ case SvcId::UnmapProcessCodeMemory:
+ return SvcWrap_UnmapProcessCodeMemory64From32(system);
+ case SvcId::CreateProcess:
+ return SvcWrap_CreateProcess64From32(system);
+ case SvcId::StartProcess:
+ return SvcWrap_StartProcess64From32(system);
+ case SvcId::TerminateProcess:
+ return SvcWrap_TerminateProcess64From32(system);
+ case SvcId::GetProcessInfo:
+ return SvcWrap_GetProcessInfo64From32(system);
+ case SvcId::CreateResourceLimit:
+ return SvcWrap_CreateResourceLimit64From32(system);
+ case SvcId::SetResourceLimitLimitValue:
+ return SvcWrap_SetResourceLimitLimitValue64From32(system);
+ case SvcId::CallSecureMonitor:
+ return SvcWrap_CallSecureMonitor64From32(system);
+ case SvcId::MapInsecureMemory:
+ return SvcWrap_MapInsecureMemory64From32(system);
+ case SvcId::UnmapInsecureMemory:
+ return SvcWrap_UnmapInsecureMemory64From32(system);
+ default:
+ LOG_CRITICAL(Kernel_SVC, "Unknown SVC {:x}!", imm);
+ break;
}
- return &SVC_Table_32[func_num];
}
-static const FunctionDef* GetSVCInfo64(u32 func_num) {
- if (func_num >= std::size(SVC_Table_64)) {
- LOG_ERROR(Kernel_SVC, "Unknown svc=0x{:02X}", func_num);
- return nullptr;
+static void Call64(Core::System& system, u32 imm) {
+ switch (static_cast<SvcId>(imm)) {
+ case SvcId::SetHeapSize:
+ return SvcWrap_SetHeapSize64(system);
+ case SvcId::SetMemoryPermission:
+ return SvcWrap_SetMemoryPermission64(system);
+ case SvcId::SetMemoryAttribute:
+ return SvcWrap_SetMemoryAttribute64(system);
+ case SvcId::MapMemory:
+ return SvcWrap_MapMemory64(system);
+ case SvcId::UnmapMemory:
+ return SvcWrap_UnmapMemory64(system);
+ case SvcId::QueryMemory:
+ return SvcWrap_QueryMemory64(system);
+ case SvcId::ExitProcess:
+ return SvcWrap_ExitProcess64(system);
+ case SvcId::CreateThread:
+ return SvcWrap_CreateThread64(system);
+ case SvcId::StartThread:
+ return SvcWrap_StartThread64(system);
+ case SvcId::ExitThread:
+ return SvcWrap_ExitThread64(system);
+ case SvcId::SleepThread:
+ return SvcWrap_SleepThread64(system);
+ case SvcId::GetThreadPriority:
+ return SvcWrap_GetThreadPriority64(system);
+ case SvcId::SetThreadPriority:
+ return SvcWrap_SetThreadPriority64(system);
+ case SvcId::GetThreadCoreMask:
+ return SvcWrap_GetThreadCoreMask64(system);
+ case SvcId::SetThreadCoreMask:
+ return SvcWrap_SetThreadCoreMask64(system);
+ case SvcId::GetCurrentProcessorNumber:
+ return SvcWrap_GetCurrentProcessorNumber64(system);
+ case SvcId::SignalEvent:
+ return SvcWrap_SignalEvent64(system);
+ case SvcId::ClearEvent:
+ return SvcWrap_ClearEvent64(system);
+ case SvcId::MapSharedMemory:
+ return SvcWrap_MapSharedMemory64(system);
+ case SvcId::UnmapSharedMemory:
+ return SvcWrap_UnmapSharedMemory64(system);
+ case SvcId::CreateTransferMemory:
+ return SvcWrap_CreateTransferMemory64(system);
+ case SvcId::CloseHandle:
+ return SvcWrap_CloseHandle64(system);
+ case SvcId::ResetSignal:
+ return SvcWrap_ResetSignal64(system);
+ case SvcId::WaitSynchronization:
+ return SvcWrap_WaitSynchronization64(system);
+ case SvcId::CancelSynchronization:
+ return SvcWrap_CancelSynchronization64(system);
+ case SvcId::ArbitrateLock:
+ return SvcWrap_ArbitrateLock64(system);
+ case SvcId::ArbitrateUnlock:
+ return SvcWrap_ArbitrateUnlock64(system);
+ case SvcId::WaitProcessWideKeyAtomic:
+ return SvcWrap_WaitProcessWideKeyAtomic64(system);
+ case SvcId::SignalProcessWideKey:
+ return SvcWrap_SignalProcessWideKey64(system);
+ case SvcId::GetSystemTick:
+ return SvcWrap_GetSystemTick64(system);
+ case SvcId::ConnectToNamedPort:
+ return SvcWrap_ConnectToNamedPort64(system);
+ case SvcId::SendSyncRequestLight:
+ return SvcWrap_SendSyncRequestLight64(system);
+ case SvcId::SendSyncRequest:
+ return SvcWrap_SendSyncRequest64(system);
+ case SvcId::SendSyncRequestWithUserBuffer:
+ return SvcWrap_SendSyncRequestWithUserBuffer64(system);
+ case SvcId::SendAsyncRequestWithUserBuffer:
+ return SvcWrap_SendAsyncRequestWithUserBuffer64(system);
+ case SvcId::GetProcessId:
+ return SvcWrap_GetProcessId64(system);
+ case SvcId::GetThreadId:
+ return SvcWrap_GetThreadId64(system);
+ case SvcId::Break:
+ return SvcWrap_Break64(system);
+ case SvcId::OutputDebugString:
+ return SvcWrap_OutputDebugString64(system);
+ case SvcId::ReturnFromException:
+ return SvcWrap_ReturnFromException64(system);
+ case SvcId::GetInfo:
+ return SvcWrap_GetInfo64(system);
+ case SvcId::FlushEntireDataCache:
+ return SvcWrap_FlushEntireDataCache64(system);
+ case SvcId::FlushDataCache:
+ return SvcWrap_FlushDataCache64(system);
+ case SvcId::MapPhysicalMemory:
+ return SvcWrap_MapPhysicalMemory64(system);
+ case SvcId::UnmapPhysicalMemory:
+ return SvcWrap_UnmapPhysicalMemory64(system);
+ case SvcId::GetDebugFutureThreadInfo:
+ return SvcWrap_GetDebugFutureThreadInfo64(system);
+ case SvcId::GetLastThreadInfo:
+ return SvcWrap_GetLastThreadInfo64(system);
+ case SvcId::GetResourceLimitLimitValue:
+ return SvcWrap_GetResourceLimitLimitValue64(system);
+ case SvcId::GetResourceLimitCurrentValue:
+ return SvcWrap_GetResourceLimitCurrentValue64(system);
+ case SvcId::SetThreadActivity:
+ return SvcWrap_SetThreadActivity64(system);
+ case SvcId::GetThreadContext3:
+ return SvcWrap_GetThreadContext364(system);
+ case SvcId::WaitForAddress:
+ return SvcWrap_WaitForAddress64(system);
+ case SvcId::SignalToAddress:
+ return SvcWrap_SignalToAddress64(system);
+ case SvcId::SynchronizePreemptionState:
+ return SvcWrap_SynchronizePreemptionState64(system);
+ case SvcId::GetResourceLimitPeakValue:
+ return SvcWrap_GetResourceLimitPeakValue64(system);
+ case SvcId::CreateIoPool:
+ return SvcWrap_CreateIoPool64(system);
+ case SvcId::CreateIoRegion:
+ return SvcWrap_CreateIoRegion64(system);
+ case SvcId::KernelDebug:
+ return SvcWrap_KernelDebug64(system);
+ case SvcId::ChangeKernelTraceState:
+ return SvcWrap_ChangeKernelTraceState64(system);
+ case SvcId::CreateSession:
+ return SvcWrap_CreateSession64(system);
+ case SvcId::AcceptSession:
+ return SvcWrap_AcceptSession64(system);
+ case SvcId::ReplyAndReceiveLight:
+ return SvcWrap_ReplyAndReceiveLight64(system);
+ case SvcId::ReplyAndReceive:
+ return SvcWrap_ReplyAndReceive64(system);
+ case SvcId::ReplyAndReceiveWithUserBuffer:
+ return SvcWrap_ReplyAndReceiveWithUserBuffer64(system);
+ case SvcId::CreateEvent:
+ return SvcWrap_CreateEvent64(system);
+ case SvcId::MapIoRegion:
+ return SvcWrap_MapIoRegion64(system);
+ case SvcId::UnmapIoRegion:
+ return SvcWrap_UnmapIoRegion64(system);
+ case SvcId::MapPhysicalMemoryUnsafe:
+ return SvcWrap_MapPhysicalMemoryUnsafe64(system);
+ case SvcId::UnmapPhysicalMemoryUnsafe:
+ return SvcWrap_UnmapPhysicalMemoryUnsafe64(system);
+ case SvcId::SetUnsafeLimit:
+ return SvcWrap_SetUnsafeLimit64(system);
+ case SvcId::CreateCodeMemory:
+ return SvcWrap_CreateCodeMemory64(system);
+ case SvcId::ControlCodeMemory:
+ return SvcWrap_ControlCodeMemory64(system);
+ case SvcId::SleepSystem:
+ return SvcWrap_SleepSystem64(system);
+ case SvcId::ReadWriteRegister:
+ return SvcWrap_ReadWriteRegister64(system);
+ case SvcId::SetProcessActivity:
+ return SvcWrap_SetProcessActivity64(system);
+ case SvcId::CreateSharedMemory:
+ return SvcWrap_CreateSharedMemory64(system);
+ case SvcId::MapTransferMemory:
+ return SvcWrap_MapTransferMemory64(system);
+ case SvcId::UnmapTransferMemory:
+ return SvcWrap_UnmapTransferMemory64(system);
+ case SvcId::CreateInterruptEvent:
+ return SvcWrap_CreateInterruptEvent64(system);
+ case SvcId::QueryPhysicalAddress:
+ return SvcWrap_QueryPhysicalAddress64(system);
+ case SvcId::QueryIoMapping:
+ return SvcWrap_QueryIoMapping64(system);
+ case SvcId::CreateDeviceAddressSpace:
+ return SvcWrap_CreateDeviceAddressSpace64(system);
+ case SvcId::AttachDeviceAddressSpace:
+ return SvcWrap_AttachDeviceAddressSpace64(system);
+ case SvcId::DetachDeviceAddressSpace:
+ return SvcWrap_DetachDeviceAddressSpace64(system);
+ case SvcId::MapDeviceAddressSpaceByForce:
+ return SvcWrap_MapDeviceAddressSpaceByForce64(system);
+ case SvcId::MapDeviceAddressSpaceAligned:
+ return SvcWrap_MapDeviceAddressSpaceAligned64(system);
+ case SvcId::UnmapDeviceAddressSpace:
+ return SvcWrap_UnmapDeviceAddressSpace64(system);
+ case SvcId::InvalidateProcessDataCache:
+ return SvcWrap_InvalidateProcessDataCache64(system);
+ case SvcId::StoreProcessDataCache:
+ return SvcWrap_StoreProcessDataCache64(system);
+ case SvcId::FlushProcessDataCache:
+ return SvcWrap_FlushProcessDataCache64(system);
+ case SvcId::DebugActiveProcess:
+ return SvcWrap_DebugActiveProcess64(system);
+ case SvcId::BreakDebugProcess:
+ return SvcWrap_BreakDebugProcess64(system);
+ case SvcId::TerminateDebugProcess:
+ return SvcWrap_TerminateDebugProcess64(system);
+ case SvcId::GetDebugEvent:
+ return SvcWrap_GetDebugEvent64(system);
+ case SvcId::ContinueDebugEvent:
+ return SvcWrap_ContinueDebugEvent64(system);
+ case SvcId::GetProcessList:
+ return SvcWrap_GetProcessList64(system);
+ case SvcId::GetThreadList:
+ return SvcWrap_GetThreadList64(system);
+ case SvcId::GetDebugThreadContext:
+ return SvcWrap_GetDebugThreadContext64(system);
+ case SvcId::SetDebugThreadContext:
+ return SvcWrap_SetDebugThreadContext64(system);
+ case SvcId::QueryDebugProcessMemory:
+ return SvcWrap_QueryDebugProcessMemory64(system);
+ case SvcId::ReadDebugProcessMemory:
+ return SvcWrap_ReadDebugProcessMemory64(system);
+ case SvcId::WriteDebugProcessMemory:
+ return SvcWrap_WriteDebugProcessMemory64(system);
+ case SvcId::SetHardwareBreakPoint:
+ return SvcWrap_SetHardwareBreakPoint64(system);
+ case SvcId::GetDebugThreadParam:
+ return SvcWrap_GetDebugThreadParam64(system);
+ case SvcId::GetSystemInfo:
+ return SvcWrap_GetSystemInfo64(system);
+ case SvcId::CreatePort:
+ return SvcWrap_CreatePort64(system);
+ case SvcId::ManageNamedPort:
+ return SvcWrap_ManageNamedPort64(system);
+ case SvcId::ConnectToPort:
+ return SvcWrap_ConnectToPort64(system);
+ case SvcId::SetProcessMemoryPermission:
+ return SvcWrap_SetProcessMemoryPermission64(system);
+ case SvcId::MapProcessMemory:
+ return SvcWrap_MapProcessMemory64(system);
+ case SvcId::UnmapProcessMemory:
+ return SvcWrap_UnmapProcessMemory64(system);
+ case SvcId::QueryProcessMemory:
+ return SvcWrap_QueryProcessMemory64(system);
+ case SvcId::MapProcessCodeMemory:
+ return SvcWrap_MapProcessCodeMemory64(system);
+ case SvcId::UnmapProcessCodeMemory:
+ return SvcWrap_UnmapProcessCodeMemory64(system);
+ case SvcId::CreateProcess:
+ return SvcWrap_CreateProcess64(system);
+ case SvcId::StartProcess:
+ return SvcWrap_StartProcess64(system);
+ case SvcId::TerminateProcess:
+ return SvcWrap_TerminateProcess64(system);
+ case SvcId::GetProcessInfo:
+ return SvcWrap_GetProcessInfo64(system);
+ case SvcId::CreateResourceLimit:
+ return SvcWrap_CreateResourceLimit64(system);
+ case SvcId::SetResourceLimitLimitValue:
+ return SvcWrap_SetResourceLimitLimitValue64(system);
+ case SvcId::CallSecureMonitor:
+ return SvcWrap_CallSecureMonitor64(system);
+ case SvcId::MapInsecureMemory:
+ return SvcWrap_MapInsecureMemory64(system);
+ case SvcId::UnmapInsecureMemory:
+ return SvcWrap_UnmapInsecureMemory64(system);
+ default:
+ LOG_CRITICAL(Kernel_SVC, "Unknown SVC {:x}!", imm);
+ break;
}
- return &SVC_Table_64[func_num];
}
+// clang-format on
-void Call(Core::System& system, u32 immediate) {
+void Call(Core::System& system, u32 imm) {
auto& kernel = system.Kernel();
kernel.EnterSVCProfile();
- auto* thread = GetCurrentThreadPointer(kernel);
- thread->SetIsCallingSvc();
-
- const FunctionDef* info = system.CurrentProcess()->Is64BitProcess() ? GetSVCInfo64(immediate)
- : GetSVCInfo32(immediate);
- if (info) {
- if (info->func) {
- info->func(system);
- } else {
- LOG_CRITICAL(Kernel_SVC, "Unimplemented SVC function {}(..)", info->name);
- }
+ if (GetCurrentProcess(system.Kernel()).Is64BitProcess()) {
+ Call64(system, imm);
} else {
- LOG_CRITICAL(Kernel_SVC, "Unknown SVC function 0x{:X}", immediate);
+ Call32(system, imm);
}
kernel.ExitSVCProfile();
diff --git a/src/core/hle/kernel/svc.h b/src/core/hle/kernel/svc.h
index 13f061b83..36e619959 100644
--- a/src/core/hle/kernel/svc.h
+++ b/src/core/hle/kernel/svc.h
@@ -1,16 +1,536 @@
-// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
-#pragma once
+// This file is automatically generated using svc_generator.py.
-#include "common/common_types.h"
+#pragma once
namespace Core {
class System;
}
+#include "common/common_types.h"
+#include "core/hle/kernel/svc_types.h"
+#include "core/hle/result.h"
+
namespace Kernel::Svc {
-void Call(Core::System& system, u32 immediate);
+// clang-format off
+Result SetHeapSize(Core::System& system, uintptr_t* out_address, uint64_t size);
+Result SetMemoryPermission(Core::System& system, uint64_t address, uint64_t size, MemoryPermission perm);
+Result SetMemoryAttribute(Core::System& system, uint64_t address, uint64_t size, uint32_t mask, uint32_t attr);
+Result MapMemory(Core::System& system, uint64_t dst_address, uint64_t src_address, uint64_t size);
+Result UnmapMemory(Core::System& system, uint64_t dst_address, uint64_t src_address, uint64_t size);
+Result QueryMemory(Core::System& system, uint64_t out_memory_info, PageInfo* out_page_info, uint64_t address);
+void ExitProcess(Core::System& system);
+Result CreateThread(Core::System& system, Handle* out_handle, uint64_t func, uint64_t arg, uint64_t stack_bottom, int32_t priority, int32_t core_id);
+Result StartThread(Core::System& system, Handle thread_handle);
+void ExitThread(Core::System& system);
+void SleepThread(Core::System& system, int64_t ns);
+Result GetThreadPriority(Core::System& system, int32_t* out_priority, Handle thread_handle);
+Result SetThreadPriority(Core::System& system, Handle thread_handle, int32_t priority);
+Result GetThreadCoreMask(Core::System& system, int32_t* out_core_id, uint64_t* out_affinity_mask, Handle thread_handle);
+Result SetThreadCoreMask(Core::System& system, Handle thread_handle, int32_t core_id, uint64_t affinity_mask);
+int32_t GetCurrentProcessorNumber(Core::System& system);
+Result SignalEvent(Core::System& system, Handle event_handle);
+Result ClearEvent(Core::System& system, Handle event_handle);
+Result MapSharedMemory(Core::System& system, Handle shmem_handle, uint64_t address, uint64_t size, MemoryPermission map_perm);
+Result UnmapSharedMemory(Core::System& system, Handle shmem_handle, uint64_t address, uint64_t size);
+Result CreateTransferMemory(Core::System& system, Handle* out_handle, uint64_t address, uint64_t size, MemoryPermission map_perm);
+Result CloseHandle(Core::System& system, Handle handle);
+Result ResetSignal(Core::System& system, Handle handle);
+Result WaitSynchronization(Core::System& system, int32_t* out_index, uint64_t handles, int32_t num_handles, int64_t timeout_ns);
+Result CancelSynchronization(Core::System& system, Handle handle);
+Result ArbitrateLock(Core::System& system, Handle thread_handle, uint64_t address, uint32_t tag);
+Result ArbitrateUnlock(Core::System& system, uint64_t address);
+Result WaitProcessWideKeyAtomic(Core::System& system, uint64_t address, uint64_t cv_key, uint32_t tag, int64_t timeout_ns);
+void SignalProcessWideKey(Core::System& system, uint64_t cv_key, int32_t count);
+int64_t GetSystemTick(Core::System& system);
+Result ConnectToNamedPort(Core::System& system, Handle* out_handle, uint64_t name);
+Result SendSyncRequest(Core::System& system, Handle session_handle);
+Result SendSyncRequestWithUserBuffer(Core::System& system, uint64_t message_buffer, uint64_t message_buffer_size, Handle session_handle);
+Result SendAsyncRequestWithUserBuffer(Core::System& system, Handle* out_event_handle, uint64_t message_buffer, uint64_t message_buffer_size, Handle session_handle);
+Result GetProcessId(Core::System& system, uint64_t* out_process_id, Handle process_handle);
+Result GetThreadId(Core::System& system, uint64_t* out_thread_id, Handle thread_handle);
+void Break(Core::System& system, BreakReason break_reason, uint64_t arg, uint64_t size);
+Result OutputDebugString(Core::System& system, uint64_t debug_str, uint64_t len);
+void ReturnFromException(Core::System& system, Result result);
+Result GetInfo(Core::System& system, uint64_t* out, InfoType info_type, Handle handle, uint64_t info_subtype);
+void FlushEntireDataCache(Core::System& system);
+Result FlushDataCache(Core::System& system, uint64_t address, uint64_t size);
+Result MapPhysicalMemory(Core::System& system, uint64_t address, uint64_t size);
+Result UnmapPhysicalMemory(Core::System& system, uint64_t address, uint64_t size);
+Result GetDebugFutureThreadInfo(Core::System& system, lp64::LastThreadContext* out_context, uint64_t* out_thread_id, Handle debug_handle, int64_t ns);
+Result GetLastThreadInfo(Core::System& system, lp64::LastThreadContext* out_context, uintptr_t* out_tls_address, uint32_t* out_flags);
+Result GetResourceLimitLimitValue(Core::System& system, int64_t* out_limit_value, Handle resource_limit_handle, LimitableResource which);
+Result GetResourceLimitCurrentValue(Core::System& system, int64_t* out_current_value, Handle resource_limit_handle, LimitableResource which);
+Result SetThreadActivity(Core::System& system, Handle thread_handle, ThreadActivity thread_activity);
+Result GetThreadContext3(Core::System& system, uint64_t out_context, Handle thread_handle);
+Result WaitForAddress(Core::System& system, uint64_t address, ArbitrationType arb_type, int32_t value, int64_t timeout_ns);
+Result SignalToAddress(Core::System& system, uint64_t address, SignalType signal_type, int32_t value, int32_t count);
+void SynchronizePreemptionState(Core::System& system);
+Result GetResourceLimitPeakValue(Core::System& system, int64_t* out_peak_value, Handle resource_limit_handle, LimitableResource which);
+Result CreateIoPool(Core::System& system, Handle* out_handle, IoPoolType which);
+Result CreateIoRegion(Core::System& system, Handle* out_handle, Handle io_pool, uint64_t physical_address, uint64_t size, MemoryMapping mapping, MemoryPermission perm);
+void KernelDebug(Core::System& system, KernelDebugType kern_debug_type, uint64_t arg0, uint64_t arg1, uint64_t arg2);
+void ChangeKernelTraceState(Core::System& system, KernelTraceState kern_trace_state);
+Result CreateSession(Core::System& system, Handle* out_server_session_handle, Handle* out_client_session_handle, bool is_light, uint64_t name);
+Result AcceptSession(Core::System& system, Handle* out_handle, Handle port);
+Result ReplyAndReceive(Core::System& system, int32_t* out_index, uint64_t handles, int32_t num_handles, Handle reply_target, int64_t timeout_ns);
+Result ReplyAndReceiveWithUserBuffer(Core::System& system, int32_t* out_index, uint64_t message_buffer, uint64_t message_buffer_size, uint64_t handles, int32_t num_handles, Handle reply_target, int64_t timeout_ns);
+Result CreateEvent(Core::System& system, Handle* out_write_handle, Handle* out_read_handle);
+Result MapIoRegion(Core::System& system, Handle io_region, uint64_t address, uint64_t size, MemoryPermission perm);
+Result UnmapIoRegion(Core::System& system, Handle io_region, uint64_t address, uint64_t size);
+Result MapPhysicalMemoryUnsafe(Core::System& system, uint64_t address, uint64_t size);
+Result UnmapPhysicalMemoryUnsafe(Core::System& system, uint64_t address, uint64_t size);
+Result SetUnsafeLimit(Core::System& system, uint64_t limit);
+Result CreateCodeMemory(Core::System& system, Handle* out_handle, uint64_t address, uint64_t size);
+Result ControlCodeMemory(Core::System& system, Handle code_memory_handle, CodeMemoryOperation operation, uint64_t address, uint64_t size, MemoryPermission perm);
+void SleepSystem(Core::System& system);
+Result ReadWriteRegister(Core::System& system, uint32_t* out_value, uint64_t address, uint32_t mask, uint32_t value);
+Result SetProcessActivity(Core::System& system, Handle process_handle, ProcessActivity process_activity);
+Result CreateSharedMemory(Core::System& system, Handle* out_handle, uint64_t size, MemoryPermission owner_perm, MemoryPermission remote_perm);
+Result MapTransferMemory(Core::System& system, Handle trmem_handle, uint64_t address, uint64_t size, MemoryPermission owner_perm);
+Result UnmapTransferMemory(Core::System& system, Handle trmem_handle, uint64_t address, uint64_t size);
+Result CreateInterruptEvent(Core::System& system, Handle* out_read_handle, int32_t interrupt_id, InterruptType interrupt_type);
+Result QueryPhysicalAddress(Core::System& system, lp64::PhysicalMemoryInfo* out_info, uint64_t address);
+Result QueryIoMapping(Core::System& system, uintptr_t* out_address, uintptr_t* out_size, uint64_t physical_address, uint64_t size);
+Result CreateDeviceAddressSpace(Core::System& system, Handle* out_handle, uint64_t das_address, uint64_t das_size);
+Result AttachDeviceAddressSpace(Core::System& system, DeviceName device_name, Handle das_handle);
+Result DetachDeviceAddressSpace(Core::System& system, DeviceName device_name, Handle das_handle);
+Result MapDeviceAddressSpaceByForce(Core::System& system, Handle das_handle, Handle process_handle, uint64_t process_address, uint64_t size, uint64_t device_address, uint32_t option);
+Result MapDeviceAddressSpaceAligned(Core::System& system, Handle das_handle, Handle process_handle, uint64_t process_address, uint64_t size, uint64_t device_address, uint32_t option);
+Result UnmapDeviceAddressSpace(Core::System& system, Handle das_handle, Handle process_handle, uint64_t process_address, uint64_t size, uint64_t device_address);
+Result InvalidateProcessDataCache(Core::System& system, Handle process_handle, uint64_t address, uint64_t size);
+Result StoreProcessDataCache(Core::System& system, Handle process_handle, uint64_t address, uint64_t size);
+Result FlushProcessDataCache(Core::System& system, Handle process_handle, uint64_t address, uint64_t size);
+Result DebugActiveProcess(Core::System& system, Handle* out_handle, uint64_t process_id);
+Result BreakDebugProcess(Core::System& system, Handle debug_handle);
+Result TerminateDebugProcess(Core::System& system, Handle debug_handle);
+Result GetDebugEvent(Core::System& system, uint64_t out_info, Handle debug_handle);
+Result ContinueDebugEvent(Core::System& system, Handle debug_handle, uint32_t flags, uint64_t thread_ids, int32_t num_thread_ids);
+Result GetProcessList(Core::System& system, int32_t* out_num_processes, uint64_t out_process_ids, int32_t max_out_count);
+Result GetThreadList(Core::System& system, int32_t* out_num_threads, uint64_t out_thread_ids, int32_t max_out_count, Handle debug_handle);
+Result GetDebugThreadContext(Core::System& system, uint64_t out_context, Handle debug_handle, uint64_t thread_id, uint32_t context_flags);
+Result SetDebugThreadContext(Core::System& system, Handle debug_handle, uint64_t thread_id, uint64_t context, uint32_t context_flags);
+Result QueryDebugProcessMemory(Core::System& system, uint64_t out_memory_info, PageInfo* out_page_info, Handle process_handle, uint64_t address);
+Result ReadDebugProcessMemory(Core::System& system, uint64_t buffer, Handle debug_handle, uint64_t address, uint64_t size);
+Result WriteDebugProcessMemory(Core::System& system, Handle debug_handle, uint64_t buffer, uint64_t address, uint64_t size);
+Result SetHardwareBreakPoint(Core::System& system, HardwareBreakPointRegisterName name, uint64_t flags, uint64_t value);
+Result GetDebugThreadParam(Core::System& system, uint64_t* out_64, uint32_t* out_32, Handle debug_handle, uint64_t thread_id, DebugThreadParam param);
+Result GetSystemInfo(Core::System& system, uint64_t* out, SystemInfoType info_type, Handle handle, uint64_t info_subtype);
+Result CreatePort(Core::System& system, Handle* out_server_handle, Handle* out_client_handle, int32_t max_sessions, bool is_light, uint64_t name);
+Result ManageNamedPort(Core::System& system, Handle* out_server_handle, uint64_t name, int32_t max_sessions);
+Result ConnectToPort(Core::System& system, Handle* out_handle, Handle port);
+Result SetProcessMemoryPermission(Core::System& system, Handle process_handle, uint64_t address, uint64_t size, MemoryPermission perm);
+Result MapProcessMemory(Core::System& system, uint64_t dst_address, Handle process_handle, uint64_t src_address, uint64_t size);
+Result UnmapProcessMemory(Core::System& system, uint64_t dst_address, Handle process_handle, uint64_t src_address, uint64_t size);
+Result QueryProcessMemory(Core::System& system, uint64_t out_memory_info, PageInfo* out_page_info, Handle process_handle, uint64_t address);
+Result MapProcessCodeMemory(Core::System& system, Handle process_handle, uint64_t dst_address, uint64_t src_address, uint64_t size);
+Result UnmapProcessCodeMemory(Core::System& system, Handle process_handle, uint64_t dst_address, uint64_t src_address, uint64_t size);
+Result CreateProcess(Core::System& system, Handle* out_handle, uint64_t parameters, uint64_t caps, int32_t num_caps);
+Result StartProcess(Core::System& system, Handle process_handle, int32_t priority, int32_t core_id, uint64_t main_thread_stack_size);
+Result TerminateProcess(Core::System& system, Handle process_handle);
+Result GetProcessInfo(Core::System& system, int64_t* out_info, Handle process_handle, ProcessInfoType info_type);
+Result CreateResourceLimit(Core::System& system, Handle* out_handle);
+Result SetResourceLimitLimitValue(Core::System& system, Handle resource_limit_handle, LimitableResource which, int64_t limit_value);
+Result MapInsecureMemory(Core::System& system, uint64_t address, uint64_t size);
+Result UnmapInsecureMemory(Core::System& system, uint64_t address, uint64_t size);
+
+Result SetHeapSize64From32(Core::System& system, uintptr_t* out_address, uint32_t size);
+Result SetMemoryPermission64From32(Core::System& system, uint32_t address, uint32_t size, MemoryPermission perm);
+Result SetMemoryAttribute64From32(Core::System& system, uint32_t address, uint32_t size, uint32_t mask, uint32_t attr);
+Result MapMemory64From32(Core::System& system, uint32_t dst_address, uint32_t src_address, uint32_t size);
+Result UnmapMemory64From32(Core::System& system, uint32_t dst_address, uint32_t src_address, uint32_t size);
+Result QueryMemory64From32(Core::System& system, uint32_t out_memory_info, PageInfo* out_page_info, uint32_t address);
+void ExitProcess64From32(Core::System& system);
+Result CreateThread64From32(Core::System& system, Handle* out_handle, uint32_t func, uint32_t arg, uint32_t stack_bottom, int32_t priority, int32_t core_id);
+Result StartThread64From32(Core::System& system, Handle thread_handle);
+void ExitThread64From32(Core::System& system);
+void SleepThread64From32(Core::System& system, int64_t ns);
+Result GetThreadPriority64From32(Core::System& system, int32_t* out_priority, Handle thread_handle);
+Result SetThreadPriority64From32(Core::System& system, Handle thread_handle, int32_t priority);
+Result GetThreadCoreMask64From32(Core::System& system, int32_t* out_core_id, uint64_t* out_affinity_mask, Handle thread_handle);
+Result SetThreadCoreMask64From32(Core::System& system, Handle thread_handle, int32_t core_id, uint64_t affinity_mask);
+int32_t GetCurrentProcessorNumber64From32(Core::System& system);
+Result SignalEvent64From32(Core::System& system, Handle event_handle);
+Result ClearEvent64From32(Core::System& system, Handle event_handle);
+Result MapSharedMemory64From32(Core::System& system, Handle shmem_handle, uint32_t address, uint32_t size, MemoryPermission map_perm);
+Result UnmapSharedMemory64From32(Core::System& system, Handle shmem_handle, uint32_t address, uint32_t size);
+Result CreateTransferMemory64From32(Core::System& system, Handle* out_handle, uint32_t address, uint32_t size, MemoryPermission map_perm);
+Result CloseHandle64From32(Core::System& system, Handle handle);
+Result ResetSignal64From32(Core::System& system, Handle handle);
+Result WaitSynchronization64From32(Core::System& system, int32_t* out_index, uint32_t handles, int32_t num_handles, int64_t timeout_ns);
+Result CancelSynchronization64From32(Core::System& system, Handle handle);
+Result ArbitrateLock64From32(Core::System& system, Handle thread_handle, uint32_t address, uint32_t tag);
+Result ArbitrateUnlock64From32(Core::System& system, uint32_t address);
+Result WaitProcessWideKeyAtomic64From32(Core::System& system, uint32_t address, uint32_t cv_key, uint32_t tag, int64_t timeout_ns);
+void SignalProcessWideKey64From32(Core::System& system, uint32_t cv_key, int32_t count);
+int64_t GetSystemTick64From32(Core::System& system);
+Result ConnectToNamedPort64From32(Core::System& system, Handle* out_handle, uint32_t name);
+Result SendSyncRequest64From32(Core::System& system, Handle session_handle);
+Result SendSyncRequestWithUserBuffer64From32(Core::System& system, uint32_t message_buffer, uint32_t message_buffer_size, Handle session_handle);
+Result SendAsyncRequestWithUserBuffer64From32(Core::System& system, Handle* out_event_handle, uint32_t message_buffer, uint32_t message_buffer_size, Handle session_handle);
+Result GetProcessId64From32(Core::System& system, uint64_t* out_process_id, Handle process_handle);
+Result GetThreadId64From32(Core::System& system, uint64_t* out_thread_id, Handle thread_handle);
+void Break64From32(Core::System& system, BreakReason break_reason, uint32_t arg, uint32_t size);
+Result OutputDebugString64From32(Core::System& system, uint32_t debug_str, uint32_t len);
+void ReturnFromException64From32(Core::System& system, Result result);
+Result GetInfo64From32(Core::System& system, uint64_t* out, InfoType info_type, Handle handle, uint64_t info_subtype);
+void FlushEntireDataCache64From32(Core::System& system);
+Result FlushDataCache64From32(Core::System& system, uint32_t address, uint32_t size);
+Result MapPhysicalMemory64From32(Core::System& system, uint32_t address, uint32_t size);
+Result UnmapPhysicalMemory64From32(Core::System& system, uint32_t address, uint32_t size);
+Result GetDebugFutureThreadInfo64From32(Core::System& system, ilp32::LastThreadContext* out_context, uint64_t* out_thread_id, Handle debug_handle, int64_t ns);
+Result GetLastThreadInfo64From32(Core::System& system, ilp32::LastThreadContext* out_context, uintptr_t* out_tls_address, uint32_t* out_flags);
+Result GetResourceLimitLimitValue64From32(Core::System& system, int64_t* out_limit_value, Handle resource_limit_handle, LimitableResource which);
+Result GetResourceLimitCurrentValue64From32(Core::System& system, int64_t* out_current_value, Handle resource_limit_handle, LimitableResource which);
+Result SetThreadActivity64From32(Core::System& system, Handle thread_handle, ThreadActivity thread_activity);
+Result GetThreadContext364From32(Core::System& system, uint32_t out_context, Handle thread_handle);
+Result WaitForAddress64From32(Core::System& system, uint32_t address, ArbitrationType arb_type, int32_t value, int64_t timeout_ns);
+Result SignalToAddress64From32(Core::System& system, uint32_t address, SignalType signal_type, int32_t value, int32_t count);
+void SynchronizePreemptionState64From32(Core::System& system);
+Result GetResourceLimitPeakValue64From32(Core::System& system, int64_t* out_peak_value, Handle resource_limit_handle, LimitableResource which);
+Result CreateIoPool64From32(Core::System& system, Handle* out_handle, IoPoolType which);
+Result CreateIoRegion64From32(Core::System& system, Handle* out_handle, Handle io_pool, uint64_t physical_address, uint32_t size, MemoryMapping mapping, MemoryPermission perm);
+void KernelDebug64From32(Core::System& system, KernelDebugType kern_debug_type, uint64_t arg0, uint64_t arg1, uint64_t arg2);
+void ChangeKernelTraceState64From32(Core::System& system, KernelTraceState kern_trace_state);
+Result CreateSession64From32(Core::System& system, Handle* out_server_session_handle, Handle* out_client_session_handle, bool is_light, uint32_t name);
+Result AcceptSession64From32(Core::System& system, Handle* out_handle, Handle port);
+Result ReplyAndReceive64From32(Core::System& system, int32_t* out_index, uint32_t handles, int32_t num_handles, Handle reply_target, int64_t timeout_ns);
+Result ReplyAndReceiveWithUserBuffer64From32(Core::System& system, int32_t* out_index, uint32_t message_buffer, uint32_t message_buffer_size, uint32_t handles, int32_t num_handles, Handle reply_target, int64_t timeout_ns);
+Result CreateEvent64From32(Core::System& system, Handle* out_write_handle, Handle* out_read_handle);
+Result MapIoRegion64From32(Core::System& system, Handle io_region, uint32_t address, uint32_t size, MemoryPermission perm);
+Result UnmapIoRegion64From32(Core::System& system, Handle io_region, uint32_t address, uint32_t size);
+Result MapPhysicalMemoryUnsafe64From32(Core::System& system, uint32_t address, uint32_t size);
+Result UnmapPhysicalMemoryUnsafe64From32(Core::System& system, uint32_t address, uint32_t size);
+Result SetUnsafeLimit64From32(Core::System& system, uint32_t limit);
+Result CreateCodeMemory64From32(Core::System& system, Handle* out_handle, uint32_t address, uint32_t size);
+Result ControlCodeMemory64From32(Core::System& system, Handle code_memory_handle, CodeMemoryOperation operation, uint64_t address, uint64_t size, MemoryPermission perm);
+void SleepSystem64From32(Core::System& system);
+Result ReadWriteRegister64From32(Core::System& system, uint32_t* out_value, uint64_t address, uint32_t mask, uint32_t value);
+Result SetProcessActivity64From32(Core::System& system, Handle process_handle, ProcessActivity process_activity);
+Result CreateSharedMemory64From32(Core::System& system, Handle* out_handle, uint32_t size, MemoryPermission owner_perm, MemoryPermission remote_perm);
+Result MapTransferMemory64From32(Core::System& system, Handle trmem_handle, uint32_t address, uint32_t size, MemoryPermission owner_perm);
+Result UnmapTransferMemory64From32(Core::System& system, Handle trmem_handle, uint32_t address, uint32_t size);
+Result CreateInterruptEvent64From32(Core::System& system, Handle* out_read_handle, int32_t interrupt_id, InterruptType interrupt_type);
+Result QueryPhysicalAddress64From32(Core::System& system, ilp32::PhysicalMemoryInfo* out_info, uint32_t address);
+Result QueryIoMapping64From32(Core::System& system, uintptr_t* out_address, uintptr_t* out_size, uint64_t physical_address, uint32_t size);
+Result CreateDeviceAddressSpace64From32(Core::System& system, Handle* out_handle, uint64_t das_address, uint64_t das_size);
+Result AttachDeviceAddressSpace64From32(Core::System& system, DeviceName device_name, Handle das_handle);
+Result DetachDeviceAddressSpace64From32(Core::System& system, DeviceName device_name, Handle das_handle);
+Result MapDeviceAddressSpaceByForce64From32(Core::System& system, Handle das_handle, Handle process_handle, uint64_t process_address, uint32_t size, uint64_t device_address, uint32_t option);
+Result MapDeviceAddressSpaceAligned64From32(Core::System& system, Handle das_handle, Handle process_handle, uint64_t process_address, uint32_t size, uint64_t device_address, uint32_t option);
+Result UnmapDeviceAddressSpace64From32(Core::System& system, Handle das_handle, Handle process_handle, uint64_t process_address, uint32_t size, uint64_t device_address);
+Result InvalidateProcessDataCache64From32(Core::System& system, Handle process_handle, uint64_t address, uint64_t size);
+Result StoreProcessDataCache64From32(Core::System& system, Handle process_handle, uint64_t address, uint64_t size);
+Result FlushProcessDataCache64From32(Core::System& system, Handle process_handle, uint64_t address, uint64_t size);
+Result DebugActiveProcess64From32(Core::System& system, Handle* out_handle, uint64_t process_id);
+Result BreakDebugProcess64From32(Core::System& system, Handle debug_handle);
+Result TerminateDebugProcess64From32(Core::System& system, Handle debug_handle);
+Result GetDebugEvent64From32(Core::System& system, uint32_t out_info, Handle debug_handle);
+Result ContinueDebugEvent64From32(Core::System& system, Handle debug_handle, uint32_t flags, uint32_t thread_ids, int32_t num_thread_ids);
+Result GetProcessList64From32(Core::System& system, int32_t* out_num_processes, uint32_t out_process_ids, int32_t max_out_count);
+Result GetThreadList64From32(Core::System& system, int32_t* out_num_threads, uint32_t out_thread_ids, int32_t max_out_count, Handle debug_handle);
+Result GetDebugThreadContext64From32(Core::System& system, uint32_t out_context, Handle debug_handle, uint64_t thread_id, uint32_t context_flags);
+Result SetDebugThreadContext64From32(Core::System& system, Handle debug_handle, uint64_t thread_id, uint32_t context, uint32_t context_flags);
+Result QueryDebugProcessMemory64From32(Core::System& system, uint32_t out_memory_info, PageInfo* out_page_info, Handle process_handle, uint32_t address);
+Result ReadDebugProcessMemory64From32(Core::System& system, uint32_t buffer, Handle debug_handle, uint32_t address, uint32_t size);
+Result WriteDebugProcessMemory64From32(Core::System& system, Handle debug_handle, uint32_t buffer, uint32_t address, uint32_t size);
+Result SetHardwareBreakPoint64From32(Core::System& system, HardwareBreakPointRegisterName name, uint64_t flags, uint64_t value);
+Result GetDebugThreadParam64From32(Core::System& system, uint64_t* out_64, uint32_t* out_32, Handle debug_handle, uint64_t thread_id, DebugThreadParam param);
+Result GetSystemInfo64From32(Core::System& system, uint64_t* out, SystemInfoType info_type, Handle handle, uint64_t info_subtype);
+Result CreatePort64From32(Core::System& system, Handle* out_server_handle, Handle* out_client_handle, int32_t max_sessions, bool is_light, uint32_t name);
+Result ManageNamedPort64From32(Core::System& system, Handle* out_server_handle, uint32_t name, int32_t max_sessions);
+Result ConnectToPort64From32(Core::System& system, Handle* out_handle, Handle port);
+Result SetProcessMemoryPermission64From32(Core::System& system, Handle process_handle, uint64_t address, uint64_t size, MemoryPermission perm);
+Result MapProcessMemory64From32(Core::System& system, uint32_t dst_address, Handle process_handle, uint64_t src_address, uint32_t size);
+Result UnmapProcessMemory64From32(Core::System& system, uint32_t dst_address, Handle process_handle, uint64_t src_address, uint32_t size);
+Result QueryProcessMemory64From32(Core::System& system, uint32_t out_memory_info, PageInfo* out_page_info, Handle process_handle, uint64_t address);
+Result MapProcessCodeMemory64From32(Core::System& system, Handle process_handle, uint64_t dst_address, uint64_t src_address, uint64_t size);
+Result UnmapProcessCodeMemory64From32(Core::System& system, Handle process_handle, uint64_t dst_address, uint64_t src_address, uint64_t size);
+Result CreateProcess64From32(Core::System& system, Handle* out_handle, uint32_t parameters, uint32_t caps, int32_t num_caps);
+Result StartProcess64From32(Core::System& system, Handle process_handle, int32_t priority, int32_t core_id, uint64_t main_thread_stack_size);
+Result TerminateProcess64From32(Core::System& system, Handle process_handle);
+Result GetProcessInfo64From32(Core::System& system, int64_t* out_info, Handle process_handle, ProcessInfoType info_type);
+Result CreateResourceLimit64From32(Core::System& system, Handle* out_handle);
+Result SetResourceLimitLimitValue64From32(Core::System& system, Handle resource_limit_handle, LimitableResource which, int64_t limit_value);
+Result MapInsecureMemory64From32(Core::System& system, uint32_t address, uint32_t size);
+Result UnmapInsecureMemory64From32(Core::System& system, uint32_t address, uint32_t size);
+
+Result SetHeapSize64(Core::System& system, uintptr_t* out_address, uint64_t size);
+Result SetMemoryPermission64(Core::System& system, uint64_t address, uint64_t size, MemoryPermission perm);
+Result SetMemoryAttribute64(Core::System& system, uint64_t address, uint64_t size, uint32_t mask, uint32_t attr);
+Result MapMemory64(Core::System& system, uint64_t dst_address, uint64_t src_address, uint64_t size);
+Result UnmapMemory64(Core::System& system, uint64_t dst_address, uint64_t src_address, uint64_t size);
+Result QueryMemory64(Core::System& system, uint64_t out_memory_info, PageInfo* out_page_info, uint64_t address);
+void ExitProcess64(Core::System& system);
+Result CreateThread64(Core::System& system, Handle* out_handle, uint64_t func, uint64_t arg, uint64_t stack_bottom, int32_t priority, int32_t core_id);
+Result StartThread64(Core::System& system, Handle thread_handle);
+void ExitThread64(Core::System& system);
+void SleepThread64(Core::System& system, int64_t ns);
+Result GetThreadPriority64(Core::System& system, int32_t* out_priority, Handle thread_handle);
+Result SetThreadPriority64(Core::System& system, Handle thread_handle, int32_t priority);
+Result GetThreadCoreMask64(Core::System& system, int32_t* out_core_id, uint64_t* out_affinity_mask, Handle thread_handle);
+Result SetThreadCoreMask64(Core::System& system, Handle thread_handle, int32_t core_id, uint64_t affinity_mask);
+int32_t GetCurrentProcessorNumber64(Core::System& system);
+Result SignalEvent64(Core::System& system, Handle event_handle);
+Result ClearEvent64(Core::System& system, Handle event_handle);
+Result MapSharedMemory64(Core::System& system, Handle shmem_handle, uint64_t address, uint64_t size, MemoryPermission map_perm);
+Result UnmapSharedMemory64(Core::System& system, Handle shmem_handle, uint64_t address, uint64_t size);
+Result CreateTransferMemory64(Core::System& system, Handle* out_handle, uint64_t address, uint64_t size, MemoryPermission map_perm);
+Result CloseHandle64(Core::System& system, Handle handle);
+Result ResetSignal64(Core::System& system, Handle handle);
+Result WaitSynchronization64(Core::System& system, int32_t* out_index, uint64_t handles, int32_t num_handles, int64_t timeout_ns);
+Result CancelSynchronization64(Core::System& system, Handle handle);
+Result ArbitrateLock64(Core::System& system, Handle thread_handle, uint64_t address, uint32_t tag);
+Result ArbitrateUnlock64(Core::System& system, uint64_t address);
+Result WaitProcessWideKeyAtomic64(Core::System& system, uint64_t address, uint64_t cv_key, uint32_t tag, int64_t timeout_ns);
+void SignalProcessWideKey64(Core::System& system, uint64_t cv_key, int32_t count);
+int64_t GetSystemTick64(Core::System& system);
+Result ConnectToNamedPort64(Core::System& system, Handle* out_handle, uint64_t name);
+Result SendSyncRequest64(Core::System& system, Handle session_handle);
+Result SendSyncRequestWithUserBuffer64(Core::System& system, uint64_t message_buffer, uint64_t message_buffer_size, Handle session_handle);
+Result SendAsyncRequestWithUserBuffer64(Core::System& system, Handle* out_event_handle, uint64_t message_buffer, uint64_t message_buffer_size, Handle session_handle);
+Result GetProcessId64(Core::System& system, uint64_t* out_process_id, Handle process_handle);
+Result GetThreadId64(Core::System& system, uint64_t* out_thread_id, Handle thread_handle);
+void Break64(Core::System& system, BreakReason break_reason, uint64_t arg, uint64_t size);
+Result OutputDebugString64(Core::System& system, uint64_t debug_str, uint64_t len);
+void ReturnFromException64(Core::System& system, Result result);
+Result GetInfo64(Core::System& system, uint64_t* out, InfoType info_type, Handle handle, uint64_t info_subtype);
+void FlushEntireDataCache64(Core::System& system);
+Result FlushDataCache64(Core::System& system, uint64_t address, uint64_t size);
+Result MapPhysicalMemory64(Core::System& system, uint64_t address, uint64_t size);
+Result UnmapPhysicalMemory64(Core::System& system, uint64_t address, uint64_t size);
+Result GetDebugFutureThreadInfo64(Core::System& system, lp64::LastThreadContext* out_context, uint64_t* out_thread_id, Handle debug_handle, int64_t ns);
+Result GetLastThreadInfo64(Core::System& system, lp64::LastThreadContext* out_context, uintptr_t* out_tls_address, uint32_t* out_flags);
+Result GetResourceLimitLimitValue64(Core::System& system, int64_t* out_limit_value, Handle resource_limit_handle, LimitableResource which);
+Result GetResourceLimitCurrentValue64(Core::System& system, int64_t* out_current_value, Handle resource_limit_handle, LimitableResource which);
+Result SetThreadActivity64(Core::System& system, Handle thread_handle, ThreadActivity thread_activity);
+Result GetThreadContext364(Core::System& system, uint64_t out_context, Handle thread_handle);
+Result WaitForAddress64(Core::System& system, uint64_t address, ArbitrationType arb_type, int32_t value, int64_t timeout_ns);
+Result SignalToAddress64(Core::System& system, uint64_t address, SignalType signal_type, int32_t value, int32_t count);
+void SynchronizePreemptionState64(Core::System& system);
+Result GetResourceLimitPeakValue64(Core::System& system, int64_t* out_peak_value, Handle resource_limit_handle, LimitableResource which);
+Result CreateIoPool64(Core::System& system, Handle* out_handle, IoPoolType which);
+Result CreateIoRegion64(Core::System& system, Handle* out_handle, Handle io_pool, uint64_t physical_address, uint64_t size, MemoryMapping mapping, MemoryPermission perm);
+void KernelDebug64(Core::System& system, KernelDebugType kern_debug_type, uint64_t arg0, uint64_t arg1, uint64_t arg2);
+void ChangeKernelTraceState64(Core::System& system, KernelTraceState kern_trace_state);
+Result CreateSession64(Core::System& system, Handle* out_server_session_handle, Handle* out_client_session_handle, bool is_light, uint64_t name);
+Result AcceptSession64(Core::System& system, Handle* out_handle, Handle port);
+Result ReplyAndReceive64(Core::System& system, int32_t* out_index, uint64_t handles, int32_t num_handles, Handle reply_target, int64_t timeout_ns);
+Result ReplyAndReceiveWithUserBuffer64(Core::System& system, int32_t* out_index, uint64_t message_buffer, uint64_t message_buffer_size, uint64_t handles, int32_t num_handles, Handle reply_target, int64_t timeout_ns);
+Result CreateEvent64(Core::System& system, Handle* out_write_handle, Handle* out_read_handle);
+Result MapIoRegion64(Core::System& system, Handle io_region, uint64_t address, uint64_t size, MemoryPermission perm);
+Result UnmapIoRegion64(Core::System& system, Handle io_region, uint64_t address, uint64_t size);
+Result MapPhysicalMemoryUnsafe64(Core::System& system, uint64_t address, uint64_t size);
+Result UnmapPhysicalMemoryUnsafe64(Core::System& system, uint64_t address, uint64_t size);
+Result SetUnsafeLimit64(Core::System& system, uint64_t limit);
+Result CreateCodeMemory64(Core::System& system, Handle* out_handle, uint64_t address, uint64_t size);
+Result ControlCodeMemory64(Core::System& system, Handle code_memory_handle, CodeMemoryOperation operation, uint64_t address, uint64_t size, MemoryPermission perm);
+void SleepSystem64(Core::System& system);
+Result ReadWriteRegister64(Core::System& system, uint32_t* out_value, uint64_t address, uint32_t mask, uint32_t value);
+Result SetProcessActivity64(Core::System& system, Handle process_handle, ProcessActivity process_activity);
+Result CreateSharedMemory64(Core::System& system, Handle* out_handle, uint64_t size, MemoryPermission owner_perm, MemoryPermission remote_perm);
+Result MapTransferMemory64(Core::System& system, Handle trmem_handle, uint64_t address, uint64_t size, MemoryPermission owner_perm);
+Result UnmapTransferMemory64(Core::System& system, Handle trmem_handle, uint64_t address, uint64_t size);
+Result CreateInterruptEvent64(Core::System& system, Handle* out_read_handle, int32_t interrupt_id, InterruptType interrupt_type);
+Result QueryPhysicalAddress64(Core::System& system, lp64::PhysicalMemoryInfo* out_info, uint64_t address);
+Result QueryIoMapping64(Core::System& system, uintptr_t* out_address, uintptr_t* out_size, uint64_t physical_address, uint64_t size);
+Result CreateDeviceAddressSpace64(Core::System& system, Handle* out_handle, uint64_t das_address, uint64_t das_size);
+Result AttachDeviceAddressSpace64(Core::System& system, DeviceName device_name, Handle das_handle);
+Result DetachDeviceAddressSpace64(Core::System& system, DeviceName device_name, Handle das_handle);
+Result MapDeviceAddressSpaceByForce64(Core::System& system, Handle das_handle, Handle process_handle, uint64_t process_address, uint64_t size, uint64_t device_address, uint32_t option);
+Result MapDeviceAddressSpaceAligned64(Core::System& system, Handle das_handle, Handle process_handle, uint64_t process_address, uint64_t size, uint64_t device_address, uint32_t option);
+Result UnmapDeviceAddressSpace64(Core::System& system, Handle das_handle, Handle process_handle, uint64_t process_address, uint64_t size, uint64_t device_address);
+Result InvalidateProcessDataCache64(Core::System& system, Handle process_handle, uint64_t address, uint64_t size);
+Result StoreProcessDataCache64(Core::System& system, Handle process_handle, uint64_t address, uint64_t size);
+Result FlushProcessDataCache64(Core::System& system, Handle process_handle, uint64_t address, uint64_t size);
+Result DebugActiveProcess64(Core::System& system, Handle* out_handle, uint64_t process_id);
+Result BreakDebugProcess64(Core::System& system, Handle debug_handle);
+Result TerminateDebugProcess64(Core::System& system, Handle debug_handle);
+Result GetDebugEvent64(Core::System& system, uint64_t out_info, Handle debug_handle);
+Result ContinueDebugEvent64(Core::System& system, Handle debug_handle, uint32_t flags, uint64_t thread_ids, int32_t num_thread_ids);
+Result GetProcessList64(Core::System& system, int32_t* out_num_processes, uint64_t out_process_ids, int32_t max_out_count);
+Result GetThreadList64(Core::System& system, int32_t* out_num_threads, uint64_t out_thread_ids, int32_t max_out_count, Handle debug_handle);
+Result GetDebugThreadContext64(Core::System& system, uint64_t out_context, Handle debug_handle, uint64_t thread_id, uint32_t context_flags);
+Result SetDebugThreadContext64(Core::System& system, Handle debug_handle, uint64_t thread_id, uint64_t context, uint32_t context_flags);
+Result QueryDebugProcessMemory64(Core::System& system, uint64_t out_memory_info, PageInfo* out_page_info, Handle process_handle, uint64_t address);
+Result ReadDebugProcessMemory64(Core::System& system, uint64_t buffer, Handle debug_handle, uint64_t address, uint64_t size);
+Result WriteDebugProcessMemory64(Core::System& system, Handle debug_handle, uint64_t buffer, uint64_t address, uint64_t size);
+Result SetHardwareBreakPoint64(Core::System& system, HardwareBreakPointRegisterName name, uint64_t flags, uint64_t value);
+Result GetDebugThreadParam64(Core::System& system, uint64_t* out_64, uint32_t* out_32, Handle debug_handle, uint64_t thread_id, DebugThreadParam param);
+Result GetSystemInfo64(Core::System& system, uint64_t* out, SystemInfoType info_type, Handle handle, uint64_t info_subtype);
+Result CreatePort64(Core::System& system, Handle* out_server_handle, Handle* out_client_handle, int32_t max_sessions, bool is_light, uint64_t name);
+Result ManageNamedPort64(Core::System& system, Handle* out_server_handle, uint64_t name, int32_t max_sessions);
+Result ConnectToPort64(Core::System& system, Handle* out_handle, Handle port);
+Result SetProcessMemoryPermission64(Core::System& system, Handle process_handle, uint64_t address, uint64_t size, MemoryPermission perm);
+Result MapProcessMemory64(Core::System& system, uint64_t dst_address, Handle process_handle, uint64_t src_address, uint64_t size);
+Result UnmapProcessMemory64(Core::System& system, uint64_t dst_address, Handle process_handle, uint64_t src_address, uint64_t size);
+Result QueryProcessMemory64(Core::System& system, uint64_t out_memory_info, PageInfo* out_page_info, Handle process_handle, uint64_t address);
+Result MapProcessCodeMemory64(Core::System& system, Handle process_handle, uint64_t dst_address, uint64_t src_address, uint64_t size);
+Result UnmapProcessCodeMemory64(Core::System& system, Handle process_handle, uint64_t dst_address, uint64_t src_address, uint64_t size);
+Result CreateProcess64(Core::System& system, Handle* out_handle, uint64_t parameters, uint64_t caps, int32_t num_caps);
+Result StartProcess64(Core::System& system, Handle process_handle, int32_t priority, int32_t core_id, uint64_t main_thread_stack_size);
+Result TerminateProcess64(Core::System& system, Handle process_handle);
+Result GetProcessInfo64(Core::System& system, int64_t* out_info, Handle process_handle, ProcessInfoType info_type);
+Result CreateResourceLimit64(Core::System& system, Handle* out_handle);
+Result SetResourceLimitLimitValue64(Core::System& system, Handle resource_limit_handle, LimitableResource which, int64_t limit_value);
+Result MapInsecureMemory64(Core::System& system, uint64_t address, uint64_t size);
+Result UnmapInsecureMemory64(Core::System& system, uint64_t address, uint64_t size);
+
+enum class SvcId : u32 {
+ SetHeapSize = 0x1,
+ SetMemoryPermission = 0x2,
+ SetMemoryAttribute = 0x3,
+ MapMemory = 0x4,
+ UnmapMemory = 0x5,
+ QueryMemory = 0x6,
+ ExitProcess = 0x7,
+ CreateThread = 0x8,
+ StartThread = 0x9,
+ ExitThread = 0xa,
+ SleepThread = 0xb,
+ GetThreadPriority = 0xc,
+ SetThreadPriority = 0xd,
+ GetThreadCoreMask = 0xe,
+ SetThreadCoreMask = 0xf,
+ GetCurrentProcessorNumber = 0x10,
+ SignalEvent = 0x11,
+ ClearEvent = 0x12,
+ MapSharedMemory = 0x13,
+ UnmapSharedMemory = 0x14,
+ CreateTransferMemory = 0x15,
+ CloseHandle = 0x16,
+ ResetSignal = 0x17,
+ WaitSynchronization = 0x18,
+ CancelSynchronization = 0x19,
+ ArbitrateLock = 0x1a,
+ ArbitrateUnlock = 0x1b,
+ WaitProcessWideKeyAtomic = 0x1c,
+ SignalProcessWideKey = 0x1d,
+ GetSystemTick = 0x1e,
+ ConnectToNamedPort = 0x1f,
+ SendSyncRequestLight = 0x20,
+ SendSyncRequest = 0x21,
+ SendSyncRequestWithUserBuffer = 0x22,
+ SendAsyncRequestWithUserBuffer = 0x23,
+ GetProcessId = 0x24,
+ GetThreadId = 0x25,
+ Break = 0x26,
+ OutputDebugString = 0x27,
+ ReturnFromException = 0x28,
+ GetInfo = 0x29,
+ FlushEntireDataCache = 0x2a,
+ FlushDataCache = 0x2b,
+ MapPhysicalMemory = 0x2c,
+ UnmapPhysicalMemory = 0x2d,
+ GetDebugFutureThreadInfo = 0x2e,
+ GetLastThreadInfo = 0x2f,
+ GetResourceLimitLimitValue = 0x30,
+ GetResourceLimitCurrentValue = 0x31,
+ SetThreadActivity = 0x32,
+ GetThreadContext3 = 0x33,
+ WaitForAddress = 0x34,
+ SignalToAddress = 0x35,
+ SynchronizePreemptionState = 0x36,
+ GetResourceLimitPeakValue = 0x37,
+ CreateIoPool = 0x39,
+ CreateIoRegion = 0x3a,
+ KernelDebug = 0x3c,
+ ChangeKernelTraceState = 0x3d,
+ CreateSession = 0x40,
+ AcceptSession = 0x41,
+ ReplyAndReceiveLight = 0x42,
+ ReplyAndReceive = 0x43,
+ ReplyAndReceiveWithUserBuffer = 0x44,
+ CreateEvent = 0x45,
+ MapIoRegion = 0x46,
+ UnmapIoRegion = 0x47,
+ MapPhysicalMemoryUnsafe = 0x48,
+ UnmapPhysicalMemoryUnsafe = 0x49,
+ SetUnsafeLimit = 0x4a,
+ CreateCodeMemory = 0x4b,
+ ControlCodeMemory = 0x4c,
+ SleepSystem = 0x4d,
+ ReadWriteRegister = 0x4e,
+ SetProcessActivity = 0x4f,
+ CreateSharedMemory = 0x50,
+ MapTransferMemory = 0x51,
+ UnmapTransferMemory = 0x52,
+ CreateInterruptEvent = 0x53,
+ QueryPhysicalAddress = 0x54,
+ QueryIoMapping = 0x55,
+ CreateDeviceAddressSpace = 0x56,
+ AttachDeviceAddressSpace = 0x57,
+ DetachDeviceAddressSpace = 0x58,
+ MapDeviceAddressSpaceByForce = 0x59,
+ MapDeviceAddressSpaceAligned = 0x5a,
+ UnmapDeviceAddressSpace = 0x5c,
+ InvalidateProcessDataCache = 0x5d,
+ StoreProcessDataCache = 0x5e,
+ FlushProcessDataCache = 0x5f,
+ DebugActiveProcess = 0x60,
+ BreakDebugProcess = 0x61,
+ TerminateDebugProcess = 0x62,
+ GetDebugEvent = 0x63,
+ ContinueDebugEvent = 0x64,
+ GetProcessList = 0x65,
+ GetThreadList = 0x66,
+ GetDebugThreadContext = 0x67,
+ SetDebugThreadContext = 0x68,
+ QueryDebugProcessMemory = 0x69,
+ ReadDebugProcessMemory = 0x6a,
+ WriteDebugProcessMemory = 0x6b,
+ SetHardwareBreakPoint = 0x6c,
+ GetDebugThreadParam = 0x6d,
+ GetSystemInfo = 0x6f,
+ CreatePort = 0x70,
+ ManageNamedPort = 0x71,
+ ConnectToPort = 0x72,
+ SetProcessMemoryPermission = 0x73,
+ MapProcessMemory = 0x74,
+ UnmapProcessMemory = 0x75,
+ QueryProcessMemory = 0x76,
+ MapProcessCodeMemory = 0x77,
+ UnmapProcessCodeMemory = 0x78,
+ CreateProcess = 0x79,
+ StartProcess = 0x7a,
+ TerminateProcess = 0x7b,
+ GetProcessInfo = 0x7c,
+ CreateResourceLimit = 0x7d,
+ SetResourceLimitLimitValue = 0x7e,
+ CallSecureMonitor = 0x7f,
+ MapInsecureMemory = 0x90,
+ UnmapInsecureMemory = 0x91,
+};
+// clang-format on
+
+// Custom ABI.
+Result ReplyAndReceiveLight(Core::System& system, Handle handle, uint32_t* args);
+Result ReplyAndReceiveLight64From32(Core::System& system, Handle handle, uint32_t* args);
+Result ReplyAndReceiveLight64(Core::System& system, Handle handle, uint32_t* args);
+
+Result SendSyncRequestLight(Core::System& system, Handle session_handle, uint32_t* args);
+Result SendSyncRequestLight64From32(Core::System& system, Handle session_handle, uint32_t* args);
+Result SendSyncRequestLight64(Core::System& system, Handle session_handle, uint32_t* args);
+
+void CallSecureMonitor(Core::System& system, lp64::SecureMonitorArguments* args);
+void CallSecureMonitor64From32(Core::System& system, ilp32::SecureMonitorArguments* args);
+void CallSecureMonitor64(Core::System& system, lp64::SecureMonitorArguments* args);
+
+// Defined in svc_light_ipc.cpp.
+void SvcWrap_ReplyAndReceiveLight64From32(Core::System& system);
+void SvcWrap_ReplyAndReceiveLight64(Core::System& system);
+
+void SvcWrap_SendSyncRequestLight64From32(Core::System& system);
+void SvcWrap_SendSyncRequestLight64(Core::System& system);
+
+// Defined in svc_secure_monitor_call.cpp.
+void SvcWrap_CallSecureMonitor64From32(Core::System& system);
+void SvcWrap_CallSecureMonitor64(Core::System& system);
+
+// Perform a supervisor call by index.
+void Call(Core::System& system, u32 imm);
} // 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..63bc08555
--- /dev/null
+++ b/src/core/hle/kernel/svc/svc_activity.cpp
@@ -0,0 +1,66 @@
+// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include "core/core.h"
+#include "core/hle/kernel/k_process.h"
+#include "core/hle/kernel/k_thread.h"
+#include "core/hle/kernel/svc.h"
+#include "core/hle/kernel/svc_results.h"
+
+namespace Kernel::Svc {
+
+/// Sets the thread activity
+Result SetThreadActivity(Core::System& system, Handle thread_handle,
+ ThreadActivity thread_activity) {
+ LOG_DEBUG(Kernel_SVC, "called, handle=0x{:08X}, activity=0x{:08X}", thread_handle,
+ thread_activity);
+
+ // Validate the activity.
+ static constexpr auto IsValidThreadActivity = [](ThreadActivity activity) {
+ return activity == ThreadActivity::Runnable || activity == ThreadActivity::Paused;
+ };
+ R_UNLESS(IsValidThreadActivity(thread_activity), ResultInvalidEnumValue);
+
+ // Get the thread from its handle.
+ KScopedAutoObject thread =
+ GetCurrentProcess(system.Kernel()).GetHandleTable().GetObject<KThread>(thread_handle);
+ R_UNLESS(thread.IsNotNull(), ResultInvalidHandle);
+
+ // Check that the activity is being set on a non-current thread for the current process.
+ R_UNLESS(thread->GetOwnerProcess() == GetCurrentProcessPointer(system.Kernel()),
+ ResultInvalidHandle);
+ R_UNLESS(thread.GetPointerUnsafe() != GetCurrentThreadPointer(system.Kernel()), ResultBusy);
+
+ // Set the activity.
+ R_TRY(thread->SetActivity(thread_activity));
+
+ return ResultSuccess;
+}
+
+Result SetProcessActivity(Core::System& system, Handle process_handle,
+ ProcessActivity process_activity) {
+ UNIMPLEMENTED();
+ R_THROW(ResultNotImplemented);
+}
+
+Result SetThreadActivity64(Core::System& system, Handle thread_handle,
+ ThreadActivity thread_activity) {
+ return SetThreadActivity(system, thread_handle, thread_activity);
+}
+
+Result SetProcessActivity64(Core::System& system, Handle process_handle,
+ ProcessActivity process_activity) {
+ return SetProcessActivity(system, process_handle, process_activity);
+}
+
+Result SetThreadActivity64From32(Core::System& system, Handle thread_handle,
+ ThreadActivity thread_activity) {
+ return SetThreadActivity(system, thread_handle, thread_activity);
+}
+
+Result SetProcessActivity64From32(Core::System& system, Handle process_handle,
+ ProcessActivity process_activity) {
+ return SetProcessActivity(system, process_handle, process_activity);
+}
+
+} // 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..998bd3f22
--- /dev/null
+++ b/src/core/hle/kernel/svc/svc_address_arbiter.cpp
@@ -0,0 +1,122 @@
+// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include "core/core.h"
+#include "core/hle/kernel/k_memory_layout.h"
+#include "core/hle/kernel/k_process.h"
+#include "core/hle/kernel/kernel.h"
+#include "core/hle/kernel/svc.h"
+#include "core/hle/kernel/svc_results.h"
+#include "core/hle/kernel/svc_types.h"
+
+namespace Kernel::Svc {
+namespace {
+
+constexpr bool IsValidSignalType(Svc::SignalType type) {
+ switch (type) {
+ case Svc::SignalType::Signal:
+ case Svc::SignalType::SignalAndIncrementIfEqual:
+ case Svc::SignalType::SignalAndModifyByWaitingCountIfEqual:
+ return true;
+ default:
+ return false;
+ }
+}
+
+constexpr bool IsValidArbitrationType(Svc::ArbitrationType type) {
+ switch (type) {
+ case Svc::ArbitrationType::WaitIfLessThan:
+ case Svc::ArbitrationType::DecrementAndWaitIfLessThan:
+ case Svc::ArbitrationType::WaitIfEqual:
+ return true;
+ default:
+ return false;
+ }
+}
+
+} // namespace
+
+// Wait for an address (via Address Arbiter)
+Result WaitForAddress(Core::System& system, VAddr address, ArbitrationType arb_type, s32 value,
+ s64 timeout_ns) {
+ LOG_TRACE(Kernel_SVC, "called, address=0x{:X}, arb_type=0x{:X}, value=0x{:X}, timeout_ns={}",
+ address, arb_type, value, timeout_ns);
+
+ // Validate input.
+ if (IsKernelAddress(address)) {
+ LOG_ERROR(Kernel_SVC, "Attempting to wait on kernel address (address={:08X})", address);
+ return ResultInvalidCurrentMemory;
+ }
+ if (!Common::IsAligned(address, sizeof(s32))) {
+ LOG_ERROR(Kernel_SVC, "Wait address must be 4 byte aligned (address={:08X})", address);
+ return ResultInvalidAddress;
+ }
+ if (!IsValidArbitrationType(arb_type)) {
+ LOG_ERROR(Kernel_SVC, "Invalid arbitration type specified (type={})", arb_type);
+ return ResultInvalidEnumValue;
+ }
+
+ // Convert timeout from nanoseconds to ticks.
+ s64 timeout{};
+ if (timeout_ns > 0) {
+ const s64 offset_tick(timeout_ns);
+ if (offset_tick > 0) {
+ timeout = offset_tick + 2;
+ if (timeout <= 0) {
+ timeout = std::numeric_limits<s64>::max();
+ }
+ } else {
+ timeout = std::numeric_limits<s64>::max();
+ }
+ } else {
+ timeout = timeout_ns;
+ }
+
+ return GetCurrentProcess(system.Kernel()).WaitAddressArbiter(address, arb_type, value, timeout);
+}
+
+// Signals to an address (via Address Arbiter)
+Result SignalToAddress(Core::System& system, VAddr address, SignalType signal_type, s32 value,
+ s32 count) {
+ LOG_TRACE(Kernel_SVC, "called, address=0x{:X}, signal_type=0x{:X}, value=0x{:X}, count=0x{:X}",
+ address, signal_type, value, count);
+
+ // Validate input.
+ if (IsKernelAddress(address)) {
+ LOG_ERROR(Kernel_SVC, "Attempting to signal to a kernel address (address={:08X})", address);
+ return ResultInvalidCurrentMemory;
+ }
+ if (!Common::IsAligned(address, sizeof(s32))) {
+ LOG_ERROR(Kernel_SVC, "Signaled address must be 4 byte aligned (address={:08X})", address);
+ return ResultInvalidAddress;
+ }
+ if (!IsValidSignalType(signal_type)) {
+ LOG_ERROR(Kernel_SVC, "Invalid signal type specified (type={})", signal_type);
+ return ResultInvalidEnumValue;
+ }
+
+ return GetCurrentProcess(system.Kernel())
+ .SignalAddressArbiter(address, signal_type, value, count);
+}
+
+Result WaitForAddress64(Core::System& system, VAddr address, ArbitrationType arb_type, s32 value,
+ s64 timeout_ns) {
+ return WaitForAddress(system, address, arb_type, value, timeout_ns);
+}
+
+Result SignalToAddress64(Core::System& system, VAddr address, SignalType signal_type, s32 value,
+ s32 count) {
+ return SignalToAddress(system, address, signal_type, value, count);
+}
+
+Result WaitForAddress64From32(Core::System& system, u32 address, ArbitrationType arb_type,
+ s32 value, s64 timeout_ns) {
+ return WaitForAddress(system, address, arb_type, value, timeout_ns);
+}
+
+Result SignalToAddress64From32(Core::System& system, u32 address, SignalType signal_type, s32 value,
+ s32 count) {
+ return SignalToAddress(system, address, signal_type, value, count);
+}
+
+} // 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..c25e144cd
--- /dev/null
+++ b/src/core/hle/kernel/svc/svc_address_translation.cpp
@@ -0,0 +1,50 @@
+// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include "core/hle/kernel/svc.h"
+#include "core/hle/kernel/svc_results.h"
+
+namespace Kernel::Svc {
+
+Result QueryPhysicalAddress(Core::System& system, lp64::PhysicalMemoryInfo* out_info,
+ uint64_t address) {
+ UNIMPLEMENTED();
+ R_THROW(ResultNotImplemented);
+}
+
+Result QueryIoMapping(Core::System& system, uintptr_t* out_address, uintptr_t* out_size,
+ uint64_t physical_address, uint64_t size) {
+ UNIMPLEMENTED();
+ R_THROW(ResultNotImplemented);
+}
+
+Result QueryPhysicalAddress64(Core::System& system, lp64::PhysicalMemoryInfo* out_info,
+ uint64_t address) {
+ R_RETURN(QueryPhysicalAddress(system, out_info, address));
+}
+
+Result QueryIoMapping64(Core::System& system, uintptr_t* out_address, uintptr_t* out_size,
+ uint64_t physical_address, uint64_t size) {
+ R_RETURN(QueryIoMapping(system, out_address, out_size, physical_address, size));
+}
+
+Result QueryPhysicalAddress64From32(Core::System& system, ilp32::PhysicalMemoryInfo* out_info,
+ uint32_t address) {
+ lp64::PhysicalMemoryInfo info{};
+ R_TRY(QueryPhysicalAddress(system, std::addressof(info), address));
+
+ *out_info = {
+ .physical_address = info.physical_address,
+ .virtual_address = static_cast<u32>(info.virtual_address),
+ .size = static_cast<u32>(info.size),
+ };
+ R_SUCCEED();
+}
+
+Result QueryIoMapping64From32(Core::System& system, uintptr_t* out_address, uintptr_t* out_size,
+ uint64_t physical_address, uint32_t size) {
+ R_RETURN(QueryIoMapping(system, reinterpret_cast<uintptr_t*>(out_address),
+ reinterpret_cast<uintptr_t*>(out_size), physical_address, size));
+}
+
+} // 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..598b71da5
--- /dev/null
+++ b/src/core/hle/kernel/svc/svc_cache.cpp
@@ -0,0 +1,98 @@
+// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include "core/core.h"
+#include "core/hle/kernel/k_process.h"
+#include "core/hle/kernel/svc.h"
+#include "core/hle/kernel/svc_results.h"
+#include "core/hle/kernel/svc_types.h"
+
+namespace Kernel::Svc {
+
+void FlushEntireDataCache(Core::System& system) {
+ UNIMPLEMENTED();
+}
+
+Result FlushDataCache(Core::System& system, VAddr address, size_t size) {
+ UNIMPLEMENTED();
+ R_THROW(ResultNotImplemented);
+}
+
+Result InvalidateProcessDataCache(Core::System& system, Handle process_handle, uint64_t address,
+ uint64_t size) {
+ UNIMPLEMENTED();
+ R_THROW(ResultNotImplemented);
+}
+
+Result StoreProcessDataCache(Core::System& system, Handle process_handle, uint64_t address,
+ uint64_t size) {
+ UNIMPLEMENTED();
+ R_THROW(ResultNotImplemented);
+}
+
+Result FlushProcessDataCache(Core::System& system, Handle process_handle, u64 address, u64 size) {
+ // Validate address/size.
+ R_UNLESS(size > 0, ResultInvalidSize);
+ R_UNLESS(address == static_cast<uintptr_t>(address), ResultInvalidCurrentMemory);
+ R_UNLESS(size == static_cast<size_t>(size), ResultInvalidCurrentMemory);
+
+ // Get the process from its handle.
+ KScopedAutoObject process =
+ GetCurrentProcess(system.Kernel()).GetHandleTable().GetObject<KProcess>(process_handle);
+ R_UNLESS(process.IsNotNull(), ResultInvalidHandle);
+
+ // Verify the region is within range.
+ auto& page_table = process->PageTable();
+ R_UNLESS(page_table.Contains(address, size), ResultInvalidCurrentMemory);
+
+ // Perform the operation.
+ R_RETURN(system.Memory().FlushDataCache(*process, address, size));
+}
+
+void FlushEntireDataCache64(Core::System& system) {
+ FlushEntireDataCache(system);
+}
+
+Result FlushDataCache64(Core::System& system, VAddr address, size_t size) {
+ R_RETURN(FlushDataCache(system, address, size));
+}
+
+Result InvalidateProcessDataCache64(Core::System& system, Handle process_handle, uint64_t address,
+ uint64_t size) {
+ R_RETURN(InvalidateProcessDataCache(system, process_handle, address, size));
+}
+
+Result StoreProcessDataCache64(Core::System& system, Handle process_handle, uint64_t address,
+ uint64_t size) {
+ R_RETURN(StoreProcessDataCache(system, process_handle, address, size));
+}
+
+Result FlushProcessDataCache64(Core::System& system, Handle process_handle, uint64_t address,
+ uint64_t size) {
+ R_RETURN(FlushProcessDataCache(system, process_handle, address, size));
+}
+
+void FlushEntireDataCache64From32(Core::System& system) {
+ return FlushEntireDataCache(system);
+}
+
+Result FlushDataCache64From32(Core::System& system, uint32_t address, uint32_t size) {
+ R_RETURN(FlushDataCache(system, address, size));
+}
+
+Result InvalidateProcessDataCache64From32(Core::System& system, Handle process_handle,
+ uint64_t address, uint64_t size) {
+ R_RETURN(InvalidateProcessDataCache(system, process_handle, address, size));
+}
+
+Result StoreProcessDataCache64From32(Core::System& system, Handle process_handle, uint64_t address,
+ uint64_t size) {
+ R_RETURN(StoreProcessDataCache(system, process_handle, address, size));
+}
+
+Result FlushProcessDataCache64From32(Core::System& system, Handle process_handle, uint64_t address,
+ uint64_t size) {
+ R_RETURN(FlushProcessDataCache(system, process_handle, address, size));
+}
+
+} // 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..538ff1c71
--- /dev/null
+++ b/src/core/hle/kernel/svc/svc_code_memory.cpp
@@ -0,0 +1,171 @@
+// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include "core/core.h"
+#include "core/hle/kernel/k_code_memory.h"
+#include "core/hle/kernel/k_process.h"
+#include "core/hle/kernel/kernel.h"
+#include "core/hle/kernel/svc.h"
+
+namespace Kernel::Svc {
+namespace {
+
+constexpr bool IsValidMapCodeMemoryPermission(MemoryPermission perm) {
+ return perm == MemoryPermission::ReadWrite;
+}
+
+constexpr bool IsValidMapToOwnerCodeMemoryPermission(MemoryPermission perm) {
+ return perm == MemoryPermission::Read || perm == MemoryPermission::ReadExecute;
+}
+
+constexpr bool IsValidUnmapCodeMemoryPermission(MemoryPermission perm) {
+ return perm == MemoryPermission::None;
+}
+
+constexpr bool IsValidUnmapFromOwnerCodeMemoryPermission(MemoryPermission perm) {
+ return perm == MemoryPermission::None;
+}
+
+} // namespace
+
+Result CreateCodeMemory(Core::System& system, Handle* out, VAddr address, size_t size) {
+ LOG_TRACE(Kernel_SVC, "called, address=0x{:X}, size=0x{:X}", address, size);
+
+ // Get kernel instance.
+ auto& kernel = system.Kernel();
+
+ // Validate address / size.
+ R_UNLESS(Common::IsAligned(address, PageSize), ResultInvalidAddress);
+ R_UNLESS(Common::IsAligned(size, PageSize), ResultInvalidSize);
+ R_UNLESS(size > 0, ResultInvalidSize);
+ R_UNLESS((address < address + size), ResultInvalidCurrentMemory);
+
+ // Create the code memory.
+
+ KCodeMemory* code_mem = KCodeMemory::Create(kernel);
+ R_UNLESS(code_mem != nullptr, ResultOutOfResource);
+
+ // Verify that the region is in range.
+ R_UNLESS(GetCurrentProcess(system.Kernel()).PageTable().Contains(address, size),
+ ResultInvalidCurrentMemory);
+
+ // Initialize the code memory.
+ R_TRY(code_mem->Initialize(system.DeviceMemory(), address, size));
+
+ // Register the code memory.
+ KCodeMemory::Register(kernel, code_mem);
+
+ // Add the code memory to the handle table.
+ R_TRY(GetCurrentProcess(system.Kernel()).GetHandleTable().Add(out, code_mem));
+
+ code_mem->Close();
+
+ return ResultSuccess;
+}
+
+Result ControlCodeMemory(Core::System& system, Handle code_memory_handle,
+ CodeMemoryOperation operation, VAddr address, size_t size,
+ MemoryPermission perm) {
+
+ LOG_TRACE(Kernel_SVC,
+ "called, code_memory_handle=0x{:X}, operation=0x{:X}, address=0x{:X}, size=0x{:X}, "
+ "permission=0x{:X}",
+ code_memory_handle, operation, address, size, perm);
+
+ // Validate the address / size.
+ R_UNLESS(Common::IsAligned(address, PageSize), ResultInvalidAddress);
+ R_UNLESS(Common::IsAligned(size, PageSize), ResultInvalidSize);
+ R_UNLESS(size > 0, ResultInvalidSize);
+ R_UNLESS((address < address + size), ResultInvalidCurrentMemory);
+
+ // Get the code memory from its handle.
+ KScopedAutoObject code_mem = GetCurrentProcess(system.Kernel())
+ .GetHandleTable()
+ .GetObject<KCodeMemory>(code_memory_handle);
+ R_UNLESS(code_mem.IsNotNull(), ResultInvalidHandle);
+
+ // NOTE: Here, Atmosphere extends the SVC to allow code memory operations on one's own process.
+ // This enables homebrew usage of these SVCs for JIT.
+
+ // Perform the operation.
+ switch (operation) {
+ case CodeMemoryOperation::Map: {
+ // Check that the region is in range.
+ R_UNLESS(GetCurrentProcess(system.Kernel())
+ .PageTable()
+ .CanContain(address, size, KMemoryState::CodeOut),
+ ResultInvalidMemoryRegion);
+
+ // Check the memory permission.
+ R_UNLESS(IsValidMapCodeMemoryPermission(perm), ResultInvalidNewMemoryPermission);
+
+ // Map the memory.
+ R_TRY(code_mem->Map(address, size));
+ } break;
+ case CodeMemoryOperation::Unmap: {
+ // Check that the region is in range.
+ R_UNLESS(GetCurrentProcess(system.Kernel())
+ .PageTable()
+ .CanContain(address, size, KMemoryState::CodeOut),
+ ResultInvalidMemoryRegion);
+
+ // Check the memory permission.
+ R_UNLESS(IsValidUnmapCodeMemoryPermission(perm), ResultInvalidNewMemoryPermission);
+
+ // Unmap the memory.
+ R_TRY(code_mem->Unmap(address, size));
+ } break;
+ case CodeMemoryOperation::MapToOwner: {
+ // Check that the region is in range.
+ R_UNLESS(code_mem->GetOwner()->PageTable().CanContain(address, size,
+ KMemoryState::GeneratedCode),
+ ResultInvalidMemoryRegion);
+
+ // Check the memory permission.
+ R_UNLESS(IsValidMapToOwnerCodeMemoryPermission(perm), ResultInvalidNewMemoryPermission);
+
+ // Map the memory to its owner.
+ R_TRY(code_mem->MapToOwner(address, size, perm));
+ } break;
+ case CodeMemoryOperation::UnmapFromOwner: {
+ // Check that the region is in range.
+ R_UNLESS(code_mem->GetOwner()->PageTable().CanContain(address, size,
+ KMemoryState::GeneratedCode),
+ ResultInvalidMemoryRegion);
+
+ // Check the memory permission.
+ R_UNLESS(IsValidUnmapFromOwnerCodeMemoryPermission(perm), ResultInvalidNewMemoryPermission);
+
+ // Unmap the memory from its owner.
+ R_TRY(code_mem->UnmapFromOwner(address, size));
+ } break;
+ default:
+ return ResultInvalidEnumValue;
+ }
+
+ return ResultSuccess;
+}
+
+Result CreateCodeMemory64(Core::System& system, Handle* out_handle, uint64_t address,
+ uint64_t size) {
+ R_RETURN(CreateCodeMemory(system, out_handle, address, size));
+}
+
+Result ControlCodeMemory64(Core::System& system, Handle code_memory_handle,
+ CodeMemoryOperation operation, uint64_t address, uint64_t size,
+ MemoryPermission perm) {
+ R_RETURN(ControlCodeMemory(system, code_memory_handle, operation, address, size, perm));
+}
+
+Result CreateCodeMemory64From32(Core::System& system, Handle* out_handle, uint32_t address,
+ uint32_t size) {
+ R_RETURN(CreateCodeMemory(system, out_handle, address, size));
+}
+
+Result ControlCodeMemory64From32(Core::System& system, Handle code_memory_handle,
+ CodeMemoryOperation operation, uint64_t address, uint64_t size,
+ MemoryPermission perm) {
+ R_RETURN(ControlCodeMemory(system, code_memory_handle, operation, address, size, perm));
+}
+
+} // 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..8ad1a0b8f
--- /dev/null
+++ b/src/core/hle/kernel/svc/svc_condition_variable.cpp
@@ -0,0 +1,77 @@
+// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include "core/core.h"
+#include "core/hle/kernel/k_memory_layout.h"
+#include "core/hle/kernel/k_process.h"
+#include "core/hle/kernel/kernel.h"
+#include "core/hle/kernel/svc.h"
+#include "core/hle/kernel/svc_results.h"
+
+namespace Kernel::Svc {
+
+/// Wait process wide key atomic
+Result WaitProcessWideKeyAtomic(Core::System& system, VAddr address, VAddr cv_key, u32 tag,
+ s64 timeout_ns) {
+ LOG_TRACE(Kernel_SVC, "called address={:X}, cv_key={:X}, tag=0x{:08X}, timeout_ns={}", address,
+ cv_key, tag, timeout_ns);
+
+ // Validate input.
+ if (IsKernelAddress(address)) {
+ LOG_ERROR(Kernel_SVC, "Attempted to wait on kernel address (address={:08X})", address);
+ return ResultInvalidCurrentMemory;
+ }
+ if (!Common::IsAligned(address, sizeof(s32))) {
+ LOG_ERROR(Kernel_SVC, "Address must be 4 byte aligned (address={:08X})", address);
+ return ResultInvalidAddress;
+ }
+
+ // Convert timeout from nanoseconds to ticks.
+ s64 timeout{};
+ if (timeout_ns > 0) {
+ const s64 offset_tick(timeout_ns);
+ if (offset_tick > 0) {
+ timeout = offset_tick + 2;
+ if (timeout <= 0) {
+ timeout = std::numeric_limits<s64>::max();
+ }
+ } else {
+ timeout = std::numeric_limits<s64>::max();
+ }
+ } else {
+ timeout = timeout_ns;
+ }
+
+ // Wait on the condition variable.
+ return GetCurrentProcess(system.Kernel())
+ .WaitConditionVariable(address, Common::AlignDown(cv_key, sizeof(u32)), tag, timeout);
+}
+
+/// Signal process wide key
+void SignalProcessWideKey(Core::System& system, VAddr cv_key, s32 count) {
+ LOG_TRACE(Kernel_SVC, "called, cv_key=0x{:X}, count=0x{:08X}", cv_key, count);
+
+ // Signal the condition variable.
+ return GetCurrentProcess(system.Kernel())
+ .SignalConditionVariable(Common::AlignDown(cv_key, sizeof(u32)), count);
+}
+
+Result WaitProcessWideKeyAtomic64(Core::System& system, uint64_t address, uint64_t cv_key,
+ uint32_t tag, int64_t timeout_ns) {
+ R_RETURN(WaitProcessWideKeyAtomic(system, address, cv_key, tag, timeout_ns));
+}
+
+void SignalProcessWideKey64(Core::System& system, uint64_t cv_key, int32_t count) {
+ SignalProcessWideKey(system, cv_key, count);
+}
+
+Result WaitProcessWideKeyAtomic64From32(Core::System& system, uint32_t address, uint32_t cv_key,
+ uint32_t tag, int64_t timeout_ns) {
+ R_RETURN(WaitProcessWideKeyAtomic(system, address, cv_key, tag, timeout_ns));
+}
+
+void SignalProcessWideKey64From32(Core::System& system, uint32_t cv_key, int32_t count) {
+ SignalProcessWideKey(system, cv_key, count);
+}
+
+} // 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..a14050fa7
--- /dev/null
+++ b/src/core/hle/kernel/svc/svc_debug.cpp
@@ -0,0 +1,194 @@
+// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include "core/hle/kernel/svc.h"
+#include "core/hle/kernel/svc_results.h"
+
+namespace Kernel::Svc {
+
+Result DebugActiveProcess(Core::System& system, Handle* out_handle, uint64_t process_id) {
+ UNIMPLEMENTED();
+ R_THROW(ResultNotImplemented);
+}
+
+Result BreakDebugProcess(Core::System& system, Handle debug_handle) {
+ UNIMPLEMENTED();
+ R_THROW(ResultNotImplemented);
+}
+
+Result TerminateDebugProcess(Core::System& system, Handle debug_handle) {
+ UNIMPLEMENTED();
+ R_THROW(ResultNotImplemented);
+}
+
+Result GetDebugEvent(Core::System& system, uint64_t out_info, Handle debug_handle) {
+ UNIMPLEMENTED();
+ R_THROW(ResultNotImplemented);
+}
+
+Result ContinueDebugEvent(Core::System& system, Handle debug_handle, uint32_t flags,
+ uint64_t user_thread_ids, int32_t num_thread_ids) {
+ UNIMPLEMENTED();
+ R_THROW(ResultNotImplemented);
+}
+
+Result GetDebugThreadContext(Core::System& system, uint64_t out_context, Handle debug_handle,
+ uint64_t thread_id, uint32_t context_flags) {
+ UNIMPLEMENTED();
+ R_THROW(ResultNotImplemented);
+}
+
+Result SetDebugThreadContext(Core::System& system, Handle debug_handle, uint64_t thread_id,
+ uint64_t user_context, uint32_t context_flags) {
+ UNIMPLEMENTED();
+ R_THROW(ResultNotImplemented);
+}
+
+Result QueryDebugProcessMemory(Core::System& system, uint64_t out_memory_info,
+ PageInfo* out_page_info, Handle debug_handle, uintptr_t address) {
+ UNIMPLEMENTED();
+ R_THROW(ResultNotImplemented);
+}
+
+Result ReadDebugProcessMemory(Core::System& system, uintptr_t buffer, Handle debug_handle,
+ uintptr_t address, size_t size) {
+ UNIMPLEMENTED();
+ R_THROW(ResultNotImplemented);
+}
+
+Result WriteDebugProcessMemory(Core::System& system, Handle debug_handle, uintptr_t buffer,
+ uintptr_t address, size_t size) {
+ UNIMPLEMENTED();
+ R_THROW(ResultNotImplemented);
+}
+
+Result SetHardwareBreakPoint(Core::System& system, HardwareBreakPointRegisterName name,
+ uint64_t flags, uint64_t value) {
+ UNIMPLEMENTED();
+ R_THROW(ResultNotImplemented);
+}
+
+Result GetDebugThreadParam(Core::System& system, uint64_t* out_64, uint32_t* out_32,
+ Handle debug_handle, uint64_t thread_id, DebugThreadParam param) {
+ UNIMPLEMENTED();
+ R_THROW(ResultNotImplemented);
+}
+
+Result DebugActiveProcess64(Core::System& system, Handle* out_handle, uint64_t process_id) {
+ R_RETURN(DebugActiveProcess(system, out_handle, process_id));
+}
+
+Result BreakDebugProcess64(Core::System& system, Handle debug_handle) {
+ R_RETURN(BreakDebugProcess(system, debug_handle));
+}
+
+Result TerminateDebugProcess64(Core::System& system, Handle debug_handle) {
+ R_RETURN(TerminateDebugProcess(system, debug_handle));
+}
+
+Result GetDebugEvent64(Core::System& system, uint64_t out_info, Handle debug_handle) {
+ R_RETURN(GetDebugEvent(system, out_info, debug_handle));
+}
+
+Result ContinueDebugEvent64(Core::System& system, Handle debug_handle, uint32_t flags,
+ uint64_t thread_ids, int32_t num_thread_ids) {
+ R_RETURN(ContinueDebugEvent(system, debug_handle, flags, thread_ids, num_thread_ids));
+}
+
+Result GetDebugThreadContext64(Core::System& system, uint64_t out_context, Handle debug_handle,
+ uint64_t thread_id, uint32_t context_flags) {
+ R_RETURN(GetDebugThreadContext(system, out_context, debug_handle, thread_id, context_flags));
+}
+
+Result SetDebugThreadContext64(Core::System& system, Handle debug_handle, uint64_t thread_id,
+ uint64_t context, uint32_t context_flags) {
+ R_RETURN(SetDebugThreadContext(system, debug_handle, thread_id, context, context_flags));
+}
+
+Result QueryDebugProcessMemory64(Core::System& system, uint64_t out_memory_info,
+ PageInfo* out_page_info, Handle debug_handle, uint64_t address) {
+ R_RETURN(
+ QueryDebugProcessMemory(system, out_memory_info, out_page_info, debug_handle, address));
+}
+
+Result ReadDebugProcessMemory64(Core::System& system, uint64_t buffer, Handle debug_handle,
+ uint64_t address, uint64_t size) {
+ R_RETURN(ReadDebugProcessMemory(system, buffer, debug_handle, address, size));
+}
+
+Result WriteDebugProcessMemory64(Core::System& system, Handle debug_handle, uint64_t buffer,
+ uint64_t address, uint64_t size) {
+ R_RETURN(WriteDebugProcessMemory(system, debug_handle, buffer, address, size));
+}
+
+Result SetHardwareBreakPoint64(Core::System& system, HardwareBreakPointRegisterName name,
+ uint64_t flags, uint64_t value) {
+ R_RETURN(SetHardwareBreakPoint(system, name, flags, value));
+}
+
+Result GetDebugThreadParam64(Core::System& system, uint64_t* out_64, uint32_t* out_32,
+ Handle debug_handle, uint64_t thread_id, DebugThreadParam param) {
+ R_RETURN(GetDebugThreadParam(system, out_64, out_32, debug_handle, thread_id, param));
+}
+
+Result DebugActiveProcess64From32(Core::System& system, Handle* out_handle, uint64_t process_id) {
+ R_RETURN(DebugActiveProcess(system, out_handle, process_id));
+}
+
+Result BreakDebugProcess64From32(Core::System& system, Handle debug_handle) {
+ R_RETURN(BreakDebugProcess(system, debug_handle));
+}
+
+Result TerminateDebugProcess64From32(Core::System& system, Handle debug_handle) {
+ R_RETURN(TerminateDebugProcess(system, debug_handle));
+}
+
+Result GetDebugEvent64From32(Core::System& system, uint32_t out_info, Handle debug_handle) {
+ R_RETURN(GetDebugEvent(system, out_info, debug_handle));
+}
+
+Result ContinueDebugEvent64From32(Core::System& system, Handle debug_handle, uint32_t flags,
+ uint32_t thread_ids, int32_t num_thread_ids) {
+ R_RETURN(ContinueDebugEvent(system, debug_handle, flags, thread_ids, num_thread_ids));
+}
+
+Result GetDebugThreadContext64From32(Core::System& system, uint32_t out_context,
+ Handle debug_handle, uint64_t thread_id,
+ uint32_t context_flags) {
+ R_RETURN(GetDebugThreadContext(system, out_context, debug_handle, thread_id, context_flags));
+}
+
+Result SetDebugThreadContext64From32(Core::System& system, Handle debug_handle, uint64_t thread_id,
+ uint32_t context, uint32_t context_flags) {
+ R_RETURN(SetDebugThreadContext(system, debug_handle, thread_id, context, context_flags));
+}
+
+Result QueryDebugProcessMemory64From32(Core::System& system, uint32_t out_memory_info,
+ PageInfo* out_page_info, Handle debug_handle,
+ uint32_t address) {
+ R_RETURN(
+ QueryDebugProcessMemory(system, out_memory_info, out_page_info, debug_handle, address));
+}
+
+Result ReadDebugProcessMemory64From32(Core::System& system, uint32_t buffer, Handle debug_handle,
+ uint32_t address, uint32_t size) {
+ R_RETURN(ReadDebugProcessMemory(system, buffer, debug_handle, address, size));
+}
+
+Result WriteDebugProcessMemory64From32(Core::System& system, Handle debug_handle, uint32_t buffer,
+ uint32_t address, uint32_t size) {
+ R_RETURN(WriteDebugProcessMemory(system, debug_handle, buffer, address, size));
+}
+
+Result SetHardwareBreakPoint64From32(Core::System& system, HardwareBreakPointRegisterName name,
+ uint64_t flags, uint64_t value) {
+ R_RETURN(SetHardwareBreakPoint(system, name, flags, value));
+}
+
+Result GetDebugThreadParam64From32(Core::System& system, uint64_t* out_64, uint32_t* out_32,
+ Handle debug_handle, uint64_t thread_id,
+ DebugThreadParam param) {
+ R_RETURN(GetDebugThreadParam(system, out_64, out_32, debug_handle, thread_id, param));
+}
+
+} // 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..d4bf062d1
--- /dev/null
+++ b/src/core/hle/kernel/svc/svc_debug_string.cpp
@@ -0,0 +1,29 @@
+// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include "core/core.h"
+#include "core/hle/kernel/svc.h"
+#include "core/memory.h"
+
+namespace Kernel::Svc {
+
+/// Used to output a message on a debug hardware unit - does nothing on a retail unit
+Result OutputDebugString(Core::System& system, VAddr address, u64 len) {
+ R_SUCCEED_IF(len == 0);
+
+ std::string str(len, '\0');
+ system.Memory().ReadBlock(address, str.data(), str.size());
+ LOG_DEBUG(Debug_Emulated, "{}", str);
+
+ R_SUCCEED();
+}
+
+Result OutputDebugString64(Core::System& system, uint64_t debug_str, uint64_t len) {
+ R_RETURN(OutputDebugString(system, debug_str, len));
+}
+
+Result OutputDebugString64From32(Core::System& system, uint32_t debug_str, uint32_t len) {
+ R_RETURN(OutputDebugString(system, debug_str, len));
+}
+
+} // 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..f68c0e6a9
--- /dev/null
+++ b/src/core/hle/kernel/svc/svc_device_address_space.cpp
@@ -0,0 +1,258 @@
+// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include "common/alignment.h"
+#include "common/scope_exit.h"
+#include "core/core.h"
+#include "core/hle/kernel/k_device_address_space.h"
+#include "core/hle/kernel/k_process.h"
+#include "core/hle/kernel/svc.h"
+
+namespace Kernel::Svc {
+
+constexpr inline u64 DeviceAddressSpaceAlignMask = (1ULL << 22) - 1;
+
+constexpr bool IsProcessAndDeviceAligned(uint64_t process_address, uint64_t device_address) {
+ return (process_address & DeviceAddressSpaceAlignMask) ==
+ (device_address & DeviceAddressSpaceAlignMask);
+}
+
+Result CreateDeviceAddressSpace(Core::System& system, Handle* out, uint64_t das_address,
+ uint64_t das_size) {
+ // Validate input.
+ R_UNLESS(Common::IsAligned(das_address, PageSize), ResultInvalidMemoryRegion);
+ R_UNLESS(Common::IsAligned(das_size, PageSize), ResultInvalidMemoryRegion);
+ R_UNLESS(das_size > 0, ResultInvalidMemoryRegion);
+ R_UNLESS((das_address < das_address + das_size), ResultInvalidMemoryRegion);
+
+ // Create the device address space.
+ KDeviceAddressSpace* das = KDeviceAddressSpace::Create(system.Kernel());
+ R_UNLESS(das != nullptr, ResultOutOfResource);
+ SCOPE_EXIT({ das->Close(); });
+
+ // Initialize the device address space.
+ R_TRY(das->Initialize(das_address, das_size));
+
+ // Register the device address space.
+ KDeviceAddressSpace::Register(system.Kernel(), das);
+
+ // Add to the handle table.
+ R_TRY(GetCurrentProcess(system.Kernel()).GetHandleTable().Add(out, das));
+
+ R_SUCCEED();
+}
+
+Result AttachDeviceAddressSpace(Core::System& system, DeviceName device_name, Handle das_handle) {
+ // Get the device address space.
+ KScopedAutoObject das = GetCurrentProcess(system.Kernel())
+ .GetHandleTable()
+ .GetObject<KDeviceAddressSpace>(das_handle);
+ R_UNLESS(das.IsNotNull(), ResultInvalidHandle);
+
+ // Attach.
+ R_RETURN(das->Attach(device_name));
+}
+
+Result DetachDeviceAddressSpace(Core::System& system, DeviceName device_name, Handle das_handle) {
+ // Get the device address space.
+ KScopedAutoObject das = GetCurrentProcess(system.Kernel())
+ .GetHandleTable()
+ .GetObject<KDeviceAddressSpace>(das_handle);
+ R_UNLESS(das.IsNotNull(), ResultInvalidHandle);
+
+ // Detach.
+ R_RETURN(das->Detach(device_name));
+}
+
+constexpr bool IsValidDeviceMemoryPermission(MemoryPermission device_perm) {
+ switch (device_perm) {
+ case MemoryPermission::Read:
+ case MemoryPermission::Write:
+ case MemoryPermission::ReadWrite:
+ return true;
+ default:
+ return false;
+ }
+}
+
+Result MapDeviceAddressSpaceByForce(Core::System& system, Handle das_handle, Handle process_handle,
+ uint64_t process_address, size_t size, uint64_t device_address,
+ u32 option) {
+ // Decode the option.
+ const MapDeviceAddressSpaceOption option_pack{option};
+ const auto device_perm = option_pack.permission;
+ const auto reserved = option_pack.reserved;
+
+ // Validate input.
+ R_UNLESS(Common::IsAligned(process_address, PageSize), ResultInvalidAddress);
+ R_UNLESS(Common::IsAligned(device_address, PageSize), ResultInvalidAddress);
+ R_UNLESS(Common::IsAligned(size, PageSize), ResultInvalidSize);
+ R_UNLESS(size > 0, ResultInvalidSize);
+ R_UNLESS((process_address < process_address + size), ResultInvalidCurrentMemory);
+ R_UNLESS((device_address < device_address + size), ResultInvalidMemoryRegion);
+ R_UNLESS((process_address == static_cast<uintptr_t>(process_address)),
+ ResultInvalidCurrentMemory);
+ R_UNLESS(IsValidDeviceMemoryPermission(device_perm), ResultInvalidNewMemoryPermission);
+ R_UNLESS(reserved == 0, ResultInvalidEnumValue);
+
+ // Get the device address space.
+ KScopedAutoObject das = GetCurrentProcess(system.Kernel())
+ .GetHandleTable()
+ .GetObject<KDeviceAddressSpace>(das_handle);
+ R_UNLESS(das.IsNotNull(), ResultInvalidHandle);
+
+ // Get the process.
+ KScopedAutoObject process =
+ GetCurrentProcess(system.Kernel()).GetHandleTable().GetObject<KProcess>(process_handle);
+ R_UNLESS(process.IsNotNull(), ResultInvalidHandle);
+
+ // Validate that the process address is within range.
+ auto& page_table = process->PageTable();
+ R_UNLESS(page_table.Contains(process_address, size), ResultInvalidCurrentMemory);
+
+ // Map.
+ R_RETURN(
+ das->MapByForce(std::addressof(page_table), process_address, size, device_address, option));
+}
+
+Result MapDeviceAddressSpaceAligned(Core::System& system, Handle das_handle, Handle process_handle,
+ uint64_t process_address, size_t size, uint64_t device_address,
+ u32 option) {
+ // Decode the option.
+ const MapDeviceAddressSpaceOption option_pack{option};
+ const auto device_perm = option_pack.permission;
+ const auto reserved = option_pack.reserved;
+
+ // Validate input.
+ R_UNLESS(Common::IsAligned(process_address, PageSize), ResultInvalidAddress);
+ R_UNLESS(Common::IsAligned(device_address, PageSize), ResultInvalidAddress);
+ R_UNLESS(IsProcessAndDeviceAligned(process_address, device_address), ResultInvalidAddress);
+ R_UNLESS(Common::IsAligned(size, PageSize), ResultInvalidSize);
+ R_UNLESS(size > 0, ResultInvalidSize);
+ R_UNLESS((process_address < process_address + size), ResultInvalidCurrentMemory);
+ R_UNLESS((device_address < device_address + size), ResultInvalidMemoryRegion);
+ R_UNLESS((process_address == static_cast<uintptr_t>(process_address)),
+ ResultInvalidCurrentMemory);
+ R_UNLESS(IsValidDeviceMemoryPermission(device_perm), ResultInvalidNewMemoryPermission);
+ R_UNLESS(reserved == 0, ResultInvalidEnumValue);
+
+ // Get the device address space.
+ KScopedAutoObject das = GetCurrentProcess(system.Kernel())
+ .GetHandleTable()
+ .GetObject<KDeviceAddressSpace>(das_handle);
+ R_UNLESS(das.IsNotNull(), ResultInvalidHandle);
+
+ // Get the process.
+ KScopedAutoObject process =
+ GetCurrentProcess(system.Kernel()).GetHandleTable().GetObject<KProcess>(process_handle);
+ R_UNLESS(process.IsNotNull(), ResultInvalidHandle);
+
+ // Validate that the process address is within range.
+ auto& page_table = process->PageTable();
+ R_UNLESS(page_table.Contains(process_address, size), ResultInvalidCurrentMemory);
+
+ // Map.
+ R_RETURN(
+ das->MapAligned(std::addressof(page_table), process_address, size, device_address, option));
+}
+
+Result UnmapDeviceAddressSpace(Core::System& system, Handle das_handle, Handle process_handle,
+ uint64_t process_address, size_t size, uint64_t device_address) {
+ // Validate input.
+ R_UNLESS(Common::IsAligned(process_address, PageSize), ResultInvalidAddress);
+ R_UNLESS(Common::IsAligned(device_address, PageSize), ResultInvalidAddress);
+ R_UNLESS(Common::IsAligned(size, PageSize), ResultInvalidSize);
+ R_UNLESS(size > 0, ResultInvalidSize);
+ R_UNLESS((process_address < process_address + size), ResultInvalidCurrentMemory);
+ R_UNLESS((device_address < device_address + size), ResultInvalidMemoryRegion);
+ R_UNLESS((process_address == static_cast<uintptr_t>(process_address)),
+ ResultInvalidCurrentMemory);
+
+ // Get the device address space.
+ KScopedAutoObject das = GetCurrentProcess(system.Kernel())
+ .GetHandleTable()
+ .GetObject<KDeviceAddressSpace>(das_handle);
+ R_UNLESS(das.IsNotNull(), ResultInvalidHandle);
+
+ // Get the process.
+ KScopedAutoObject process =
+ GetCurrentProcess(system.Kernel()).GetHandleTable().GetObject<KProcess>(process_handle);
+ R_UNLESS(process.IsNotNull(), ResultInvalidHandle);
+
+ // Validate that the process address is within range.
+ auto& page_table = process->PageTable();
+ R_UNLESS(page_table.Contains(process_address, size), ResultInvalidCurrentMemory);
+
+ R_RETURN(das->Unmap(std::addressof(page_table), process_address, size, device_address));
+}
+
+Result CreateDeviceAddressSpace64(Core::System& system, Handle* out_handle, uint64_t das_address,
+ uint64_t das_size) {
+ R_RETURN(CreateDeviceAddressSpace(system, out_handle, das_address, das_size));
+}
+
+Result AttachDeviceAddressSpace64(Core::System& system, DeviceName device_name, Handle das_handle) {
+ R_RETURN(AttachDeviceAddressSpace(system, device_name, das_handle));
+}
+
+Result DetachDeviceAddressSpace64(Core::System& system, DeviceName device_name, Handle das_handle) {
+ R_RETURN(DetachDeviceAddressSpace(system, device_name, das_handle));
+}
+
+Result MapDeviceAddressSpaceByForce64(Core::System& system, Handle das_handle,
+ Handle process_handle, uint64_t process_address,
+ uint64_t size, uint64_t device_address, u32 option) {
+ R_RETURN(MapDeviceAddressSpaceByForce(system, das_handle, process_handle, process_address, size,
+ device_address, option));
+}
+
+Result MapDeviceAddressSpaceAligned64(Core::System& system, Handle das_handle,
+ Handle process_handle, uint64_t process_address,
+ uint64_t size, uint64_t device_address, u32 option) {
+ R_RETURN(MapDeviceAddressSpaceAligned(system, das_handle, process_handle, process_address, size,
+ device_address, option));
+}
+
+Result UnmapDeviceAddressSpace64(Core::System& system, Handle das_handle, Handle process_handle,
+ uint64_t process_address, uint64_t size, uint64_t device_address) {
+ R_RETURN(UnmapDeviceAddressSpace(system, das_handle, process_handle, process_address, size,
+ device_address));
+}
+
+Result CreateDeviceAddressSpace64From32(Core::System& system, Handle* out_handle,
+ uint64_t das_address, uint64_t das_size) {
+ R_RETURN(CreateDeviceAddressSpace(system, out_handle, das_address, das_size));
+}
+
+Result AttachDeviceAddressSpace64From32(Core::System& system, DeviceName device_name,
+ Handle das_handle) {
+ R_RETURN(AttachDeviceAddressSpace(system, device_name, das_handle));
+}
+
+Result DetachDeviceAddressSpace64From32(Core::System& system, DeviceName device_name,
+ Handle das_handle) {
+ R_RETURN(DetachDeviceAddressSpace(system, device_name, das_handle));
+}
+
+Result MapDeviceAddressSpaceByForce64From32(Core::System& system, Handle das_handle,
+ Handle process_handle, uint64_t process_address,
+ uint32_t size, uint64_t device_address, u32 option) {
+ R_RETURN(MapDeviceAddressSpaceByForce(system, das_handle, process_handle, process_address, size,
+ device_address, option));
+}
+
+Result MapDeviceAddressSpaceAligned64From32(Core::System& system, Handle das_handle,
+ Handle process_handle, uint64_t process_address,
+ uint32_t size, uint64_t device_address, u32 option) {
+ R_RETURN(MapDeviceAddressSpaceAligned(system, das_handle, process_handle, process_address, size,
+ device_address, option));
+}
+
+Result UnmapDeviceAddressSpace64From32(Core::System& system, Handle das_handle,
+ Handle process_handle, uint64_t process_address,
+ uint32_t size, uint64_t device_address) {
+ R_RETURN(UnmapDeviceAddressSpace(system, das_handle, process_handle, process_address, size,
+ device_address));
+}
+
+} // 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..a948493e8
--- /dev/null
+++ b/src/core/hle/kernel/svc/svc_event.cpp
@@ -0,0 +1,124 @@
+// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include "common/scope_exit.h"
+#include "core/core.h"
+#include "core/hle/kernel/k_event.h"
+#include "core/hle/kernel/k_process.h"
+#include "core/hle/kernel/k_scoped_resource_reservation.h"
+#include "core/hle/kernel/kernel.h"
+#include "core/hle/kernel/svc.h"
+
+namespace Kernel::Svc {
+
+Result SignalEvent(Core::System& system, Handle event_handle) {
+ LOG_DEBUG(Kernel_SVC, "called, event_handle=0x{:08X}", event_handle);
+
+ // Get the current handle table.
+ const KHandleTable& handle_table = GetCurrentProcess(system.Kernel()).GetHandleTable();
+
+ // Get the event.
+ KScopedAutoObject event = handle_table.GetObject<KEvent>(event_handle);
+ R_UNLESS(event.IsNotNull(), ResultInvalidHandle);
+
+ return event->Signal();
+}
+
+Result ClearEvent(Core::System& system, Handle event_handle) {
+ LOG_TRACE(Kernel_SVC, "called, event_handle=0x{:08X}", event_handle);
+
+ // Get the current handle table.
+ const auto& handle_table = GetCurrentProcess(system.Kernel()).GetHandleTable();
+
+ // Try to clear the writable event.
+ {
+ KScopedAutoObject event = handle_table.GetObject<KEvent>(event_handle);
+ if (event.IsNotNull()) {
+ return event->Clear();
+ }
+ }
+
+ // Try to clear the readable event.
+ {
+ KScopedAutoObject readable_event = handle_table.GetObject<KReadableEvent>(event_handle);
+ if (readable_event.IsNotNull()) {
+ return readable_event->Clear();
+ }
+ }
+
+ LOG_ERROR(Kernel_SVC, "Event handle does not exist, event_handle=0x{:08X}", event_handle);
+
+ return ResultInvalidHandle;
+}
+
+Result CreateEvent(Core::System& system, Handle* out_write, Handle* out_read) {
+ LOG_DEBUG(Kernel_SVC, "called");
+
+ // Get the kernel reference and handle table.
+ auto& kernel = system.Kernel();
+ auto& handle_table = GetCurrentProcess(kernel).GetHandleTable();
+
+ // Reserve a new event from the process resource limit
+ KScopedResourceReservation event_reservation(GetCurrentProcessPointer(kernel),
+ LimitableResource::EventCountMax);
+ R_UNLESS(event_reservation.Succeeded(), ResultLimitReached);
+
+ // Create a new event.
+ KEvent* event = KEvent::Create(kernel);
+ R_UNLESS(event != nullptr, ResultOutOfResource);
+
+ // Initialize the event.
+ event->Initialize(GetCurrentProcessPointer(kernel));
+
+ // Commit the thread reservation.
+ event_reservation.Commit();
+
+ // Ensure that we clean up the event (and its only references are handle table) on function end.
+ SCOPE_EXIT({
+ event->GetReadableEvent().Close();
+ event->Close();
+ });
+
+ // Register the event.
+ KEvent::Register(kernel, event);
+
+ // Add the event to the handle table.
+ R_TRY(handle_table.Add(out_write, event));
+
+ // Ensure that we maintaing a clean handle state on exit.
+ auto handle_guard = SCOPE_GUARD({ handle_table.Remove(*out_write); });
+
+ // Add the readable event to the handle table.
+ R_TRY(handle_table.Add(out_read, std::addressof(event->GetReadableEvent())));
+
+ // We succeeded.
+ handle_guard.Cancel();
+ return ResultSuccess;
+}
+
+Result SignalEvent64(Core::System& system, Handle event_handle) {
+ R_RETURN(SignalEvent(system, event_handle));
+}
+
+Result ClearEvent64(Core::System& system, Handle event_handle) {
+ R_RETURN(ClearEvent(system, event_handle));
+}
+
+Result CreateEvent64(Core::System& system, Handle* out_write_handle, Handle* out_read_handle) {
+ R_RETURN(CreateEvent(system, out_write_handle, out_read_handle));
+}
+
+Result SignalEvent64From32(Core::System& system, Handle event_handle) {
+ R_RETURN(SignalEvent(system, event_handle));
+}
+
+Result ClearEvent64From32(Core::System& system, Handle event_handle) {
+ R_RETURN(ClearEvent(system, event_handle));
+}
+
+Result CreateEvent64From32(Core::System& system, Handle* out_write_handle,
+ Handle* out_read_handle) {
+ R_RETURN(CreateEvent(system, out_write_handle, out_read_handle));
+}
+
+} // 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..c2782908d
--- /dev/null
+++ b/src/core/hle/kernel/svc/svc_exception.cpp
@@ -0,0 +1,137 @@
+// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include "core/core.h"
+#include "core/debugger/debugger.h"
+#include "core/hle/kernel/k_thread.h"
+#include "core/hle/kernel/svc.h"
+#include "core/hle/kernel/svc_types.h"
+#include "core/memory.h"
+#include "core/reporter.h"
+
+namespace Kernel::Svc {
+
+/// Break program execution
+void Break(Core::System& system, BreakReason reason, u64 info1, u64 info2) {
+ BreakReason break_reason =
+ reason & static_cast<BreakReason>(~BreakReason::NotificationOnlyFlag);
+ bool notification_only = True(reason & BreakReason::NotificationOnlyFlag);
+
+ bool has_dumped_buffer{};
+ std::vector<u8> debug_buffer;
+
+ const auto handle_debug_buffer = [&](VAddr addr, u64 sz) {
+ if (sz == 0 || addr == 0 || has_dumped_buffer) {
+ return;
+ }
+
+ auto& memory = system.Memory();
+
+ // This typically is an error code so we're going to assume this is the case
+ if (sz == sizeof(u32)) {
+ LOG_CRITICAL(Debug_Emulated, "debug_buffer_err_code={:X}", memory.Read32(addr));
+ } else {
+ // We don't know what's in here so we'll hexdump it
+ debug_buffer.resize(sz);
+ memory.ReadBlock(addr, debug_buffer.data(), sz);
+ std::string hexdump;
+ for (std::size_t i = 0; i < debug_buffer.size(); i++) {
+ hexdump += fmt::format("{:02X} ", debug_buffer[i]);
+ if (i != 0 && i % 16 == 0) {
+ hexdump += '\n';
+ }
+ }
+ LOG_CRITICAL(Debug_Emulated, "debug_buffer=\n{}", hexdump);
+ }
+ has_dumped_buffer = true;
+ };
+ switch (break_reason) {
+ case BreakReason::Panic:
+ LOG_CRITICAL(Debug_Emulated, "Userspace PANIC! info1=0x{:016X}, info2=0x{:016X}", info1,
+ info2);
+ handle_debug_buffer(info1, info2);
+ break;
+ case BreakReason::Assert:
+ LOG_CRITICAL(Debug_Emulated, "Userspace Assertion failed! info1=0x{:016X}, info2=0x{:016X}",
+ info1, info2);
+ handle_debug_buffer(info1, info2);
+ break;
+ case BreakReason::User:
+ LOG_WARNING(Debug_Emulated, "Userspace Break! 0x{:016X} with size 0x{:016X}", info1, info2);
+ handle_debug_buffer(info1, info2);
+ break;
+ case BreakReason::PreLoadDll:
+ LOG_INFO(Debug_Emulated,
+ "Userspace Attempting to load an NRO at 0x{:016X} with size 0x{:016X}", info1,
+ info2);
+ break;
+ case BreakReason::PostLoadDll:
+ LOG_INFO(Debug_Emulated, "Userspace Loaded an NRO at 0x{:016X} with size 0x{:016X}", info1,
+ info2);
+ break;
+ case BreakReason::PreUnloadDll:
+ LOG_INFO(Debug_Emulated,
+ "Userspace Attempting to unload an NRO at 0x{:016X} with size 0x{:016X}", info1,
+ info2);
+ break;
+ case BreakReason::PostUnloadDll:
+ LOG_INFO(Debug_Emulated, "Userspace Unloaded an NRO at 0x{:016X} with size 0x{:016X}",
+ info1, info2);
+ break;
+ case BreakReason::CppException:
+ LOG_CRITICAL(Debug_Emulated, "Signalling debugger. Uncaught C++ exception encountered.");
+ break;
+ default:
+ LOG_WARNING(
+ Debug_Emulated,
+ "Signalling debugger, Unknown break reason {:#X}, info1=0x{:016X}, info2=0x{:016X}",
+ reason, info1, info2);
+ handle_debug_buffer(info1, info2);
+ break;
+ }
+
+ system.GetReporter().SaveSvcBreakReport(
+ static_cast<u32>(reason), notification_only, info1, info2,
+ has_dumped_buffer ? std::make_optional(debug_buffer) : std::nullopt);
+
+ if (!notification_only) {
+ LOG_CRITICAL(
+ Debug_Emulated,
+ "Emulated program broke execution! reason=0x{:016X}, info1=0x{:016X}, info2=0x{:016X}",
+ reason, info1, info2);
+
+ handle_debug_buffer(info1, info2);
+
+ auto* const current_thread = GetCurrentThreadPointer(system.Kernel());
+ const auto thread_processor_id = current_thread->GetActiveCore();
+ system.ArmInterface(static_cast<std::size_t>(thread_processor_id)).LogBacktrace();
+ }
+
+ if (system.DebuggerEnabled()) {
+ auto* thread = system.Kernel().GetCurrentEmuThread();
+ system.GetDebugger().NotifyThreadStopped(thread);
+ thread->RequestSuspend(Kernel::SuspendType::Debug);
+ }
+}
+
+void ReturnFromException(Core::System& system, Result result) {
+ UNIMPLEMENTED();
+}
+
+void Break64(Core::System& system, BreakReason break_reason, uint64_t arg, uint64_t size) {
+ Break(system, break_reason, arg, size);
+}
+
+void Break64From32(Core::System& system, BreakReason break_reason, uint32_t arg, uint32_t size) {
+ Break(system, break_reason, arg, size);
+}
+
+void ReturnFromException64(Core::System& system, Result result) {
+ ReturnFromException(system, result);
+}
+
+void ReturnFromException64From32(Core::System& system, Result result) {
+ ReturnFromException(system, result);
+}
+
+} // 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..58dc47508
--- /dev/null
+++ b/src/core/hle/kernel/svc/svc_info.cpp
@@ -0,0 +1,297 @@
+// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include "core/core.h"
+#include "core/core_timing.h"
+#include "core/hle/kernel/k_process.h"
+#include "core/hle/kernel/k_resource_limit.h"
+#include "core/hle/kernel/svc.h"
+
+namespace Kernel::Svc {
+
+/// Gets system/memory information for the current process
+Result GetInfo(Core::System& system, u64* result, InfoType info_id_type, Handle handle,
+ u64 info_sub_id) {
+ LOG_TRACE(Kernel_SVC, "called info_id=0x{:X}, info_sub_id=0x{:X}, handle=0x{:08X}",
+ info_id_type, info_sub_id, handle);
+
+ u32 info_id = static_cast<u32>(info_id_type);
+
+ switch (info_id_type) {
+ case InfoType::CoreMask:
+ case InfoType::PriorityMask:
+ case InfoType::AliasRegionAddress:
+ case InfoType::AliasRegionSize:
+ case InfoType::HeapRegionAddress:
+ case InfoType::HeapRegionSize:
+ case InfoType::AslrRegionAddress:
+ case InfoType::AslrRegionSize:
+ case InfoType::StackRegionAddress:
+ case InfoType::StackRegionSize:
+ case InfoType::TotalMemorySize:
+ case InfoType::UsedMemorySize:
+ case InfoType::SystemResourceSizeTotal:
+ case InfoType::SystemResourceSizeUsed:
+ case InfoType::ProgramId:
+ case InfoType::UserExceptionContextAddress:
+ case InfoType::TotalNonSystemMemorySize:
+ case InfoType::UsedNonSystemMemorySize:
+ case InfoType::IsApplication:
+ case InfoType::FreeThreadCount: {
+ if (info_sub_id != 0) {
+ LOG_ERROR(Kernel_SVC, "Info sub id is non zero! info_id={}, info_sub_id={}", info_id,
+ info_sub_id);
+ return ResultInvalidEnumValue;
+ }
+
+ const auto& handle_table = GetCurrentProcess(system.Kernel()).GetHandleTable();
+ KScopedAutoObject process = handle_table.GetObject<KProcess>(handle);
+ if (process.IsNull()) {
+ LOG_ERROR(Kernel_SVC, "Process is not valid! info_id={}, info_sub_id={}, handle={:08X}",
+ info_id, info_sub_id, handle);
+ return ResultInvalidHandle;
+ }
+
+ switch (info_id_type) {
+ case InfoType::CoreMask:
+ *result = process->GetCoreMask();
+ return ResultSuccess;
+
+ case InfoType::PriorityMask:
+ *result = process->GetPriorityMask();
+ return ResultSuccess;
+
+ case InfoType::AliasRegionAddress:
+ *result = process->PageTable().GetAliasRegionStart();
+ return ResultSuccess;
+
+ case InfoType::AliasRegionSize:
+ *result = process->PageTable().GetAliasRegionSize();
+ return ResultSuccess;
+
+ case InfoType::HeapRegionAddress:
+ *result = process->PageTable().GetHeapRegionStart();
+ return ResultSuccess;
+
+ case InfoType::HeapRegionSize:
+ *result = process->PageTable().GetHeapRegionSize();
+ return ResultSuccess;
+
+ case InfoType::AslrRegionAddress:
+ *result = process->PageTable().GetAliasCodeRegionStart();
+ return ResultSuccess;
+
+ case InfoType::AslrRegionSize:
+ *result = process->PageTable().GetAliasCodeRegionSize();
+ return ResultSuccess;
+
+ case InfoType::StackRegionAddress:
+ *result = process->PageTable().GetStackRegionStart();
+ return ResultSuccess;
+
+ case InfoType::StackRegionSize:
+ *result = process->PageTable().GetStackRegionSize();
+ return ResultSuccess;
+
+ case InfoType::TotalMemorySize:
+ *result = process->GetTotalPhysicalMemoryAvailable();
+ return ResultSuccess;
+
+ case InfoType::UsedMemorySize:
+ *result = process->GetTotalPhysicalMemoryUsed();
+ return ResultSuccess;
+
+ case InfoType::SystemResourceSizeTotal:
+ *result = process->GetSystemResourceSize();
+ return ResultSuccess;
+
+ case InfoType::SystemResourceSizeUsed:
+ LOG_WARNING(Kernel_SVC, "(STUBBED) Attempted to query system resource usage");
+ *result = process->GetSystemResourceUsage();
+ return ResultSuccess;
+
+ case InfoType::ProgramId:
+ *result = process->GetProgramID();
+ return ResultSuccess;
+
+ case InfoType::UserExceptionContextAddress:
+ *result = process->GetProcessLocalRegionAddress();
+ return ResultSuccess;
+
+ case InfoType::TotalNonSystemMemorySize:
+ *result = process->GetTotalPhysicalMemoryAvailableWithoutSystemResource();
+ return ResultSuccess;
+
+ case InfoType::UsedNonSystemMemorySize:
+ *result = process->GetTotalPhysicalMemoryUsedWithoutSystemResource();
+ return ResultSuccess;
+
+ case InfoType::FreeThreadCount:
+ *result = process->GetFreeThreadCount();
+ return ResultSuccess;
+
+ default:
+ break;
+ }
+
+ LOG_ERROR(Kernel_SVC, "Unimplemented svcGetInfo id=0x{:016X}", info_id);
+ return ResultInvalidEnumValue;
+ }
+
+ case InfoType::DebuggerAttached:
+ *result = 0;
+ return ResultSuccess;
+
+ case InfoType::ResourceLimit: {
+ if (handle != 0) {
+ LOG_ERROR(Kernel, "Handle is non zero! handle={:08X}", handle);
+ return ResultInvalidHandle;
+ }
+
+ if (info_sub_id != 0) {
+ LOG_ERROR(Kernel, "Info sub id is non zero! info_id={}, info_sub_id={}", info_id,
+ info_sub_id);
+ return ResultInvalidCombination;
+ }
+
+ KProcess* const current_process = GetCurrentProcessPointer(system.Kernel());
+ KHandleTable& handle_table = current_process->GetHandleTable();
+ const auto resource_limit = current_process->GetResourceLimit();
+ if (!resource_limit) {
+ *result = Svc::InvalidHandle;
+ // Yes, the kernel considers this a successful operation.
+ return ResultSuccess;
+ }
+
+ Handle resource_handle{};
+ R_TRY(handle_table.Add(&resource_handle, resource_limit));
+
+ *result = resource_handle;
+ return ResultSuccess;
+ }
+
+ case InfoType::RandomEntropy:
+ if (handle != 0) {
+ LOG_ERROR(Kernel_SVC, "Process Handle is non zero, expected 0 result but got {:016X}",
+ handle);
+ return ResultInvalidHandle;
+ }
+
+ if (info_sub_id >= KProcess::RANDOM_ENTROPY_SIZE) {
+ LOG_ERROR(Kernel_SVC, "Entropy size is out of range, expected {} but got {}",
+ KProcess::RANDOM_ENTROPY_SIZE, info_sub_id);
+ return ResultInvalidCombination;
+ }
+
+ *result = GetCurrentProcess(system.Kernel()).GetRandomEntropy(info_sub_id);
+ return ResultSuccess;
+
+ case InfoType::InitialProcessIdRange:
+ LOG_WARNING(Kernel_SVC,
+ "(STUBBED) Attempted to query privileged process id bounds, returned 0");
+ *result = 0;
+ return ResultSuccess;
+
+ case InfoType::ThreadTickCount: {
+ constexpr u64 num_cpus = 4;
+ if (info_sub_id != 0xFFFFFFFFFFFFFFFF && info_sub_id >= num_cpus) {
+ LOG_ERROR(Kernel_SVC, "Core count is out of range, expected {} but got {}", num_cpus,
+ info_sub_id);
+ return ResultInvalidCombination;
+ }
+
+ KScopedAutoObject thread = GetCurrentProcess(system.Kernel())
+ .GetHandleTable()
+ .GetObject<KThread>(static_cast<Handle>(handle));
+ if (thread.IsNull()) {
+ LOG_ERROR(Kernel_SVC, "Thread handle does not exist, handle=0x{:08X}",
+ static_cast<Handle>(handle));
+ return ResultInvalidHandle;
+ }
+
+ const auto& core_timing = system.CoreTiming();
+ const auto& scheduler = *system.Kernel().CurrentScheduler();
+ const auto* const current_thread = GetCurrentThreadPointer(system.Kernel());
+ const bool same_thread = current_thread == thread.GetPointerUnsafe();
+
+ const u64 prev_ctx_ticks = scheduler.GetLastContextSwitchTime();
+ u64 out_ticks = 0;
+ if (same_thread && info_sub_id == 0xFFFFFFFFFFFFFFFF) {
+ const u64 thread_ticks = current_thread->GetCpuTime();
+
+ out_ticks = thread_ticks + (core_timing.GetCPUTicks() - prev_ctx_ticks);
+ } else if (same_thread && info_sub_id == system.Kernel().CurrentPhysicalCoreIndex()) {
+ out_ticks = core_timing.GetCPUTicks() - prev_ctx_ticks;
+ }
+
+ *result = out_ticks;
+ return ResultSuccess;
+ }
+ case InfoType::IdleTickCount: {
+ // Verify the input handle is invalid.
+ R_UNLESS(handle == InvalidHandle, ResultInvalidHandle);
+
+ // Verify the requested core is valid.
+ const bool core_valid =
+ (info_sub_id == 0xFFFFFFFFFFFFFFFF) ||
+ (info_sub_id == static_cast<u64>(system.Kernel().CurrentPhysicalCoreIndex()));
+ R_UNLESS(core_valid, ResultInvalidCombination);
+
+ // Get the idle tick count.
+ *result = system.Kernel().CurrentScheduler()->GetIdleThread()->GetCpuTime();
+ return ResultSuccess;
+ }
+ case InfoType::MesosphereCurrentProcess: {
+ // Verify the input handle is invalid.
+ R_UNLESS(handle == InvalidHandle, ResultInvalidHandle);
+
+ // Verify the sub-type is valid.
+ R_UNLESS(info_sub_id == 0, ResultInvalidCombination);
+
+ // Get the handle table.
+ KProcess* current_process = GetCurrentProcessPointer(system.Kernel());
+ KHandleTable& handle_table = current_process->GetHandleTable();
+
+ // Get a new handle for the current process.
+ Handle tmp;
+ R_TRY(handle_table.Add(&tmp, current_process));
+
+ // Set the output.
+ *result = tmp;
+
+ // We succeeded.
+ return ResultSuccess;
+ }
+ default:
+ LOG_ERROR(Kernel_SVC, "Unimplemented svcGetInfo id=0x{:016X}", info_id);
+ return ResultInvalidEnumValue;
+ }
+}
+
+Result GetSystemInfo(Core::System& system, uint64_t* out, SystemInfoType info_type, Handle handle,
+ uint64_t info_subtype) {
+ UNIMPLEMENTED();
+ R_THROW(ResultNotImplemented);
+}
+
+Result GetInfo64(Core::System& system, uint64_t* out, InfoType info_type, Handle handle,
+ uint64_t info_subtype) {
+ R_RETURN(GetInfo(system, out, info_type, handle, info_subtype));
+}
+
+Result GetSystemInfo64(Core::System& system, uint64_t* out, SystemInfoType info_type, Handle handle,
+ uint64_t info_subtype) {
+ R_RETURN(GetSystemInfo(system, out, info_type, handle, info_subtype));
+}
+
+Result GetInfo64From32(Core::System& system, uint64_t* out, InfoType info_type, Handle handle,
+ uint64_t info_subtype) {
+ R_RETURN(GetInfo(system, out, info_type, handle, info_subtype));
+}
+
+Result GetSystemInfo64From32(Core::System& system, uint64_t* out, SystemInfoType info_type,
+ Handle handle, uint64_t info_subtype) {
+ R_RETURN(GetSystemInfo(system, out, info_type, handle, info_subtype));
+}
+
+} // namespace Kernel::Svc
diff --git a/src/core/hle/kernel/svc/svc_insecure_memory.cpp b/src/core/hle/kernel/svc/svc_insecure_memory.cpp
new file mode 100644
index 000000000..79882685d
--- /dev/null
+++ b/src/core/hle/kernel/svc/svc_insecure_memory.cpp
@@ -0,0 +1,35 @@
+// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include "core/hle/kernel/svc.h"
+#include "core/hle/kernel/svc_results.h"
+
+namespace Kernel::Svc {
+
+Result MapInsecureMemory(Core::System& system, uintptr_t address, size_t size) {
+ UNIMPLEMENTED();
+ R_THROW(ResultNotImplemented);
+}
+
+Result UnmapInsecureMemory(Core::System& system, uintptr_t address, size_t size) {
+ UNIMPLEMENTED();
+ R_THROW(ResultNotImplemented);
+}
+
+Result MapInsecureMemory64(Core::System& system, uint64_t address, uint64_t size) {
+ R_RETURN(MapInsecureMemory(system, address, size));
+}
+
+Result UnmapInsecureMemory64(Core::System& system, uint64_t address, uint64_t size) {
+ R_RETURN(UnmapInsecureMemory(system, address, size));
+}
+
+Result MapInsecureMemory64From32(Core::System& system, uint32_t address, uint32_t size) {
+ R_RETURN(MapInsecureMemory(system, address, size));
+}
+
+Result UnmapInsecureMemory64From32(Core::System& system, uint32_t address, uint32_t size) {
+ R_RETURN(UnmapInsecureMemory(system, address, size));
+}
+
+} // 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..768b30a1f
--- /dev/null
+++ b/src/core/hle/kernel/svc/svc_interrupt_event.cpp
@@ -0,0 +1,25 @@
+// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include "core/hle/kernel/svc.h"
+#include "core/hle/kernel/svc_results.h"
+
+namespace Kernel::Svc {
+
+Result CreateInterruptEvent(Core::System& system, Handle* out, int32_t interrupt_id,
+ InterruptType type) {
+ UNIMPLEMENTED();
+ R_THROW(ResultNotImplemented);
+}
+
+Result CreateInterruptEvent64(Core::System& system, Handle* out_read_handle, int32_t interrupt_id,
+ InterruptType interrupt_type) {
+ R_RETURN(CreateInterruptEvent(system, out_read_handle, interrupt_id, interrupt_type));
+}
+
+Result CreateInterruptEvent64From32(Core::System& system, Handle* out_read_handle,
+ int32_t interrupt_id, InterruptType interrupt_type) {
+ R_RETURN(CreateInterruptEvent(system, out_read_handle, interrupt_id, interrupt_type));
+}
+
+} // 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..33f3d69bf
--- /dev/null
+++ b/src/core/hle/kernel/svc/svc_io_pool.cpp
@@ -0,0 +1,71 @@
+// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include "core/hle/kernel/svc.h"
+#include "core/hle/kernel/svc_results.h"
+
+namespace Kernel::Svc {
+
+Result CreateIoPool(Core::System& system, Handle* out, IoPoolType pool_type) {
+ UNIMPLEMENTED();
+ R_THROW(ResultNotImplemented);
+}
+
+Result CreateIoRegion(Core::System& system, Handle* out, Handle io_pool_handle, uint64_t phys_addr,
+ size_t size, MemoryMapping mapping, MemoryPermission perm) {
+ UNIMPLEMENTED();
+ R_THROW(ResultNotImplemented);
+}
+
+Result MapIoRegion(Core::System& system, Handle io_region_handle, uintptr_t address, size_t size,
+ MemoryPermission map_perm) {
+ UNIMPLEMENTED();
+ R_THROW(ResultNotImplemented);
+}
+
+Result UnmapIoRegion(Core::System& system, Handle io_region_handle, uintptr_t address,
+ size_t size) {
+ UNIMPLEMENTED();
+ R_THROW(ResultNotImplemented);
+}
+
+Result CreateIoPool64(Core::System& system, Handle* out_handle, IoPoolType pool_type) {
+ R_RETURN(CreateIoPool(system, out_handle, pool_type));
+}
+
+Result CreateIoRegion64(Core::System& system, Handle* out_handle, Handle io_pool,
+ uint64_t physical_address, uint64_t size, MemoryMapping mapping,
+ MemoryPermission perm) {
+ R_RETURN(CreateIoRegion(system, out_handle, io_pool, physical_address, size, mapping, perm));
+}
+
+Result MapIoRegion64(Core::System& system, Handle io_region, uint64_t address, uint64_t size,
+ MemoryPermission perm) {
+ R_RETURN(MapIoRegion(system, io_region, address, size, perm));
+}
+
+Result UnmapIoRegion64(Core::System& system, Handle io_region, uint64_t address, uint64_t size) {
+ R_RETURN(UnmapIoRegion(system, io_region, address, size));
+}
+
+Result CreateIoPool64From32(Core::System& system, Handle* out_handle, IoPoolType pool_type) {
+ R_RETURN(CreateIoPool(system, out_handle, pool_type));
+}
+
+Result CreateIoRegion64From32(Core::System& system, Handle* out_handle, Handle io_pool,
+ uint64_t physical_address, uint32_t size, MemoryMapping mapping,
+ MemoryPermission perm) {
+ R_RETURN(CreateIoRegion(system, out_handle, io_pool, physical_address, size, mapping, perm));
+}
+
+Result MapIoRegion64From32(Core::System& system, Handle io_region, uint32_t address, uint32_t size,
+ MemoryPermission perm) {
+ R_RETURN(MapIoRegion(system, io_region, address, size, perm));
+}
+
+Result UnmapIoRegion64From32(Core::System& system, Handle io_region, uint32_t address,
+ uint32_t size) {
+ R_RETURN(UnmapIoRegion(system, io_region, address, size));
+}
+
+} // 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..a7a2c3b92
--- /dev/null
+++ b/src/core/hle/kernel/svc/svc_ipc.cpp
@@ -0,0 +1,172 @@
+// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include "common/scope_exit.h"
+#include "core/core.h"
+#include "core/hle/kernel/k_client_session.h"
+#include "core/hle/kernel/k_process.h"
+#include "core/hle/kernel/k_server_session.h"
+#include "core/hle/kernel/svc.h"
+
+namespace Kernel::Svc {
+
+/// Makes a blocking IPC call to a service.
+Result SendSyncRequest(Core::System& system, Handle handle) {
+ // Get the client session from its handle.
+ KScopedAutoObject session =
+ GetCurrentProcess(system.Kernel()).GetHandleTable().GetObject<KClientSession>(handle);
+ R_UNLESS(session.IsNotNull(), ResultInvalidHandle);
+
+ LOG_TRACE(Kernel_SVC, "called handle=0x{:08X}({})", handle, session->GetName());
+
+ return session->SendSyncRequest();
+}
+
+Result SendSyncRequestWithUserBuffer(Core::System& system, uint64_t message_buffer,
+ uint64_t message_buffer_size, Handle session_handle) {
+ UNIMPLEMENTED();
+ R_THROW(ResultNotImplemented);
+}
+
+Result SendAsyncRequestWithUserBuffer(Core::System& system, Handle* out_event_handle,
+ uint64_t message_buffer, uint64_t message_buffer_size,
+ Handle session_handle) {
+ UNIMPLEMENTED();
+ R_THROW(ResultNotImplemented);
+}
+
+Result ReplyAndReceive(Core::System& system, s32* out_index, uint64_t handles_addr, s32 num_handles,
+ Handle reply_target, s64 timeout_ns) {
+ auto& kernel = system.Kernel();
+ auto& handle_table = GetCurrentProcess(kernel).GetHandleTable();
+
+ R_UNLESS(0 <= num_handles && num_handles <= ArgumentHandleCountMax, ResultOutOfRange);
+ R_UNLESS(system.Memory().IsValidVirtualAddressRange(
+ handles_addr, static_cast<u64>(sizeof(Handle) * num_handles)),
+ ResultInvalidPointer);
+
+ std::vector<Handle> handles(num_handles);
+ system.Memory().ReadBlock(handles_addr, handles.data(), sizeof(Handle) * num_handles);
+
+ // Convert handle list to object table.
+ std::vector<KSynchronizationObject*> objs(num_handles);
+ R_UNLESS(handle_table.GetMultipleObjects<KSynchronizationObject>(objs.data(), handles.data(),
+ num_handles),
+ ResultInvalidHandle);
+
+ // Ensure handles are closed when we're done.
+ SCOPE_EXIT({
+ for (auto i = 0; i < num_handles; ++i) {
+ objs[i]->Close();
+ }
+ });
+
+ // Reply to the target, if one is specified.
+ if (reply_target != InvalidHandle) {
+ KScopedAutoObject session = handle_table.GetObject<KServerSession>(reply_target);
+ R_UNLESS(session.IsNotNull(), ResultInvalidHandle);
+
+ // If we fail to reply, we want to set the output index to -1.
+ ON_RESULT_FAILURE {
+ *out_index = -1;
+ };
+
+ // Send the reply.
+ R_TRY(session->SendReply());
+ }
+
+ // Wait for a message.
+ while (true) {
+ // Wait for an object.
+ s32 index;
+ Result result = KSynchronizationObject::Wait(kernel, &index, objs.data(),
+ static_cast<s32>(objs.size()), timeout_ns);
+ if (result == ResultTimedOut) {
+ return result;
+ }
+
+ // Receive the request.
+ if (R_SUCCEEDED(result)) {
+ KServerSession* session = objs[index]->DynamicCast<KServerSession*>();
+ if (session != nullptr) {
+ result = session->ReceiveRequest();
+ if (result == ResultNotFound) {
+ continue;
+ }
+ }
+ }
+
+ *out_index = index;
+ return result;
+ }
+}
+
+Result ReplyAndReceiveWithUserBuffer(Core::System& system, int32_t* out_index,
+ uint64_t message_buffer, uint64_t message_buffer_size,
+ uint64_t handles, int32_t num_handles, Handle reply_target,
+ int64_t timeout_ns) {
+ UNIMPLEMENTED();
+ R_THROW(ResultNotImplemented);
+}
+
+Result SendSyncRequest64(Core::System& system, Handle session_handle) {
+ R_RETURN(SendSyncRequest(system, session_handle));
+}
+
+Result SendSyncRequestWithUserBuffer64(Core::System& system, uint64_t message_buffer,
+ uint64_t message_buffer_size, Handle session_handle) {
+ R_RETURN(
+ SendSyncRequestWithUserBuffer(system, message_buffer, message_buffer_size, session_handle));
+}
+
+Result SendAsyncRequestWithUserBuffer64(Core::System& system, Handle* out_event_handle,
+ uint64_t message_buffer, uint64_t message_buffer_size,
+ Handle session_handle) {
+ R_RETURN(SendAsyncRequestWithUserBuffer(system, out_event_handle, message_buffer,
+ message_buffer_size, session_handle));
+}
+
+Result ReplyAndReceive64(Core::System& system, int32_t* out_index, uint64_t handles,
+ int32_t num_handles, Handle reply_target, int64_t timeout_ns) {
+ R_RETURN(ReplyAndReceive(system, out_index, handles, num_handles, reply_target, timeout_ns));
+}
+
+Result ReplyAndReceiveWithUserBuffer64(Core::System& system, int32_t* out_index,
+ uint64_t message_buffer, uint64_t message_buffer_size,
+ uint64_t handles, int32_t num_handles, Handle reply_target,
+ int64_t timeout_ns) {
+ R_RETURN(ReplyAndReceiveWithUserBuffer(system, out_index, message_buffer, message_buffer_size,
+ handles, num_handles, reply_target, timeout_ns));
+}
+
+Result SendSyncRequest64From32(Core::System& system, Handle session_handle) {
+ R_RETURN(SendSyncRequest(system, session_handle));
+}
+
+Result SendSyncRequestWithUserBuffer64From32(Core::System& system, uint32_t message_buffer,
+ uint32_t message_buffer_size, Handle session_handle) {
+ R_RETURN(
+ SendSyncRequestWithUserBuffer(system, message_buffer, message_buffer_size, session_handle));
+}
+
+Result SendAsyncRequestWithUserBuffer64From32(Core::System& system, Handle* out_event_handle,
+ uint32_t message_buffer, uint32_t message_buffer_size,
+ Handle session_handle) {
+ R_RETURN(SendAsyncRequestWithUserBuffer(system, out_event_handle, message_buffer,
+ message_buffer_size, session_handle));
+}
+
+Result ReplyAndReceive64From32(Core::System& system, int32_t* out_index, uint32_t handles,
+ int32_t num_handles, Handle reply_target, int64_t timeout_ns) {
+ R_RETURN(ReplyAndReceive(system, out_index, handles, num_handles, reply_target, timeout_ns));
+}
+
+Result ReplyAndReceiveWithUserBuffer64From32(Core::System& system, int32_t* out_index,
+ uint32_t message_buffer, uint32_t message_buffer_size,
+ uint32_t handles, int32_t num_handles,
+ Handle reply_target, int64_t timeout_ns) {
+ R_RETURN(ReplyAndReceiveWithUserBuffer(system, out_index, message_buffer, message_buffer_size,
+ handles, num_handles, reply_target, timeout_ns));
+}
+
+} // 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..cee048279
--- /dev/null
+++ b/src/core/hle/kernel/svc/svc_kernel_debug.cpp
@@ -0,0 +1,35 @@
+// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include "core/hle/kernel/svc.h"
+
+namespace Kernel::Svc {
+
+void KernelDebug(Core::System& system, KernelDebugType kernel_debug_type, u64 arg0, u64 arg1,
+ u64 arg2) {
+ // Intentionally do nothing, as this does nothing in released kernel binaries.
+}
+
+void ChangeKernelTraceState(Core::System& system, KernelTraceState trace_state) {
+ // Intentionally do nothing, as this does nothing in released kernel binaries.
+}
+
+void KernelDebug64(Core::System& system, KernelDebugType kern_debug_type, uint64_t arg0,
+ uint64_t arg1, uint64_t arg2) {
+ KernelDebug(system, kern_debug_type, arg0, arg1, arg2);
+}
+
+void ChangeKernelTraceState64(Core::System& system, KernelTraceState kern_trace_state) {
+ ChangeKernelTraceState(system, kern_trace_state);
+}
+
+void KernelDebug64From32(Core::System& system, KernelDebugType kern_debug_type, uint64_t arg0,
+ uint64_t arg1, uint64_t arg2) {
+ KernelDebug(system, kern_debug_type, arg0, arg1, arg2);
+}
+
+void ChangeKernelTraceState64From32(Core::System& system, KernelTraceState kern_trace_state) {
+ ChangeKernelTraceState(system, kern_trace_state);
+}
+
+} // 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..b76ce984c
--- /dev/null
+++ b/src/core/hle/kernel/svc/svc_light_ipc.cpp
@@ -0,0 +1,73 @@
+// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include "core/arm/arm_interface.h"
+#include "core/core.h"
+#include "core/hle/kernel/svc.h"
+#include "core/hle/kernel/svc_results.h"
+
+namespace Kernel::Svc {
+
+Result SendSyncRequestLight(Core::System& system, Handle session_handle, u32* args) {
+ UNIMPLEMENTED();
+ R_THROW(ResultNotImplemented);
+}
+
+Result ReplyAndReceiveLight(Core::System& system, Handle session_handle, u32* args) {
+ UNIMPLEMENTED();
+ R_THROW(ResultNotImplemented);
+}
+
+Result SendSyncRequestLight64(Core::System& system, Handle session_handle, u32* args) {
+ R_RETURN(SendSyncRequestLight(system, session_handle, args));
+}
+
+Result ReplyAndReceiveLight64(Core::System& system, Handle session_handle, u32* args) {
+ R_RETURN(ReplyAndReceiveLight(system, session_handle, args));
+}
+
+Result SendSyncRequestLight64From32(Core::System& system, Handle session_handle, u32* args) {
+ R_RETURN(SendSyncRequestLight(system, session_handle, args));
+}
+
+Result ReplyAndReceiveLight64From32(Core::System& system, Handle session_handle, u32* args) {
+ R_RETURN(ReplyAndReceiveLight(system, session_handle, args));
+}
+
+// Custom ABI implementation for light IPC.
+
+template <typename F>
+static void SvcWrap_LightIpc(Core::System& system, F&& cb) {
+ auto& core = system.CurrentArmInterface();
+ std::array<u32, 7> arguments{};
+
+ Handle session_handle = static_cast<Handle>(core.GetReg(0));
+ for (int i = 0; i < 7; i++) {
+ arguments[i] = static_cast<u32>(core.GetReg(i + 1));
+ }
+
+ Result ret = cb(system, session_handle, arguments.data());
+
+ core.SetReg(0, ret.raw);
+ for (int i = 0; i < 7; i++) {
+ core.SetReg(i + 1, arguments[i]);
+ }
+}
+
+void SvcWrap_SendSyncRequestLight64(Core::System& system) {
+ SvcWrap_LightIpc(system, SendSyncRequestLight64);
+}
+
+void SvcWrap_ReplyAndReceiveLight64(Core::System& system) {
+ SvcWrap_LightIpc(system, ReplyAndReceiveLight64);
+}
+
+void SvcWrap_SendSyncRequestLight64From32(Core::System& system) {
+ SvcWrap_LightIpc(system, SendSyncRequestLight64From32);
+}
+
+void SvcWrap_ReplyAndReceiveLight64From32(Core::System& system) {
+ SvcWrap_LightIpc(system, ReplyAndReceiveLight64From32);
+}
+
+} // 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..f3d3e140b
--- /dev/null
+++ b/src/core/hle/kernel/svc/svc_lock.cpp
@@ -0,0 +1,66 @@
+// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include "core/core.h"
+#include "core/hle/kernel/k_memory_layout.h"
+#include "core/hle/kernel/k_process.h"
+#include "core/hle/kernel/svc.h"
+
+namespace Kernel::Svc {
+
+/// Attempts to locks a mutex
+Result ArbitrateLock(Core::System& system, Handle thread_handle, VAddr address, u32 tag) {
+ LOG_TRACE(Kernel_SVC, "called thread_handle=0x{:08X}, address=0x{:X}, tag=0x{:08X}",
+ thread_handle, address, tag);
+
+ // Validate the input address.
+ if (IsKernelAddress(address)) {
+ LOG_ERROR(Kernel_SVC, "Attempting to arbitrate a lock on a kernel address (address={:08X})",
+ address);
+ return ResultInvalidCurrentMemory;
+ }
+ if (!Common::IsAligned(address, sizeof(u32))) {
+ LOG_ERROR(Kernel_SVC, "Input address must be 4 byte aligned (address: {:08X})", address);
+ return ResultInvalidAddress;
+ }
+
+ return GetCurrentProcess(system.Kernel()).WaitForAddress(thread_handle, address, tag);
+}
+
+/// Unlock a mutex
+Result ArbitrateUnlock(Core::System& system, VAddr address) {
+ LOG_TRACE(Kernel_SVC, "called address=0x{:X}", address);
+
+ // Validate the input address.
+ if (IsKernelAddress(address)) {
+ LOG_ERROR(Kernel_SVC,
+ "Attempting to arbitrate an unlock on a kernel address (address={:08X})",
+ address);
+ return ResultInvalidCurrentMemory;
+ }
+ if (!Common::IsAligned(address, sizeof(u32))) {
+ LOG_ERROR(Kernel_SVC, "Input address must be 4 byte aligned (address: {:08X})", address);
+ return ResultInvalidAddress;
+ }
+
+ return GetCurrentProcess(system.Kernel()).SignalToAddress(address);
+}
+
+Result ArbitrateLock64(Core::System& system, Handle thread_handle, uint64_t address, uint32_t tag) {
+ R_RETURN(ArbitrateLock(system, thread_handle, address, tag));
+}
+
+Result ArbitrateUnlock64(Core::System& system, uint64_t address) {
+ R_RETURN(ArbitrateUnlock(system, address));
+}
+
+Result ArbitrateLock64From32(Core::System& system, Handle thread_handle, uint32_t address,
+ uint32_t tag) {
+ R_RETURN(ArbitrateLock(system, thread_handle, address, tag));
+}
+
+Result ArbitrateUnlock64From32(Core::System& system, uint32_t address) {
+ R_RETURN(ArbitrateUnlock(system, address));
+}
+
+} // 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..214bcd073
--- /dev/null
+++ b/src/core/hle/kernel/svc/svc_memory.cpp
@@ -0,0 +1,217 @@
+// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include "core/core.h"
+#include "core/hle/kernel/k_process.h"
+#include "core/hle/kernel/svc.h"
+
+namespace Kernel::Svc {
+namespace {
+
+constexpr bool IsValidSetMemoryPermission(MemoryPermission perm) {
+ switch (perm) {
+ case MemoryPermission::None:
+ case MemoryPermission::Read:
+ case MemoryPermission::ReadWrite:
+ return true;
+ default:
+ return false;
+ }
+}
+
+// Checks if address + size is greater than the given address
+// This can return false if the size causes an overflow of a 64-bit type
+// or if the given size is zero.
+constexpr bool IsValidAddressRange(VAddr address, u64 size) {
+ return address + size > address;
+}
+
+// Helper function that performs the common sanity checks for svcMapMemory
+// and svcUnmapMemory. This is doable, as both functions perform their sanitizing
+// in the same order.
+Result MapUnmapMemorySanityChecks(const KPageTable& manager, VAddr dst_addr, VAddr src_addr,
+ u64 size) {
+ if (!Common::Is4KBAligned(dst_addr)) {
+ LOG_ERROR(Kernel_SVC, "Destination address is not aligned to 4KB, 0x{:016X}", dst_addr);
+ return ResultInvalidAddress;
+ }
+
+ if (!Common::Is4KBAligned(src_addr)) {
+ LOG_ERROR(Kernel_SVC, "Source address is not aligned to 4KB, 0x{:016X}", src_addr);
+ return ResultInvalidSize;
+ }
+
+ if (size == 0) {
+ LOG_ERROR(Kernel_SVC, "Size is 0");
+ return ResultInvalidSize;
+ }
+
+ if (!Common::Is4KBAligned(size)) {
+ LOG_ERROR(Kernel_SVC, "Size is not aligned to 4KB, 0x{:016X}", size);
+ return ResultInvalidSize;
+ }
+
+ if (!IsValidAddressRange(dst_addr, size)) {
+ LOG_ERROR(Kernel_SVC,
+ "Destination is not a valid address range, addr=0x{:016X}, size=0x{:016X}",
+ dst_addr, size);
+ return ResultInvalidCurrentMemory;
+ }
+
+ if (!IsValidAddressRange(src_addr, size)) {
+ LOG_ERROR(Kernel_SVC, "Source is not a valid address range, addr=0x{:016X}, size=0x{:016X}",
+ src_addr, size);
+ return ResultInvalidCurrentMemory;
+ }
+
+ if (!manager.IsInsideAddressSpace(src_addr, size)) {
+ LOG_ERROR(Kernel_SVC,
+ "Source is not within the address space, addr=0x{:016X}, size=0x{:016X}",
+ src_addr, size);
+ return ResultInvalidCurrentMemory;
+ }
+
+ if (manager.IsOutsideStackRegion(dst_addr, size)) {
+ LOG_ERROR(Kernel_SVC,
+ "Destination is not within the stack region, addr=0x{:016X}, size=0x{:016X}",
+ dst_addr, size);
+ return ResultInvalidMemoryRegion;
+ }
+
+ if (manager.IsInsideHeapRegion(dst_addr, size)) {
+ LOG_ERROR(Kernel_SVC,
+ "Destination does not fit within the heap region, addr=0x{:016X}, "
+ "size=0x{:016X}",
+ dst_addr, size);
+ return ResultInvalidMemoryRegion;
+ }
+
+ if (manager.IsInsideAliasRegion(dst_addr, size)) {
+ LOG_ERROR(Kernel_SVC,
+ "Destination does not fit within the map region, addr=0x{:016X}, "
+ "size=0x{:016X}",
+ dst_addr, size);
+ return ResultInvalidMemoryRegion;
+ }
+
+ return ResultSuccess;
+}
+
+} // namespace
+
+Result SetMemoryPermission(Core::System& system, VAddr address, u64 size, MemoryPermission perm) {
+ LOG_DEBUG(Kernel_SVC, "called, address=0x{:016X}, size=0x{:X}, perm=0x{:08X", address, size,
+ perm);
+
+ // Validate address / size.
+ R_UNLESS(Common::IsAligned(address, PageSize), ResultInvalidAddress);
+ R_UNLESS(Common::IsAligned(size, PageSize), ResultInvalidSize);
+ R_UNLESS(size > 0, ResultInvalidSize);
+ R_UNLESS((address < address + size), ResultInvalidCurrentMemory);
+
+ // Validate the permission.
+ R_UNLESS(IsValidSetMemoryPermission(perm), ResultInvalidNewMemoryPermission);
+
+ // Validate that the region is in range for the current process.
+ auto& page_table = GetCurrentProcess(system.Kernel()).PageTable();
+ R_UNLESS(page_table.Contains(address, size), ResultInvalidCurrentMemory);
+
+ // Set the memory attribute.
+ return page_table.SetMemoryPermission(address, size, perm);
+}
+
+Result SetMemoryAttribute(Core::System& system, VAddr address, u64 size, u32 mask, u32 attr) {
+ LOG_DEBUG(Kernel_SVC,
+ "called, address=0x{:016X}, size=0x{:X}, mask=0x{:08X}, attribute=0x{:08X}", address,
+ size, mask, attr);
+
+ // Validate address / size.
+ R_UNLESS(Common::IsAligned(address, PageSize), ResultInvalidAddress);
+ R_UNLESS(Common::IsAligned(size, PageSize), ResultInvalidSize);
+ R_UNLESS(size > 0, ResultInvalidSize);
+ R_UNLESS((address < address + size), ResultInvalidCurrentMemory);
+
+ // Validate the attribute and mask.
+ constexpr u32 SupportedMask = static_cast<u32>(MemoryAttribute::Uncached);
+ R_UNLESS((mask | attr) == mask, ResultInvalidCombination);
+ R_UNLESS((mask | attr | SupportedMask) == SupportedMask, ResultInvalidCombination);
+
+ // Validate that the region is in range for the current process.
+ auto& page_table{GetCurrentProcess(system.Kernel()).PageTable()};
+ R_UNLESS(page_table.Contains(address, size), ResultInvalidCurrentMemory);
+
+ // Set the memory attribute.
+ return page_table.SetMemoryAttribute(address, size, mask, attr);
+}
+
+/// Maps a memory range into a different range.
+Result MapMemory(Core::System& system, VAddr dst_addr, VAddr src_addr, u64 size) {
+ LOG_TRACE(Kernel_SVC, "called, dst_addr=0x{:X}, src_addr=0x{:X}, size=0x{:X}", dst_addr,
+ src_addr, size);
+
+ auto& page_table{GetCurrentProcess(system.Kernel()).PageTable()};
+
+ if (const Result result{MapUnmapMemorySanityChecks(page_table, dst_addr, src_addr, size)};
+ result.IsError()) {
+ return result;
+ }
+
+ return page_table.MapMemory(dst_addr, src_addr, size);
+}
+
+/// Unmaps a region that was previously mapped with svcMapMemory
+Result UnmapMemory(Core::System& system, VAddr dst_addr, VAddr src_addr, u64 size) {
+ LOG_TRACE(Kernel_SVC, "called, dst_addr=0x{:X}, src_addr=0x{:X}, size=0x{:X}", dst_addr,
+ src_addr, size);
+
+ auto& page_table{GetCurrentProcess(system.Kernel()).PageTable()};
+
+ if (const Result result{MapUnmapMemorySanityChecks(page_table, dst_addr, src_addr, size)};
+ result.IsError()) {
+ return result;
+ }
+
+ return page_table.UnmapMemory(dst_addr, src_addr, size);
+}
+
+Result SetMemoryPermission64(Core::System& system, uint64_t address, uint64_t size,
+ MemoryPermission perm) {
+ R_RETURN(SetMemoryPermission(system, address, size, perm));
+}
+
+Result SetMemoryAttribute64(Core::System& system, uint64_t address, uint64_t size, uint32_t mask,
+ uint32_t attr) {
+ R_RETURN(SetMemoryAttribute(system, address, size, mask, attr));
+}
+
+Result MapMemory64(Core::System& system, uint64_t dst_address, uint64_t src_address,
+ uint64_t size) {
+ R_RETURN(MapMemory(system, dst_address, src_address, size));
+}
+
+Result UnmapMemory64(Core::System& system, uint64_t dst_address, uint64_t src_address,
+ uint64_t size) {
+ R_RETURN(UnmapMemory(system, dst_address, src_address, size));
+}
+
+Result SetMemoryPermission64From32(Core::System& system, uint32_t address, uint32_t size,
+ MemoryPermission perm) {
+ R_RETURN(SetMemoryPermission(system, address, size, perm));
+}
+
+Result SetMemoryAttribute64From32(Core::System& system, uint32_t address, uint32_t size,
+ uint32_t mask, uint32_t attr) {
+ R_RETURN(SetMemoryAttribute(system, address, size, mask, attr));
+}
+
+Result MapMemory64From32(Core::System& system, uint32_t dst_address, uint32_t src_address,
+ uint32_t size) {
+ R_RETURN(MapMemory(system, dst_address, src_address, size));
+}
+
+Result UnmapMemory64From32(Core::System& system, uint32_t dst_address, uint32_t src_address,
+ uint32_t size) {
+ R_RETURN(UnmapMemory(system, dst_address, src_address, size));
+}
+
+} // 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..a1f534454
--- /dev/null
+++ b/src/core/hle/kernel/svc/svc_physical_memory.cpp
@@ -0,0 +1,185 @@
+// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include "core/core.h"
+#include "core/hle/kernel/k_process.h"
+#include "core/hle/kernel/svc.h"
+
+namespace Kernel::Svc {
+
+/// Set the process heap to a given Size. It can both extend and shrink the heap.
+Result SetHeapSize(Core::System& system, VAddr* out_address, u64 size) {
+ LOG_TRACE(Kernel_SVC, "called, heap_size=0x{:X}", size);
+
+ // Validate size.
+ R_UNLESS(Common::IsAligned(size, HeapSizeAlignment), ResultInvalidSize);
+ R_UNLESS(size < MainMemorySizeMax, ResultInvalidSize);
+
+ // Set the heap size.
+ R_TRY(GetCurrentProcess(system.Kernel()).PageTable().SetHeapSize(out_address, size));
+
+ return ResultSuccess;
+}
+
+/// Maps memory at a desired address
+Result MapPhysicalMemory(Core::System& system, VAddr addr, u64 size) {
+ LOG_DEBUG(Kernel_SVC, "called, addr=0x{:016X}, size=0x{:X}", addr, size);
+
+ if (!Common::Is4KBAligned(addr)) {
+ LOG_ERROR(Kernel_SVC, "Address is not aligned to 4KB, 0x{:016X}", addr);
+ return ResultInvalidAddress;
+ }
+
+ if (!Common::Is4KBAligned(size)) {
+ LOG_ERROR(Kernel_SVC, "Size is not aligned to 4KB, 0x{:X}", size);
+ return ResultInvalidSize;
+ }
+
+ if (size == 0) {
+ LOG_ERROR(Kernel_SVC, "Size is zero");
+ return ResultInvalidSize;
+ }
+
+ if (!(addr < addr + size)) {
+ LOG_ERROR(Kernel_SVC, "Size causes 64-bit overflow of address");
+ return ResultInvalidMemoryRegion;
+ }
+
+ KProcess* const current_process{GetCurrentProcessPointer(system.Kernel())};
+ auto& page_table{current_process->PageTable()};
+
+ if (current_process->GetSystemResourceSize() == 0) {
+ LOG_ERROR(Kernel_SVC, "System Resource Size is zero");
+ return ResultInvalidState;
+ }
+
+ if (!page_table.IsInsideAddressSpace(addr, size)) {
+ LOG_ERROR(Kernel_SVC,
+ "Address is not within the address space, addr=0x{:016X}, size=0x{:016X}", addr,
+ size);
+ return ResultInvalidMemoryRegion;
+ }
+
+ if (page_table.IsOutsideAliasRegion(addr, size)) {
+ LOG_ERROR(Kernel_SVC,
+ "Address is not within the alias region, addr=0x{:016X}, size=0x{:016X}", addr,
+ size);
+ return ResultInvalidMemoryRegion;
+ }
+
+ return page_table.MapPhysicalMemory(addr, size);
+}
+
+/// Unmaps memory previously mapped via MapPhysicalMemory
+Result UnmapPhysicalMemory(Core::System& system, VAddr addr, u64 size) {
+ LOG_DEBUG(Kernel_SVC, "called, addr=0x{:016X}, size=0x{:X}", addr, size);
+
+ if (!Common::Is4KBAligned(addr)) {
+ LOG_ERROR(Kernel_SVC, "Address is not aligned to 4KB, 0x{:016X}", addr);
+ return ResultInvalidAddress;
+ }
+
+ if (!Common::Is4KBAligned(size)) {
+ LOG_ERROR(Kernel_SVC, "Size is not aligned to 4KB, 0x{:X}", size);
+ return ResultInvalidSize;
+ }
+
+ if (size == 0) {
+ LOG_ERROR(Kernel_SVC, "Size is zero");
+ return ResultInvalidSize;
+ }
+
+ if (!(addr < addr + size)) {
+ LOG_ERROR(Kernel_SVC, "Size causes 64-bit overflow of address");
+ return ResultInvalidMemoryRegion;
+ }
+
+ KProcess* const current_process{GetCurrentProcessPointer(system.Kernel())};
+ auto& page_table{current_process->PageTable()};
+
+ if (current_process->GetSystemResourceSize() == 0) {
+ LOG_ERROR(Kernel_SVC, "System Resource Size is zero");
+ return ResultInvalidState;
+ }
+
+ if (!page_table.IsInsideAddressSpace(addr, size)) {
+ LOG_ERROR(Kernel_SVC,
+ "Address is not within the address space, addr=0x{:016X}, size=0x{:016X}", addr,
+ size);
+ return ResultInvalidMemoryRegion;
+ }
+
+ if (page_table.IsOutsideAliasRegion(addr, size)) {
+ LOG_ERROR(Kernel_SVC,
+ "Address is not within the alias region, addr=0x{:016X}, size=0x{:016X}", addr,
+ size);
+ return ResultInvalidMemoryRegion;
+ }
+
+ return page_table.UnmapPhysicalMemory(addr, size);
+}
+
+Result MapPhysicalMemoryUnsafe(Core::System& system, uint64_t address, uint64_t size) {
+ UNIMPLEMENTED();
+ R_THROW(ResultNotImplemented);
+}
+
+Result UnmapPhysicalMemoryUnsafe(Core::System& system, uint64_t address, uint64_t size) {
+ UNIMPLEMENTED();
+ R_THROW(ResultNotImplemented);
+}
+
+Result SetUnsafeLimit(Core::System& system, uint64_t limit) {
+ UNIMPLEMENTED();
+ R_THROW(ResultNotImplemented);
+}
+
+Result SetHeapSize64(Core::System& system, uint64_t* out_address, uint64_t size) {
+ R_RETURN(SetHeapSize(system, out_address, size));
+}
+
+Result MapPhysicalMemory64(Core::System& system, uint64_t address, uint64_t size) {
+ R_RETURN(MapPhysicalMemory(system, address, size));
+}
+
+Result UnmapPhysicalMemory64(Core::System& system, uint64_t address, uint64_t size) {
+ R_RETURN(UnmapPhysicalMemory(system, address, size));
+}
+
+Result MapPhysicalMemoryUnsafe64(Core::System& system, uint64_t address, uint64_t size) {
+ R_RETURN(MapPhysicalMemoryUnsafe(system, address, size));
+}
+
+Result UnmapPhysicalMemoryUnsafe64(Core::System& system, uint64_t address, uint64_t size) {
+ R_RETURN(UnmapPhysicalMemoryUnsafe(system, address, size));
+}
+
+Result SetUnsafeLimit64(Core::System& system, uint64_t limit) {
+ R_RETURN(SetUnsafeLimit(system, limit));
+}
+
+Result SetHeapSize64From32(Core::System& system, uintptr_t* out_address, uint32_t size) {
+ R_RETURN(SetHeapSize(system, out_address, size));
+}
+
+Result MapPhysicalMemory64From32(Core::System& system, uint32_t address, uint32_t size) {
+ R_RETURN(MapPhysicalMemory(system, address, size));
+}
+
+Result UnmapPhysicalMemory64From32(Core::System& system, uint32_t address, uint32_t size) {
+ R_RETURN(UnmapPhysicalMemory(system, address, size));
+}
+
+Result MapPhysicalMemoryUnsafe64From32(Core::System& system, uint32_t address, uint32_t size) {
+ R_RETURN(MapPhysicalMemoryUnsafe(system, address, size));
+}
+
+Result UnmapPhysicalMemoryUnsafe64From32(Core::System& system, uint32_t address, uint32_t size) {
+ R_RETURN(UnmapPhysicalMemoryUnsafe(system, address, size));
+}
+
+Result SetUnsafeLimit64From32(Core::System& system, uint32_t limit) {
+ R_RETURN(SetUnsafeLimit(system, limit));
+}
+
+} // 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..2f9bfcb52
--- /dev/null
+++ b/src/core/hle/kernel/svc/svc_port.cpp
@@ -0,0 +1,170 @@
+// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include "common/scope_exit.h"
+#include "core/core.h"
+#include "core/hle/kernel/k_client_port.h"
+#include "core/hle/kernel/k_client_session.h"
+#include "core/hle/kernel/k_object_name.h"
+#include "core/hle/kernel/k_port.h"
+#include "core/hle/kernel/k_process.h"
+#include "core/hle/kernel/svc.h"
+
+namespace Kernel::Svc {
+
+/// Connect to an OS service given the port name, returns the handle to the port to out
+Result ConnectToNamedPort(Core::System& system, Handle* out, VAddr port_name_address) {
+ auto& memory = system.Memory();
+ if (!memory.IsValidVirtualAddress(port_name_address)) {
+ LOG_ERROR(Kernel_SVC,
+ "Port Name Address is not a valid virtual address, port_name_address=0x{:016X}",
+ port_name_address);
+ return ResultNotFound;
+ }
+
+ static constexpr std::size_t PortNameMaxLength = 11;
+ // Read 1 char beyond the max allowed port name to detect names that are too long.
+ const std::string port_name = memory.ReadCString(port_name_address, PortNameMaxLength + 1);
+ if (port_name.size() > PortNameMaxLength) {
+ LOG_ERROR(Kernel_SVC, "Port name is too long, expected {} but got {}", PortNameMaxLength,
+ port_name.size());
+ return ResultOutOfRange;
+ }
+
+ LOG_TRACE(Kernel_SVC, "called port_name={}", port_name);
+
+ // Get the current handle table.
+ auto& kernel = system.Kernel();
+ auto& handle_table = GetCurrentProcess(kernel).GetHandleTable();
+
+ // Find the client port.
+ auto port = kernel.CreateNamedServicePort(port_name);
+ if (!port) {
+ LOG_ERROR(Kernel_SVC, "tried to connect to unknown port: {}", port_name);
+ return ResultNotFound;
+ }
+
+ // Reserve a handle for the port.
+ // NOTE: Nintendo really does write directly to the output handle here.
+ R_TRY(handle_table.Reserve(out));
+ auto handle_guard = SCOPE_GUARD({ handle_table.Unreserve(*out); });
+
+ // Create a session.
+ KClientSession* session{};
+ R_TRY(port->CreateSession(std::addressof(session)));
+
+ kernel.RegisterNamedServiceHandler(port_name, &port->GetParent()->GetServerPort());
+
+ // Register the session in the table, close the extra reference.
+ handle_table.Register(*out, session);
+ session->Close();
+
+ // We succeeded.
+ handle_guard.Cancel();
+ return ResultSuccess;
+}
+
+Result CreatePort(Core::System& system, Handle* out_server, Handle* out_client,
+ int32_t max_sessions, bool is_light, uintptr_t name) {
+ UNIMPLEMENTED();
+ R_THROW(ResultNotImplemented);
+}
+
+Result ConnectToPort(Core::System& system, Handle* out_handle, Handle port) {
+ UNIMPLEMENTED();
+ R_THROW(ResultNotImplemented);
+}
+
+Result ManageNamedPort(Core::System& system, Handle* out_server_handle, uint64_t user_name,
+ int32_t max_sessions) {
+ // Copy the provided name from user memory to kernel memory.
+ std::array<char, KObjectName::NameLengthMax> name{};
+ system.Memory().ReadBlock(user_name, name.data(), sizeof(name));
+
+ // Validate that sessions and name are valid.
+ R_UNLESS(max_sessions >= 0, ResultOutOfRange);
+ R_UNLESS(name[sizeof(name) - 1] == '\x00', ResultOutOfRange);
+
+ if (max_sessions > 0) {
+ // Get the current handle table.
+ auto& handle_table = GetCurrentProcess(system.Kernel()).GetHandleTable();
+
+ // Create a new port.
+ KPort* port = KPort::Create(system.Kernel());
+ R_UNLESS(port != nullptr, ResultOutOfResource);
+
+ // Initialize the new port.
+ port->Initialize(max_sessions, false, "");
+
+ // Register the port.
+ KPort::Register(system.Kernel(), port);
+
+ // Ensure that our only reference to the port is in the handle table when we're done.
+ SCOPE_EXIT({
+ port->GetClientPort().Close();
+ port->GetServerPort().Close();
+ });
+
+ // Register the handle in the table.
+ R_TRY(handle_table.Add(out_server_handle, std::addressof(port->GetServerPort())));
+ ON_RESULT_FAILURE {
+ handle_table.Remove(*out_server_handle);
+ };
+
+ // Create a new object name.
+ R_TRY(KObjectName::NewFromName(system.Kernel(), std::addressof(port->GetClientPort()),
+ name.data()));
+ } else /* if (max_sessions == 0) */ {
+ // Ensure that this else case is correct.
+ ASSERT(max_sessions == 0);
+
+ // If we're closing, there's no server handle.
+ *out_server_handle = InvalidHandle;
+
+ // Delete the object.
+ R_TRY(KObjectName::Delete<KClientPort>(system.Kernel(), name.data()));
+ }
+
+ R_SUCCEED();
+}
+
+Result ConnectToNamedPort64(Core::System& system, Handle* out_handle, uint64_t name) {
+ R_RETURN(ConnectToNamedPort(system, out_handle, name));
+}
+
+Result CreatePort64(Core::System& system, Handle* out_server_handle, Handle* out_client_handle,
+ int32_t max_sessions, bool is_light, uint64_t name) {
+ R_RETURN(
+ CreatePort(system, out_server_handle, out_client_handle, max_sessions, is_light, name));
+}
+
+Result ManageNamedPort64(Core::System& system, Handle* out_server_handle, uint64_t name,
+ int32_t max_sessions) {
+ R_RETURN(ManageNamedPort(system, out_server_handle, name, max_sessions));
+}
+
+Result ConnectToPort64(Core::System& system, Handle* out_handle, Handle port) {
+ R_RETURN(ConnectToPort(system, out_handle, port));
+}
+
+Result ConnectToNamedPort64From32(Core::System& system, Handle* out_handle, uint32_t name) {
+ R_RETURN(ConnectToNamedPort(system, out_handle, name));
+}
+
+Result CreatePort64From32(Core::System& system, Handle* out_server_handle,
+ Handle* out_client_handle, int32_t max_sessions, bool is_light,
+ uint32_t name) {
+ R_RETURN(
+ CreatePort(system, out_server_handle, out_client_handle, max_sessions, is_light, name));
+}
+
+Result ManageNamedPort64From32(Core::System& system, Handle* out_server_handle, uint32_t name,
+ int32_t max_sessions) {
+ R_RETURN(ManageNamedPort(system, out_server_handle, name, max_sessions));
+}
+
+Result ConnectToPort64From32(Core::System& system, Handle* out_handle, Handle port) {
+ R_RETURN(ConnectToPort(system, out_handle, port));
+}
+
+} // 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..f605a0317
--- /dev/null
+++ b/src/core/hle/kernel/svc/svc_power_management.cpp
@@ -0,0 +1,21 @@
+// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include "core/hle/kernel/svc.h"
+#include "core/hle/kernel/svc_results.h"
+
+namespace Kernel::Svc {
+
+void SleepSystem(Core::System& system) {
+ UNIMPLEMENTED();
+}
+
+void SleepSystem64(Core::System& system) {
+ return SleepSystem(system);
+}
+
+void SleepSystem64From32(Core::System& system) {
+ return SleepSystem(system);
+}
+
+} // 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..c35d2be76
--- /dev/null
+++ b/src/core/hle/kernel/svc/svc_process.cpp
@@ -0,0 +1,194 @@
+// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include "core/core.h"
+#include "core/hle/kernel/k_process.h"
+#include "core/hle/kernel/svc.h"
+
+namespace Kernel::Svc {
+
+/// Exits the current process
+void ExitProcess(Core::System& system) {
+ auto* current_process = GetCurrentProcessPointer(system.Kernel());
+
+ LOG_INFO(Kernel_SVC, "Process {} exiting", current_process->GetProcessID());
+ ASSERT_MSG(current_process->GetState() == KProcess::State::Running,
+ "Process has already exited");
+
+ system.Exit();
+}
+
+/// Gets the ID of the specified process or a specified thread's owning process.
+Result GetProcessId(Core::System& system, u64* out_process_id, Handle handle) {
+ LOG_DEBUG(Kernel_SVC, "called handle=0x{:08X}", handle);
+
+ // Get the object from the handle table.
+ KScopedAutoObject obj = GetCurrentProcess(system.Kernel())
+ .GetHandleTable()
+ .GetObject<KAutoObject>(static_cast<Handle>(handle));
+ R_UNLESS(obj.IsNotNull(), ResultInvalidHandle);
+
+ // Get the process from the object.
+ KProcess* process = nullptr;
+ if (KProcess* p = obj->DynamicCast<KProcess*>(); p != nullptr) {
+ // The object is a process, so we can use it directly.
+ process = p;
+ } else if (KThread* t = obj->DynamicCast<KThread*>(); t != nullptr) {
+ // The object is a thread, so we want to use its parent.
+ process = reinterpret_cast<KThread*>(obj.GetPointerUnsafe())->GetOwnerProcess();
+ } else {
+ // TODO(bunnei): This should also handle debug objects before returning.
+ UNIMPLEMENTED_MSG("Debug objects not implemented");
+ }
+
+ // Make sure the target process exists.
+ R_UNLESS(process != nullptr, ResultInvalidHandle);
+
+ // Get the process id.
+ *out_process_id = process->GetId();
+
+ return ResultSuccess;
+}
+
+Result GetProcessList(Core::System& system, s32* out_num_processes, VAddr out_process_ids,
+ int32_t out_process_ids_size) {
+ LOG_DEBUG(Kernel_SVC, "called. out_process_ids=0x{:016X}, out_process_ids_size={}",
+ out_process_ids, out_process_ids_size);
+
+ // If the supplied size is negative or greater than INT32_MAX / sizeof(u64), bail.
+ if ((out_process_ids_size & 0xF0000000) != 0) {
+ LOG_ERROR(Kernel_SVC,
+ "Supplied size outside [0, 0x0FFFFFFF] range. out_process_ids_size={}",
+ out_process_ids_size);
+ return ResultOutOfRange;
+ }
+
+ auto& kernel = system.Kernel();
+ const auto total_copy_size = out_process_ids_size * sizeof(u64);
+
+ if (out_process_ids_size > 0 && !GetCurrentProcess(kernel).PageTable().IsInsideAddressSpace(
+ out_process_ids, total_copy_size)) {
+ LOG_ERROR(Kernel_SVC, "Address range outside address space. begin=0x{:016X}, end=0x{:016X}",
+ out_process_ids, out_process_ids + total_copy_size);
+ return ResultInvalidCurrentMemory;
+ }
+
+ auto& memory = system.Memory();
+ const auto& process_list = kernel.GetProcessList();
+ const auto num_processes = process_list.size();
+ const auto copy_amount =
+ std::min(static_cast<std::size_t>(out_process_ids_size), num_processes);
+
+ for (std::size_t i = 0; i < copy_amount; ++i) {
+ memory.Write64(out_process_ids, process_list[i]->GetProcessID());
+ out_process_ids += sizeof(u64);
+ }
+
+ *out_num_processes = static_cast<u32>(num_processes);
+ return ResultSuccess;
+}
+
+Result GetProcessInfo(Core::System& system, s64* out, Handle process_handle,
+ ProcessInfoType info_type) {
+ LOG_DEBUG(Kernel_SVC, "called, handle=0x{:08X}, type=0x{:X}", process_handle, info_type);
+
+ const auto& handle_table = GetCurrentProcess(system.Kernel()).GetHandleTable();
+ KScopedAutoObject process = handle_table.GetObject<KProcess>(process_handle);
+ if (process.IsNull()) {
+ LOG_ERROR(Kernel_SVC, "Process handle does not exist, process_handle=0x{:08X}",
+ process_handle);
+ return ResultInvalidHandle;
+ }
+
+ if (info_type != ProcessInfoType::ProcessState) {
+ LOG_ERROR(Kernel_SVC, "Expected info_type to be ProcessState but got {} instead",
+ info_type);
+ return ResultInvalidEnumValue;
+ }
+
+ *out = static_cast<s64>(process->GetState());
+ return ResultSuccess;
+}
+
+Result CreateProcess(Core::System& system, Handle* out_handle, uint64_t parameters, uint64_t caps,
+ int32_t num_caps) {
+ UNIMPLEMENTED();
+ R_THROW(ResultNotImplemented);
+}
+
+Result StartProcess(Core::System& system, Handle process_handle, int32_t priority, int32_t core_id,
+ uint64_t main_thread_stack_size) {
+ UNIMPLEMENTED();
+ R_THROW(ResultNotImplemented);
+}
+
+Result TerminateProcess(Core::System& system, Handle process_handle) {
+ UNIMPLEMENTED();
+ R_THROW(ResultNotImplemented);
+}
+
+void ExitProcess64(Core::System& system) {
+ ExitProcess(system);
+}
+
+Result GetProcessId64(Core::System& system, uint64_t* out_process_id, Handle process_handle) {
+ R_RETURN(GetProcessId(system, out_process_id, process_handle));
+}
+
+Result GetProcessList64(Core::System& system, int32_t* out_num_processes, uint64_t out_process_ids,
+ int32_t max_out_count) {
+ R_RETURN(GetProcessList(system, out_num_processes, out_process_ids, max_out_count));
+}
+
+Result CreateProcess64(Core::System& system, Handle* out_handle, uint64_t parameters, uint64_t caps,
+ int32_t num_caps) {
+ R_RETURN(CreateProcess(system, out_handle, parameters, caps, num_caps));
+}
+
+Result StartProcess64(Core::System& system, Handle process_handle, int32_t priority,
+ int32_t core_id, uint64_t main_thread_stack_size) {
+ R_RETURN(StartProcess(system, process_handle, priority, core_id, main_thread_stack_size));
+}
+
+Result TerminateProcess64(Core::System& system, Handle process_handle) {
+ R_RETURN(TerminateProcess(system, process_handle));
+}
+
+Result GetProcessInfo64(Core::System& system, int64_t* out_info, Handle process_handle,
+ ProcessInfoType info_type) {
+ R_RETURN(GetProcessInfo(system, out_info, process_handle, info_type));
+}
+
+void ExitProcess64From32(Core::System& system) {
+ ExitProcess(system);
+}
+
+Result GetProcessId64From32(Core::System& system, uint64_t* out_process_id, Handle process_handle) {
+ R_RETURN(GetProcessId(system, out_process_id, process_handle));
+}
+
+Result GetProcessList64From32(Core::System& system, int32_t* out_num_processes,
+ uint32_t out_process_ids, int32_t max_out_count) {
+ R_RETURN(GetProcessList(system, out_num_processes, out_process_ids, max_out_count));
+}
+
+Result CreateProcess64From32(Core::System& system, Handle* out_handle, uint32_t parameters,
+ uint32_t caps, int32_t num_caps) {
+ R_RETURN(CreateProcess(system, out_handle, parameters, caps, num_caps));
+}
+
+Result StartProcess64From32(Core::System& system, Handle process_handle, int32_t priority,
+ int32_t core_id, uint64_t main_thread_stack_size) {
+ R_RETURN(StartProcess(system, process_handle, priority, core_id, main_thread_stack_size));
+}
+
+Result TerminateProcess64From32(Core::System& system, Handle process_handle) {
+ R_RETURN(TerminateProcess(system, process_handle));
+}
+
+Result GetProcessInfo64From32(Core::System& system, int64_t* out_info, Handle process_handle,
+ ProcessInfoType info_type) {
+ R_RETURN(GetProcessInfo(system, out_info, process_handle, info_type));
+}
+
+} // 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..4dfd9e5bb
--- /dev/null
+++ b/src/core/hle/kernel/svc/svc_process_memory.cpp
@@ -0,0 +1,324 @@
+// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include "core/core.h"
+#include "core/hle/kernel/k_process.h"
+#include "core/hle/kernel/svc.h"
+
+namespace Kernel::Svc {
+namespace {
+
+constexpr bool IsValidAddressRange(VAddr address, u64 size) {
+ return address + size > address;
+}
+
+constexpr bool IsValidProcessMemoryPermission(Svc::MemoryPermission perm) {
+ switch (perm) {
+ case Svc::MemoryPermission::None:
+ case Svc::MemoryPermission::Read:
+ case Svc::MemoryPermission::ReadWrite:
+ case Svc::MemoryPermission::ReadExecute:
+ return true;
+ default:
+ return false;
+ }
+}
+
+} // namespace
+
+Result SetProcessMemoryPermission(Core::System& system, Handle process_handle, VAddr address,
+ u64 size, Svc::MemoryPermission perm) {
+ LOG_TRACE(Kernel_SVC,
+ "called, process_handle=0x{:X}, addr=0x{:X}, size=0x{:X}, permissions=0x{:08X}",
+ process_handle, address, size, perm);
+
+ // Validate the address/size.
+ R_UNLESS(Common::IsAligned(address, PageSize), ResultInvalidAddress);
+ R_UNLESS(Common::IsAligned(size, PageSize), ResultInvalidSize);
+ R_UNLESS(size > 0, ResultInvalidSize);
+ R_UNLESS((address < address + size), ResultInvalidCurrentMemory);
+ R_UNLESS(address == static_cast<uintptr_t>(address), ResultInvalidCurrentMemory);
+ R_UNLESS(size == static_cast<size_t>(size), ResultInvalidCurrentMemory);
+
+ // Validate the memory permission.
+ R_UNLESS(IsValidProcessMemoryPermission(perm), ResultInvalidNewMemoryPermission);
+
+ // Get the process from its handle.
+ KScopedAutoObject process =
+ GetCurrentProcess(system.Kernel()).GetHandleTable().GetObject<KProcess>(process_handle);
+ R_UNLESS(process.IsNotNull(), ResultInvalidHandle);
+
+ // Validate that the address is in range.
+ auto& page_table = process->PageTable();
+ R_UNLESS(page_table.Contains(address, size), ResultInvalidCurrentMemory);
+
+ // Set the memory permission.
+ return page_table.SetProcessMemoryPermission(address, size, perm);
+}
+
+Result MapProcessMemory(Core::System& system, VAddr dst_address, Handle process_handle,
+ VAddr src_address, u64 size) {
+ LOG_TRACE(Kernel_SVC,
+ "called, dst_address=0x{:X}, process_handle=0x{:X}, src_address=0x{:X}, size=0x{:X}",
+ dst_address, process_handle, src_address, size);
+
+ // Validate the address/size.
+ R_UNLESS(Common::IsAligned(dst_address, PageSize), ResultInvalidAddress);
+ R_UNLESS(Common::IsAligned(src_address, PageSize), ResultInvalidAddress);
+ R_UNLESS(Common::IsAligned(size, PageSize), ResultInvalidSize);
+ R_UNLESS(size > 0, ResultInvalidSize);
+ R_UNLESS((dst_address < dst_address + size), ResultInvalidCurrentMemory);
+ R_UNLESS((src_address < src_address + size), ResultInvalidCurrentMemory);
+
+ // Get the processes.
+ KProcess* dst_process = GetCurrentProcessPointer(system.Kernel());
+ KScopedAutoObject src_process =
+ dst_process->GetHandleTable().GetObjectWithoutPseudoHandle<KProcess>(process_handle);
+ R_UNLESS(src_process.IsNotNull(), ResultInvalidHandle);
+
+ // Get the page tables.
+ auto& dst_pt = dst_process->PageTable();
+ auto& src_pt = src_process->PageTable();
+
+ // Validate that the mapping is in range.
+ R_UNLESS(src_pt.Contains(src_address, size), ResultInvalidCurrentMemory);
+ R_UNLESS(dst_pt.CanContain(dst_address, size, KMemoryState::SharedCode),
+ ResultInvalidMemoryRegion);
+
+ // Create a new page group.
+ KPageGroup pg{system.Kernel(), dst_pt.GetBlockInfoManager()};
+ R_TRY(src_pt.MakeAndOpenPageGroup(
+ std::addressof(pg), src_address, size / PageSize, KMemoryState::FlagCanMapProcess,
+ KMemoryState::FlagCanMapProcess, KMemoryPermission::None, KMemoryPermission::None,
+ KMemoryAttribute::All, KMemoryAttribute::None));
+
+ // Map the group.
+ R_TRY(dst_pt.MapPageGroup(dst_address, pg, KMemoryState::SharedCode,
+ KMemoryPermission::UserReadWrite));
+
+ return ResultSuccess;
+}
+
+Result UnmapProcessMemory(Core::System& system, VAddr dst_address, Handle process_handle,
+ VAddr src_address, u64 size) {
+ LOG_TRACE(Kernel_SVC,
+ "called, dst_address=0x{:X}, process_handle=0x{:X}, src_address=0x{:X}, size=0x{:X}",
+ dst_address, process_handle, src_address, size);
+
+ // Validate the address/size.
+ R_UNLESS(Common::IsAligned(dst_address, PageSize), ResultInvalidAddress);
+ R_UNLESS(Common::IsAligned(src_address, PageSize), ResultInvalidAddress);
+ R_UNLESS(Common::IsAligned(size, PageSize), ResultInvalidSize);
+ R_UNLESS(size > 0, ResultInvalidSize);
+ R_UNLESS((dst_address < dst_address + size), ResultInvalidCurrentMemory);
+ R_UNLESS((src_address < src_address + size), ResultInvalidCurrentMemory);
+
+ // Get the processes.
+ KProcess* dst_process = GetCurrentProcessPointer(system.Kernel());
+ KScopedAutoObject src_process =
+ dst_process->GetHandleTable().GetObjectWithoutPseudoHandle<KProcess>(process_handle);
+ R_UNLESS(src_process.IsNotNull(), ResultInvalidHandle);
+
+ // Get the page tables.
+ auto& dst_pt = dst_process->PageTable();
+ auto& src_pt = src_process->PageTable();
+
+ // Validate that the mapping is in range.
+ R_UNLESS(src_pt.Contains(src_address, size), ResultInvalidCurrentMemory);
+ R_UNLESS(dst_pt.CanContain(dst_address, size, KMemoryState::SharedCode),
+ ResultInvalidMemoryRegion);
+
+ // Unmap the memory.
+ R_TRY(dst_pt.UnmapProcessMemory(dst_address, size, src_pt, src_address));
+
+ return ResultSuccess;
+}
+
+Result MapProcessCodeMemory(Core::System& system, Handle process_handle, u64 dst_address,
+ u64 src_address, u64 size) {
+ LOG_DEBUG(Kernel_SVC,
+ "called. process_handle=0x{:08X}, dst_address=0x{:016X}, "
+ "src_address=0x{:016X}, size=0x{:016X}",
+ process_handle, dst_address, src_address, size);
+
+ if (!Common::Is4KBAligned(src_address)) {
+ LOG_ERROR(Kernel_SVC, "src_address is not page-aligned (src_address=0x{:016X}).",
+ src_address);
+ return ResultInvalidAddress;
+ }
+
+ if (!Common::Is4KBAligned(dst_address)) {
+ LOG_ERROR(Kernel_SVC, "dst_address is not page-aligned (dst_address=0x{:016X}).",
+ dst_address);
+ return ResultInvalidAddress;
+ }
+
+ if (size == 0 || !Common::Is4KBAligned(size)) {
+ LOG_ERROR(Kernel_SVC, "Size is zero or not page-aligned (size=0x{:016X})", size);
+ return ResultInvalidSize;
+ }
+
+ if (!IsValidAddressRange(dst_address, size)) {
+ LOG_ERROR(Kernel_SVC,
+ "Destination address range overflows the address space (dst_address=0x{:016X}, "
+ "size=0x{:016X}).",
+ dst_address, size);
+ return ResultInvalidCurrentMemory;
+ }
+
+ if (!IsValidAddressRange(src_address, size)) {
+ LOG_ERROR(Kernel_SVC,
+ "Source address range overflows the address space (src_address=0x{:016X}, "
+ "size=0x{:016X}).",
+ src_address, size);
+ return ResultInvalidCurrentMemory;
+ }
+
+ const auto& handle_table = GetCurrentProcess(system.Kernel()).GetHandleTable();
+ KScopedAutoObject process = handle_table.GetObject<KProcess>(process_handle);
+ if (process.IsNull()) {
+ LOG_ERROR(Kernel_SVC, "Invalid process handle specified (handle=0x{:08X}).",
+ process_handle);
+ return ResultInvalidHandle;
+ }
+
+ auto& page_table = process->PageTable();
+ if (!page_table.IsInsideAddressSpace(src_address, size)) {
+ LOG_ERROR(Kernel_SVC,
+ "Source address range is not within the address space (src_address=0x{:016X}, "
+ "size=0x{:016X}).",
+ src_address, size);
+ return ResultInvalidCurrentMemory;
+ }
+
+ if (!page_table.IsInsideASLRRegion(dst_address, size)) {
+ LOG_ERROR(Kernel_SVC,
+ "Destination address range is not within the ASLR region (dst_address=0x{:016X}, "
+ "size=0x{:016X}).",
+ dst_address, size);
+ return ResultInvalidMemoryRegion;
+ }
+
+ return page_table.MapCodeMemory(dst_address, src_address, size);
+}
+
+Result UnmapProcessCodeMemory(Core::System& system, Handle process_handle, u64 dst_address,
+ u64 src_address, u64 size) {
+ LOG_DEBUG(Kernel_SVC,
+ "called. process_handle=0x{:08X}, dst_address=0x{:016X}, src_address=0x{:016X}, "
+ "size=0x{:016X}",
+ process_handle, dst_address, src_address, size);
+
+ if (!Common::Is4KBAligned(dst_address)) {
+ LOG_ERROR(Kernel_SVC, "dst_address is not page-aligned (dst_address=0x{:016X}).",
+ dst_address);
+ return ResultInvalidAddress;
+ }
+
+ if (!Common::Is4KBAligned(src_address)) {
+ LOG_ERROR(Kernel_SVC, "src_address is not page-aligned (src_address=0x{:016X}).",
+ src_address);
+ return ResultInvalidAddress;
+ }
+
+ if (size == 0 || !Common::Is4KBAligned(size)) {
+ LOG_ERROR(Kernel_SVC, "Size is zero or not page-aligned (size=0x{:016X}).", size);
+ return ResultInvalidSize;
+ }
+
+ if (!IsValidAddressRange(dst_address, size)) {
+ LOG_ERROR(Kernel_SVC,
+ "Destination address range overflows the address space (dst_address=0x{:016X}, "
+ "size=0x{:016X}).",
+ dst_address, size);
+ return ResultInvalidCurrentMemory;
+ }
+
+ if (!IsValidAddressRange(src_address, size)) {
+ LOG_ERROR(Kernel_SVC,
+ "Source address range overflows the address space (src_address=0x{:016X}, "
+ "size=0x{:016X}).",
+ src_address, size);
+ return ResultInvalidCurrentMemory;
+ }
+
+ const auto& handle_table = GetCurrentProcess(system.Kernel()).GetHandleTable();
+ KScopedAutoObject process = handle_table.GetObject<KProcess>(process_handle);
+ if (process.IsNull()) {
+ LOG_ERROR(Kernel_SVC, "Invalid process handle specified (handle=0x{:08X}).",
+ process_handle);
+ return ResultInvalidHandle;
+ }
+
+ auto& page_table = process->PageTable();
+ if (!page_table.IsInsideAddressSpace(src_address, size)) {
+ LOG_ERROR(Kernel_SVC,
+ "Source address range is not within the address space (src_address=0x{:016X}, "
+ "size=0x{:016X}).",
+ src_address, size);
+ return ResultInvalidCurrentMemory;
+ }
+
+ if (!page_table.IsInsideASLRRegion(dst_address, size)) {
+ LOG_ERROR(Kernel_SVC,
+ "Destination address range is not within the ASLR region (dst_address=0x{:016X}, "
+ "size=0x{:016X}).",
+ dst_address, size);
+ return ResultInvalidMemoryRegion;
+ }
+
+ return page_table.UnmapCodeMemory(dst_address, src_address, size,
+ KPageTable::ICacheInvalidationStrategy::InvalidateAll);
+}
+
+Result SetProcessMemoryPermission64(Core::System& system, Handle process_handle, uint64_t address,
+ uint64_t size, MemoryPermission perm) {
+ R_RETURN(SetProcessMemoryPermission(system, process_handle, address, size, perm));
+}
+
+Result MapProcessMemory64(Core::System& system, uint64_t dst_address, Handle process_handle,
+ uint64_t src_address, uint64_t size) {
+ R_RETURN(MapProcessMemory(system, dst_address, process_handle, src_address, size));
+}
+
+Result UnmapProcessMemory64(Core::System& system, uint64_t dst_address, Handle process_handle,
+ uint64_t src_address, uint64_t size) {
+ R_RETURN(UnmapProcessMemory(system, dst_address, process_handle, src_address, size));
+}
+
+Result MapProcessCodeMemory64(Core::System& system, Handle process_handle, uint64_t dst_address,
+ uint64_t src_address, uint64_t size) {
+ R_RETURN(MapProcessCodeMemory(system, process_handle, dst_address, src_address, size));
+}
+
+Result UnmapProcessCodeMemory64(Core::System& system, Handle process_handle, uint64_t dst_address,
+ uint64_t src_address, uint64_t size) {
+ R_RETURN(UnmapProcessCodeMemory(system, process_handle, dst_address, src_address, size));
+}
+
+Result SetProcessMemoryPermission64From32(Core::System& system, Handle process_handle,
+ uint64_t address, uint64_t size, MemoryPermission perm) {
+ R_RETURN(SetProcessMemoryPermission(system, process_handle, address, size, perm));
+}
+
+Result MapProcessMemory64From32(Core::System& system, uint32_t dst_address, Handle process_handle,
+ uint64_t src_address, uint32_t size) {
+ R_RETURN(MapProcessMemory(system, dst_address, process_handle, src_address, size));
+}
+
+Result UnmapProcessMemory64From32(Core::System& system, uint32_t dst_address, Handle process_handle,
+ uint64_t src_address, uint32_t size) {
+ R_RETURN(UnmapProcessMemory(system, dst_address, process_handle, src_address, size));
+}
+
+Result MapProcessCodeMemory64From32(Core::System& system, Handle process_handle,
+ uint64_t dst_address, uint64_t src_address, uint64_t size) {
+ R_RETURN(MapProcessCodeMemory(system, process_handle, dst_address, src_address, size));
+}
+
+Result UnmapProcessCodeMemory64From32(Core::System& system, Handle process_handle,
+ uint64_t dst_address, uint64_t src_address, uint64_t size) {
+ R_RETURN(UnmapProcessCodeMemory(system, process_handle, dst_address, src_address, size));
+}
+
+} // 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..7602ce6c0
--- /dev/null
+++ b/src/core/hle/kernel/svc/svc_processor.cpp
@@ -0,0 +1,25 @@
+// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include "common/logging/log.h"
+#include "core/core.h"
+#include "core/hle/kernel/physical_core.h"
+#include "core/hle/kernel/svc.h"
+
+namespace Kernel::Svc {
+
+/// Get which CPU core is executing the current thread
+int32_t GetCurrentProcessorNumber(Core::System& system) {
+ LOG_TRACE(Kernel_SVC, "called");
+ return static_cast<int32_t>(system.CurrentPhysicalCore().CoreIndex());
+}
+
+int32_t GetCurrentProcessorNumber64(Core::System& system) {
+ return GetCurrentProcessorNumber(system);
+}
+
+int32_t GetCurrentProcessorNumber64From32(Core::System& system) {
+ return GetCurrentProcessorNumber(system);
+}
+
+} // 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..ee75ad370
--- /dev/null
+++ b/src/core/hle/kernel/svc/svc_query_memory.cpp
@@ -0,0 +1,65 @@
+// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include "core/core.h"
+#include "core/hle/kernel/k_process.h"
+#include "core/hle/kernel/svc.h"
+
+namespace Kernel::Svc {
+
+Result QueryMemory(Core::System& system, uint64_t out_memory_info, PageInfo* out_page_info,
+ VAddr query_address) {
+ LOG_TRACE(Kernel_SVC,
+ "called, out_memory_info=0x{:016X}, "
+ "query_address=0x{:016X}",
+ out_memory_info, query_address);
+
+ // Query memory is just QueryProcessMemory on the current process.
+ return QueryProcessMemory(system, out_memory_info, out_page_info, CurrentProcess,
+ query_address);
+}
+
+Result QueryProcessMemory(Core::System& system, uint64_t out_memory_info, PageInfo* out_page_info,
+ Handle process_handle, uint64_t address) {
+ LOG_TRACE(Kernel_SVC, "called process=0x{:08X} address={:X}", process_handle, address);
+ const auto& handle_table = GetCurrentProcess(system.Kernel()).GetHandleTable();
+ KScopedAutoObject process = handle_table.GetObject<KProcess>(process_handle);
+ if (process.IsNull()) {
+ LOG_ERROR(Kernel_SVC, "Process handle does not exist, process_handle=0x{:08X}",
+ process_handle);
+ return ResultInvalidHandle;
+ }
+
+ auto& memory{system.Memory()};
+ const auto memory_info{process->PageTable().QueryInfo(address).GetSvcMemoryInfo()};
+
+ memory.WriteBlock(out_memory_info, &memory_info, sizeof(memory_info));
+
+ //! This is supposed to be part of the QueryInfo call.
+ *out_page_info = {};
+
+ R_SUCCEED();
+}
+
+Result QueryMemory64(Core::System& system, uint64_t out_memory_info, PageInfo* out_page_info,
+ uint64_t address) {
+ R_RETURN(QueryMemory(system, out_memory_info, out_page_info, address));
+}
+
+Result QueryProcessMemory64(Core::System& system, uint64_t out_memory_info, PageInfo* out_page_info,
+ Handle process_handle, uint64_t address) {
+ R_RETURN(QueryProcessMemory(system, out_memory_info, out_page_info, process_handle, address));
+}
+
+Result QueryMemory64From32(Core::System& system, uint32_t out_memory_info, PageInfo* out_page_info,
+ uint32_t address) {
+ R_RETURN(QueryMemory(system, out_memory_info, out_page_info, address));
+}
+
+Result QueryProcessMemory64From32(Core::System& system, uint32_t out_memory_info,
+ PageInfo* out_page_info, Handle process_handle,
+ uint64_t address) {
+ R_RETURN(QueryProcessMemory(system, out_memory_info, out_page_info, process_handle, address));
+}
+
+} // 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..b883e6618
--- /dev/null
+++ b/src/core/hle/kernel/svc/svc_register.cpp
@@ -0,0 +1,27 @@
+// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include "core/hle/kernel/svc.h"
+#include "core/hle/kernel/svc_results.h"
+
+namespace Kernel::Svc {
+
+Result ReadWriteRegister(Core::System& system, uint32_t* out, uint64_t address, uint32_t mask,
+ uint32_t value) {
+ *out = 0;
+
+ UNIMPLEMENTED();
+ R_THROW(ResultNotImplemented);
+}
+
+Result ReadWriteRegister64(Core::System& system, uint32_t* out_value, uint64_t address,
+ uint32_t mask, uint32_t value) {
+ R_RETURN(ReadWriteRegister(system, out_value, address, mask, value));
+}
+
+Result ReadWriteRegister64From32(Core::System& system, uint32_t* out_value, uint64_t address,
+ uint32_t mask, uint32_t value) {
+ R_RETURN(ReadWriteRegister(system, out_value, address, mask, value));
+}
+
+} // 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..88166299e
--- /dev/null
+++ b/src/core/hle/kernel/svc/svc_resource_limit.cpp
@@ -0,0 +1,149 @@
+// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include "common/scope_exit.h"
+#include "core/core.h"
+#include "core/hle/kernel/k_process.h"
+#include "core/hle/kernel/k_resource_limit.h"
+#include "core/hle/kernel/svc.h"
+
+namespace Kernel::Svc {
+
+Result CreateResourceLimit(Core::System& system, Handle* out_handle) {
+ LOG_DEBUG(Kernel_SVC, "called");
+
+ // Create a new resource limit.
+ auto& kernel = system.Kernel();
+ KResourceLimit* resource_limit = KResourceLimit::Create(kernel);
+ R_UNLESS(resource_limit != nullptr, ResultOutOfResource);
+
+ // Ensure we don't leak a reference to the limit.
+ SCOPE_EXIT({ resource_limit->Close(); });
+
+ // Initialize the resource limit.
+ resource_limit->Initialize(&system.CoreTiming());
+
+ // Register the limit.
+ KResourceLimit::Register(kernel, resource_limit);
+
+ // Add the limit to the handle table.
+ R_TRY(GetCurrentProcess(kernel).GetHandleTable().Add(out_handle, resource_limit));
+
+ return ResultSuccess;
+}
+
+Result GetResourceLimitLimitValue(Core::System& system, s64* out_limit_value,
+ Handle resource_limit_handle, LimitableResource which) {
+ LOG_DEBUG(Kernel_SVC, "called, resource_limit_handle={:08X}, which={}", resource_limit_handle,
+ which);
+
+ // Validate the resource.
+ R_UNLESS(IsValidResourceType(which), ResultInvalidEnumValue);
+
+ // Get the resource limit.
+ KScopedAutoObject resource_limit = GetCurrentProcess(system.Kernel())
+ .GetHandleTable()
+ .GetObject<KResourceLimit>(resource_limit_handle);
+ R_UNLESS(resource_limit.IsNotNull(), ResultInvalidHandle);
+
+ // Get the limit value.
+ *out_limit_value = resource_limit->GetLimitValue(which);
+
+ return ResultSuccess;
+}
+
+Result GetResourceLimitCurrentValue(Core::System& system, s64* out_current_value,
+ Handle resource_limit_handle, LimitableResource which) {
+ LOG_DEBUG(Kernel_SVC, "called, resource_limit_handle={:08X}, which={}", resource_limit_handle,
+ which);
+
+ // Validate the resource.
+ R_UNLESS(IsValidResourceType(which), ResultInvalidEnumValue);
+
+ // Get the resource limit.
+ KScopedAutoObject resource_limit = GetCurrentProcess(system.Kernel())
+ .GetHandleTable()
+ .GetObject<KResourceLimit>(resource_limit_handle);
+ R_UNLESS(resource_limit.IsNotNull(), ResultInvalidHandle);
+
+ // Get the current value.
+ *out_current_value = resource_limit->GetCurrentValue(which);
+
+ return ResultSuccess;
+}
+
+Result SetResourceLimitLimitValue(Core::System& system, Handle resource_limit_handle,
+ LimitableResource which, s64 limit_value) {
+ LOG_DEBUG(Kernel_SVC, "called, resource_limit_handle={:08X}, which={}, limit_value={}",
+ resource_limit_handle, which, limit_value);
+
+ // Validate the resource.
+ R_UNLESS(IsValidResourceType(which), ResultInvalidEnumValue);
+
+ // Get the resource limit.
+ KScopedAutoObject resource_limit = GetCurrentProcess(system.Kernel())
+ .GetHandleTable()
+ .GetObject<KResourceLimit>(resource_limit_handle);
+ R_UNLESS(resource_limit.IsNotNull(), ResultInvalidHandle);
+
+ // Set the limit value.
+ R_TRY(resource_limit->SetLimitValue(which, limit_value));
+
+ return ResultSuccess;
+}
+
+Result GetResourceLimitPeakValue(Core::System& system, int64_t* out_peak_value,
+ Handle resource_limit_handle, LimitableResource which) {
+ UNIMPLEMENTED();
+ R_THROW(ResultNotImplemented);
+}
+
+Result GetResourceLimitLimitValue64(Core::System& system, int64_t* out_limit_value,
+ Handle resource_limit_handle, LimitableResource which) {
+ R_RETURN(GetResourceLimitLimitValue(system, out_limit_value, resource_limit_handle, which));
+}
+
+Result GetResourceLimitCurrentValue64(Core::System& system, int64_t* out_current_value,
+ Handle resource_limit_handle, LimitableResource which) {
+ R_RETURN(GetResourceLimitCurrentValue(system, out_current_value, resource_limit_handle, which));
+}
+
+Result GetResourceLimitPeakValue64(Core::System& system, int64_t* out_peak_value,
+ Handle resource_limit_handle, LimitableResource which) {
+ R_RETURN(GetResourceLimitPeakValue(system, out_peak_value, resource_limit_handle, which));
+}
+
+Result CreateResourceLimit64(Core::System& system, Handle* out_handle) {
+ R_RETURN(CreateResourceLimit(system, out_handle));
+}
+
+Result SetResourceLimitLimitValue64(Core::System& system, Handle resource_limit_handle,
+ LimitableResource which, int64_t limit_value) {
+ R_RETURN(SetResourceLimitLimitValue(system, resource_limit_handle, which, limit_value));
+}
+
+Result GetResourceLimitLimitValue64From32(Core::System& system, int64_t* out_limit_value,
+ Handle resource_limit_handle, LimitableResource which) {
+ R_RETURN(GetResourceLimitLimitValue(system, out_limit_value, resource_limit_handle, which));
+}
+
+Result GetResourceLimitCurrentValue64From32(Core::System& system, int64_t* out_current_value,
+ Handle resource_limit_handle, LimitableResource which) {
+ R_RETURN(GetResourceLimitCurrentValue(system, out_current_value, resource_limit_handle, which));
+}
+
+Result GetResourceLimitPeakValue64From32(Core::System& system, int64_t* out_peak_value,
+ Handle resource_limit_handle, LimitableResource which) {
+ R_RETURN(GetResourceLimitPeakValue(system, out_peak_value, resource_limit_handle, which));
+}
+
+Result CreateResourceLimit64From32(Core::System& system, Handle* out_handle) {
+ R_RETURN(CreateResourceLimit(system, out_handle));
+}
+
+Result SetResourceLimitLimitValue64From32(Core::System& system, Handle resource_limit_handle,
+ LimitableResource which, int64_t limit_value) {
+ R_RETURN(SetResourceLimitLimitValue(system, resource_limit_handle, which, limit_value));
+}
+
+} // 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..20f6ec643
--- /dev/null
+++ b/src/core/hle/kernel/svc/svc_secure_monitor_call.cpp
@@ -0,0 +1,53 @@
+// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include "core/core.h"
+#include "core/hle/kernel/physical_core.h"
+#include "core/hle/kernel/svc.h"
+
+namespace Kernel::Svc {
+
+void CallSecureMonitor(Core::System& system, lp64::SecureMonitorArguments* args) {
+ UNIMPLEMENTED();
+}
+
+void CallSecureMonitor64(Core::System& system, lp64::SecureMonitorArguments* args) {
+ CallSecureMonitor(system, args);
+}
+
+void CallSecureMonitor64From32(Core::System& system, ilp32::SecureMonitorArguments* args) {
+ // CallSecureMonitor64From32 is not supported.
+ UNIMPLEMENTED_MSG("CallSecureMonitor64From32");
+}
+
+// Custom ABI for CallSecureMonitor.
+
+void SvcWrap_CallSecureMonitor64(Core::System& system) {
+ auto& core = system.CurrentPhysicalCore().ArmInterface();
+ lp64::SecureMonitorArguments args{};
+ for (int i = 0; i < 8; i++) {
+ args.r[i] = core.GetReg(i);
+ }
+
+ CallSecureMonitor64(system, &args);
+
+ for (int i = 0; i < 8; i++) {
+ core.SetReg(i, args.r[i]);
+ }
+}
+
+void SvcWrap_CallSecureMonitor64From32(Core::System& system) {
+ auto& core = system.CurrentPhysicalCore().ArmInterface();
+ ilp32::SecureMonitorArguments args{};
+ for (int i = 0; i < 8; i++) {
+ args.r[i] = static_cast<u32>(core.GetReg(i));
+ }
+
+ CallSecureMonitor64From32(system, &args);
+
+ for (int i = 0; i < 8; i++) {
+ core.SetReg(i, args.r[i]);
+ }
+}
+
+} // 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..00fd1605e
--- /dev/null
+++ b/src/core/hle/kernel/svc/svc_session.cpp
@@ -0,0 +1,128 @@
+// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include "common/scope_exit.h"
+#include "core/core.h"
+#include "core/hle/kernel/k_process.h"
+#include "core/hle/kernel/k_scoped_resource_reservation.h"
+#include "core/hle/kernel/k_session.h"
+#include "core/hle/kernel/svc.h"
+
+namespace Kernel::Svc {
+namespace {
+
+template <typename T>
+Result CreateSession(Core::System& system, Handle* out_server, Handle* out_client, u64 name) {
+ auto& process = GetCurrentProcess(system.Kernel());
+ auto& handle_table = process.GetHandleTable();
+
+ // Declare the session we're going to allocate.
+ T* session;
+
+ // Reserve a new session from the process resource limit.
+ // FIXME: LimitableResource_SessionCountMax
+ KScopedResourceReservation session_reservation(&process, LimitableResource::SessionCountMax);
+ if (session_reservation.Succeeded()) {
+ session = T::Create(system.Kernel());
+ } else {
+ return ResultLimitReached;
+
+ // // We couldn't reserve a session. Check that we support dynamically expanding the
+ // // resource limit.
+ // R_UNLESS(process.GetResourceLimit() ==
+ // &system.Kernel().GetSystemResourceLimit(), ResultLimitReached);
+ // R_UNLESS(KTargetSystem::IsDynamicResourceLimitsEnabled(), ResultLimitReached());
+
+ // // Try to allocate a session from unused slab memory.
+ // session = T::CreateFromUnusedSlabMemory();
+ // R_UNLESS(session != nullptr, ResultLimitReached);
+ // ON_RESULT_FAILURE { session->Close(); };
+
+ // // If we're creating a KSession, we want to add two KSessionRequests to the heap, to
+ // // prevent request exhaustion.
+ // // NOTE: Nintendo checks if session->DynamicCast<KSession *>() != nullptr, but there's
+ // // no reason to not do this statically.
+ // if constexpr (std::same_as<T, KSession>) {
+ // for (size_t i = 0; i < 2; i++) {
+ // KSessionRequest* request = KSessionRequest::CreateFromUnusedSlabMemory();
+ // R_UNLESS(request != nullptr, ResultLimitReached);
+ // request->Close();
+ // }
+ // }
+
+ // We successfully allocated a session, so add the object we allocated to the resource
+ // limit.
+ // system.Kernel().GetSystemResourceLimit().Reserve(LimitableResource::SessionCountMax, 1);
+ }
+
+ // Check that we successfully created a session.
+ R_UNLESS(session != nullptr, ResultOutOfResource);
+
+ // Initialize the session.
+ session->Initialize(nullptr, fmt::format("{}", name));
+
+ // Commit the session reservation.
+ session_reservation.Commit();
+
+ // Ensure that we clean up the session (and its only references are handle table) on function
+ // end.
+ SCOPE_EXIT({
+ session->GetClientSession().Close();
+ session->GetServerSession().Close();
+ });
+
+ // Register the session.
+ T::Register(system.Kernel(), session);
+
+ // Add the server session to the handle table.
+ R_TRY(handle_table.Add(out_server, &session->GetServerSession()));
+
+ // Add the client session to the handle table.
+ const auto result = handle_table.Add(out_client, &session->GetClientSession());
+
+ if (!R_SUCCEEDED(result)) {
+ // Ensure that we maintaing a clean handle state on exit.
+ handle_table.Remove(*out_server);
+ }
+
+ return result;
+}
+
+} // namespace
+
+Result CreateSession(Core::System& system, Handle* out_server, Handle* out_client, bool is_light,
+ u64 name) {
+ if (is_light) {
+ // return CreateSession<KLightSession>(system, out_server, out_client, name);
+ return ResultNotImplemented;
+ } else {
+ return CreateSession<KSession>(system, out_server, out_client, name);
+ }
+}
+
+Result AcceptSession(Core::System& system, Handle* out_handle, Handle port_handle) {
+ UNIMPLEMENTED();
+ R_THROW(ResultNotImplemented);
+}
+
+Result CreateSession64(Core::System& system, Handle* out_server_session_handle,
+ Handle* out_client_session_handle, bool is_light, uint64_t name) {
+ R_RETURN(CreateSession(system, out_server_session_handle, out_client_session_handle, is_light,
+ name));
+}
+
+Result AcceptSession64(Core::System& system, Handle* out_handle, Handle port) {
+ R_RETURN(AcceptSession(system, out_handle, port));
+}
+
+Result CreateSession64From32(Core::System& system, Handle* out_server_session_handle,
+ Handle* out_client_session_handle, bool is_light, uint32_t name) {
+ R_RETURN(CreateSession(system, out_server_session_handle, out_client_session_handle, is_light,
+ name));
+}
+
+Result AcceptSession64From32(Core::System& system, Handle* out_handle, Handle port) {
+ R_RETURN(AcceptSession(system, out_handle, port));
+}
+
+} // 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..18e0dc904
--- /dev/null
+++ b/src/core/hle/kernel/svc/svc_shared_memory.cpp
@@ -0,0 +1,133 @@
+// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include "common/scope_exit.h"
+#include "core/core.h"
+#include "core/hle/kernel/k_process.h"
+#include "core/hle/kernel/k_shared_memory.h"
+#include "core/hle/kernel/svc.h"
+
+namespace Kernel::Svc {
+namespace {
+
+constexpr bool IsValidSharedMemoryPermission(MemoryPermission perm) {
+ switch (perm) {
+ case MemoryPermission::Read:
+ case MemoryPermission::ReadWrite:
+ return true;
+ default:
+ return false;
+ }
+}
+
+[[maybe_unused]] constexpr bool IsValidRemoteSharedMemoryPermission(MemoryPermission perm) {
+ return IsValidSharedMemoryPermission(perm) || perm == MemoryPermission::DontCare;
+}
+
+} // namespace
+
+Result MapSharedMemory(Core::System& system, Handle shmem_handle, VAddr address, u64 size,
+ Svc::MemoryPermission map_perm) {
+ LOG_TRACE(Kernel_SVC,
+ "called, shared_memory_handle=0x{:X}, addr=0x{:X}, size=0x{:X}, permissions=0x{:08X}",
+ shmem_handle, address, size, map_perm);
+
+ // Validate the address/size.
+ R_UNLESS(Common::IsAligned(address, PageSize), ResultInvalidAddress);
+ R_UNLESS(Common::IsAligned(size, PageSize), ResultInvalidSize);
+ R_UNLESS(size > 0, ResultInvalidSize);
+ R_UNLESS((address < address + size), ResultInvalidCurrentMemory);
+
+ // Validate the permission.
+ R_UNLESS(IsValidSharedMemoryPermission(map_perm), ResultInvalidNewMemoryPermission);
+
+ // Get the current process.
+ auto& process = GetCurrentProcess(system.Kernel());
+ auto& page_table = process.PageTable();
+
+ // Get the shared memory.
+ KScopedAutoObject shmem = process.GetHandleTable().GetObject<KSharedMemory>(shmem_handle);
+ R_UNLESS(shmem.IsNotNull(), ResultInvalidHandle);
+
+ // Verify that the mapping is in range.
+ R_UNLESS(page_table.CanContain(address, size, KMemoryState::Shared), ResultInvalidMemoryRegion);
+
+ // Add the shared memory to the process.
+ R_TRY(process.AddSharedMemory(shmem.GetPointerUnsafe(), address, size));
+
+ // Ensure that we clean up the shared memory if we fail to map it.
+ auto guard =
+ SCOPE_GUARD({ process.RemoveSharedMemory(shmem.GetPointerUnsafe(), address, size); });
+
+ // Map the shared memory.
+ R_TRY(shmem->Map(process, address, size, map_perm));
+
+ // We succeeded.
+ guard.Cancel();
+ return ResultSuccess;
+}
+
+Result UnmapSharedMemory(Core::System& system, Handle shmem_handle, VAddr address, u64 size) {
+ // Validate the address/size.
+ R_UNLESS(Common::IsAligned(address, PageSize), ResultInvalidAddress);
+ R_UNLESS(Common::IsAligned(size, PageSize), ResultInvalidSize);
+ R_UNLESS(size > 0, ResultInvalidSize);
+ R_UNLESS((address < address + size), ResultInvalidCurrentMemory);
+
+ // Get the current process.
+ auto& process = GetCurrentProcess(system.Kernel());
+ auto& page_table = process.PageTable();
+
+ // Get the shared memory.
+ KScopedAutoObject shmem = process.GetHandleTable().GetObject<KSharedMemory>(shmem_handle);
+ R_UNLESS(shmem.IsNotNull(), ResultInvalidHandle);
+
+ // Verify that the mapping is in range.
+ R_UNLESS(page_table.CanContain(address, size, KMemoryState::Shared), ResultInvalidMemoryRegion);
+
+ // Unmap the shared memory.
+ R_TRY(shmem->Unmap(process, address, size));
+
+ // Remove the shared memory from the process.
+ process.RemoveSharedMemory(shmem.GetPointerUnsafe(), address, size);
+
+ return ResultSuccess;
+}
+
+Result CreateSharedMemory(Core::System& system, Handle* out_handle, uint64_t size,
+ MemoryPermission owner_perm, MemoryPermission remote_perm) {
+ UNIMPLEMENTED();
+ R_THROW(ResultNotImplemented);
+}
+
+Result MapSharedMemory64(Core::System& system, Handle shmem_handle, uint64_t address, uint64_t size,
+ MemoryPermission map_perm) {
+ R_RETURN(MapSharedMemory(system, shmem_handle, address, size, map_perm));
+}
+
+Result UnmapSharedMemory64(Core::System& system, Handle shmem_handle, uint64_t address,
+ uint64_t size) {
+ R_RETURN(UnmapSharedMemory(system, shmem_handle, address, size));
+}
+
+Result CreateSharedMemory64(Core::System& system, Handle* out_handle, uint64_t size,
+ MemoryPermission owner_perm, MemoryPermission remote_perm) {
+ R_RETURN(CreateSharedMemory(system, out_handle, size, owner_perm, remote_perm));
+}
+
+Result MapSharedMemory64From32(Core::System& system, Handle shmem_handle, uint32_t address,
+ uint32_t size, MemoryPermission map_perm) {
+ R_RETURN(MapSharedMemory(system, shmem_handle, address, size, map_perm));
+}
+
+Result UnmapSharedMemory64From32(Core::System& system, Handle shmem_handle, uint32_t address,
+ uint32_t size) {
+ R_RETURN(UnmapSharedMemory(system, shmem_handle, address, size));
+}
+
+Result CreateSharedMemory64From32(Core::System& system, Handle* out_handle, uint32_t size,
+ MemoryPermission owner_perm, MemoryPermission remote_perm) {
+ R_RETURN(CreateSharedMemory(system, out_handle, size, owner_perm, remote_perm));
+}
+
+} // 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..1a8f7e191
--- /dev/null
+++ b/src/core/hle/kernel/svc/svc_synchronization.cpp
@@ -0,0 +1,163 @@
+// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include "common/scope_exit.h"
+#include "core/core.h"
+#include "core/hle/kernel/k_process.h"
+#include "core/hle/kernel/k_readable_event.h"
+#include "core/hle/kernel/svc.h"
+
+namespace Kernel::Svc {
+
+/// Close a handle
+Result CloseHandle(Core::System& system, Handle handle) {
+ LOG_TRACE(Kernel_SVC, "Closing handle 0x{:08X}", handle);
+
+ // Remove the handle.
+ R_UNLESS(GetCurrentProcess(system.Kernel()).GetHandleTable().Remove(handle),
+ ResultInvalidHandle);
+
+ return ResultSuccess;
+}
+
+/// Clears the signaled state of an event or process.
+Result ResetSignal(Core::System& system, Handle handle) {
+ LOG_DEBUG(Kernel_SVC, "called handle 0x{:08X}", handle);
+
+ // Get the current handle table.
+ const auto& handle_table = GetCurrentProcess(system.Kernel()).GetHandleTable();
+
+ // Try to reset as readable event.
+ {
+ KScopedAutoObject readable_event = handle_table.GetObject<KReadableEvent>(handle);
+ if (readable_event.IsNotNull()) {
+ return readable_event->Reset();
+ }
+ }
+
+ // Try to reset as process.
+ {
+ KScopedAutoObject process = handle_table.GetObject<KProcess>(handle);
+ if (process.IsNotNull()) {
+ return process->Reset();
+ }
+ }
+
+ LOG_ERROR(Kernel_SVC, "invalid handle (0x{:08X})", handle);
+
+ return ResultInvalidHandle;
+}
+
+/// Wait for the given handles to synchronize, timeout after the specified nanoseconds
+Result WaitSynchronization(Core::System& system, s32* index, VAddr handles_address, s32 num_handles,
+ s64 nano_seconds) {
+ LOG_TRACE(Kernel_SVC, "called handles_address=0x{:X}, num_handles={}, nano_seconds={}",
+ handles_address, num_handles, nano_seconds);
+
+ // Ensure number of handles is valid.
+ R_UNLESS(0 <= num_handles && num_handles <= ArgumentHandleCountMax, ResultOutOfRange);
+
+ auto& kernel = system.Kernel();
+ std::vector<KSynchronizationObject*> objs(num_handles);
+ const auto& handle_table = GetCurrentProcess(kernel).GetHandleTable();
+ Handle* handles = system.Memory().GetPointer<Handle>(handles_address);
+
+ // Copy user handles.
+ if (num_handles > 0) {
+ // Convert the handles to objects.
+ R_UNLESS(handle_table.GetMultipleObjects<KSynchronizationObject>(objs.data(), handles,
+ num_handles),
+ ResultInvalidHandle);
+ for (const auto& obj : objs) {
+ kernel.RegisterInUseObject(obj);
+ }
+ }
+
+ // Ensure handles are closed when we're done.
+ SCOPE_EXIT({
+ for (s32 i = 0; i < num_handles; ++i) {
+ kernel.UnregisterInUseObject(objs[i]);
+ objs[i]->Close();
+ }
+ });
+
+ return KSynchronizationObject::Wait(kernel, index, objs.data(), static_cast<s32>(objs.size()),
+ nano_seconds);
+}
+
+/// Resumes a thread waiting on WaitSynchronization
+Result CancelSynchronization(Core::System& system, Handle handle) {
+ LOG_TRACE(Kernel_SVC, "called handle=0x{:X}", handle);
+
+ // Get the thread from its handle.
+ KScopedAutoObject thread =
+ GetCurrentProcess(system.Kernel()).GetHandleTable().GetObject<KThread>(handle);
+ R_UNLESS(thread.IsNotNull(), ResultInvalidHandle);
+
+ // Cancel the thread's wait.
+ thread->WaitCancel();
+ return ResultSuccess;
+}
+
+void SynchronizePreemptionState(Core::System& system) {
+ auto& kernel = system.Kernel();
+
+ // Lock the scheduler.
+ KScopedSchedulerLock sl{kernel};
+
+ // If the current thread is pinned, unpin it.
+ KProcess* cur_process = GetCurrentProcessPointer(kernel);
+ const auto core_id = GetCurrentCoreId(kernel);
+
+ if (cur_process->GetPinnedThread(core_id) == GetCurrentThreadPointer(kernel)) {
+ // Clear the current thread's interrupt flag.
+ GetCurrentThread(kernel).ClearInterruptFlag();
+
+ // Unpin the current thread.
+ cur_process->UnpinCurrentThread(core_id);
+ }
+}
+
+Result CloseHandle64(Core::System& system, Handle handle) {
+ R_RETURN(CloseHandle(system, handle));
+}
+
+Result ResetSignal64(Core::System& system, Handle handle) {
+ R_RETURN(ResetSignal(system, handle));
+}
+
+Result WaitSynchronization64(Core::System& system, int32_t* out_index, uint64_t handles,
+ int32_t num_handles, int64_t timeout_ns) {
+ R_RETURN(WaitSynchronization(system, out_index, handles, num_handles, timeout_ns));
+}
+
+Result CancelSynchronization64(Core::System& system, Handle handle) {
+ R_RETURN(CancelSynchronization(system, handle));
+}
+
+void SynchronizePreemptionState64(Core::System& system) {
+ SynchronizePreemptionState(system);
+}
+
+Result CloseHandle64From32(Core::System& system, Handle handle) {
+ R_RETURN(CloseHandle(system, handle));
+}
+
+Result ResetSignal64From32(Core::System& system, Handle handle) {
+ R_RETURN(ResetSignal(system, handle));
+}
+
+Result WaitSynchronization64From32(Core::System& system, int32_t* out_index, uint32_t handles,
+ int32_t num_handles, int64_t timeout_ns) {
+ R_RETURN(WaitSynchronization(system, out_index, handles, num_handles, timeout_ns));
+}
+
+Result CancelSynchronization64From32(Core::System& system, Handle handle) {
+ R_RETURN(CancelSynchronization(system, handle));
+}
+
+void SynchronizePreemptionState64From32(Core::System& system) {
+ SynchronizePreemptionState(system);
+}
+
+} // 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..b39807841
--- /dev/null
+++ b/src/core/hle/kernel/svc/svc_thread.cpp
@@ -0,0 +1,437 @@
+// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include "common/scope_exit.h"
+#include "core/core.h"
+#include "core/core_timing.h"
+#include "core/hle/kernel/k_process.h"
+#include "core/hle/kernel/k_scoped_resource_reservation.h"
+#include "core/hle/kernel/k_thread.h"
+#include "core/hle/kernel/svc.h"
+
+namespace Kernel::Svc {
+namespace {
+
+constexpr bool IsValidVirtualCoreId(int32_t core_id) {
+ return (0 <= core_id && core_id < static_cast<int32_t>(Core::Hardware::NUM_CPU_CORES));
+}
+
+} // Anonymous namespace
+
+/// Creates a new thread
+Result CreateThread(Core::System& system, Handle* out_handle, VAddr entry_point, u64 arg,
+ VAddr stack_bottom, s32 priority, s32 core_id) {
+ LOG_DEBUG(Kernel_SVC,
+ "called entry_point=0x{:08X}, arg=0x{:08X}, stack_bottom=0x{:08X}, "
+ "priority=0x{:08X}, core_id=0x{:08X}",
+ entry_point, arg, stack_bottom, priority, core_id);
+
+ // Adjust core id, if it's the default magic.
+ auto& kernel = system.Kernel();
+ auto& process = GetCurrentProcess(kernel);
+ if (core_id == IdealCoreUseProcessValue) {
+ core_id = process.GetIdealCoreId();
+ }
+
+ // Validate arguments.
+ if (!IsValidVirtualCoreId(core_id)) {
+ LOG_ERROR(Kernel_SVC, "Invalid Core ID specified (id={})", core_id);
+ return ResultInvalidCoreId;
+ }
+ if (((1ULL << core_id) & process.GetCoreMask()) == 0) {
+ LOG_ERROR(Kernel_SVC, "Core ID doesn't fall within allowable cores (id={})", core_id);
+ return ResultInvalidCoreId;
+ }
+
+ if (HighestThreadPriority > priority || priority > LowestThreadPriority) {
+ LOG_ERROR(Kernel_SVC, "Invalid priority specified (priority={})", priority);
+ return ResultInvalidPriority;
+ }
+ if (!process.CheckThreadPriority(priority)) {
+ LOG_ERROR(Kernel_SVC, "Invalid allowable thread priority (priority={})", priority);
+ return ResultInvalidPriority;
+ }
+
+ // Reserve a new thread from the process resource limit (waiting up to 100ms).
+ KScopedResourceReservation thread_reservation(&process, LimitableResource::ThreadCountMax, 1,
+ system.CoreTiming().GetGlobalTimeNs().count() +
+ 100000000);
+ if (!thread_reservation.Succeeded()) {
+ LOG_ERROR(Kernel_SVC, "Could not reserve a new thread");
+ return ResultLimitReached;
+ }
+
+ // Create the thread.
+ KThread* thread = KThread::Create(kernel);
+ if (!thread) {
+ LOG_ERROR(Kernel_SVC, "Unable to create new threads. Thread creation limit reached.");
+ return ResultOutOfResource;
+ }
+ SCOPE_EXIT({ thread->Close(); });
+
+ // Initialize the thread.
+ {
+ KScopedLightLock lk{process.GetStateLock()};
+ R_TRY(KThread::InitializeUserThread(system, thread, entry_point, arg, stack_bottom,
+ priority, core_id, &process));
+ }
+
+ // Set the thread name for debugging purposes.
+ thread->SetName(fmt::format("thread[entry_point={:X}, handle={:X}]", entry_point, *out_handle));
+
+ // Commit the thread reservation.
+ thread_reservation.Commit();
+
+ // Register the new thread.
+ KThread::Register(kernel, thread);
+
+ // Add the thread to the handle table.
+ R_TRY(process.GetHandleTable().Add(out_handle, thread));
+
+ return ResultSuccess;
+}
+
+/// Starts the thread for the provided handle
+Result StartThread(Core::System& system, Handle thread_handle) {
+ LOG_DEBUG(Kernel_SVC, "called thread=0x{:08X}", thread_handle);
+
+ // Get the thread from its handle.
+ KScopedAutoObject thread =
+ GetCurrentProcess(system.Kernel()).GetHandleTable().GetObject<KThread>(thread_handle);
+ R_UNLESS(thread.IsNotNull(), ResultInvalidHandle);
+
+ // Try to start the thread.
+ R_TRY(thread->Run());
+
+ // If we succeeded, persist a reference to the thread.
+ thread->Open();
+ system.Kernel().RegisterInUseObject(thread.GetPointerUnsafe());
+
+ return ResultSuccess;
+}
+
+/// Called when a thread exits
+void ExitThread(Core::System& system) {
+ LOG_DEBUG(Kernel_SVC, "called, pc=0x{:08X}", system.CurrentArmInterface().GetPC());
+
+ auto* const current_thread = GetCurrentThreadPointer(system.Kernel());
+ system.GlobalSchedulerContext().RemoveThread(current_thread);
+ current_thread->Exit();
+ system.Kernel().UnregisterInUseObject(current_thread);
+}
+
+/// Sleep the current thread
+void SleepThread(Core::System& system, s64 nanoseconds) {
+ auto& kernel = system.Kernel();
+ const auto yield_type = static_cast<Svc::YieldType>(nanoseconds);
+
+ LOG_TRACE(Kernel_SVC, "called nanoseconds={}", nanoseconds);
+
+ // When the input tick is positive, sleep.
+ if (nanoseconds > 0) {
+ // Convert the timeout from nanoseconds to ticks.
+ // NOTE: Nintendo does not use this conversion logic in WaitSynchronization...
+
+ // Sleep.
+ // NOTE: Nintendo does not check the result of this sleep.
+ static_cast<void>(GetCurrentThread(kernel).Sleep(nanoseconds));
+ } else if (yield_type == Svc::YieldType::WithoutCoreMigration) {
+ KScheduler::YieldWithoutCoreMigration(kernel);
+ } else if (yield_type == Svc::YieldType::WithCoreMigration) {
+ KScheduler::YieldWithCoreMigration(kernel);
+ } else if (yield_type == Svc::YieldType::ToAnyThread) {
+ KScheduler::YieldToAnyThread(kernel);
+ } else {
+ // Nintendo does nothing at all if an otherwise invalid value is passed.
+ ASSERT_MSG(false, "Unimplemented sleep yield type '{:016X}'!", nanoseconds);
+ }
+}
+
+/// Gets the thread context
+Result GetThreadContext3(Core::System& system, VAddr out_context, Handle thread_handle) {
+ LOG_DEBUG(Kernel_SVC, "called, out_context=0x{:08X}, thread_handle=0x{:X}", out_context,
+ thread_handle);
+
+ auto& kernel = system.Kernel();
+
+ // Get the thread from its handle.
+ KScopedAutoObject thread =
+ GetCurrentProcess(kernel).GetHandleTable().GetObject<KThread>(thread_handle);
+ R_UNLESS(thread.IsNotNull(), ResultInvalidHandle);
+
+ // Require the handle be to a non-current thread in the current process.
+ const auto* current_process = GetCurrentProcessPointer(kernel);
+ R_UNLESS(current_process == thread->GetOwnerProcess(), ResultInvalidId);
+
+ // Verify that the thread isn't terminated.
+ R_UNLESS(thread->GetState() != ThreadState::Terminated, ResultTerminationRequested);
+
+ /// Check that the thread is not the current one.
+ /// NOTE: Nintendo does not check this, and thus the following loop will deadlock.
+ R_UNLESS(thread.GetPointerUnsafe() != GetCurrentThreadPointer(kernel), ResultInvalidId);
+
+ // Try to get the thread context until the thread isn't current on any core.
+ while (true) {
+ KScopedSchedulerLock sl{kernel};
+
+ // TODO(bunnei): Enforce that thread is suspended for debug here.
+
+ // If the thread's raw state isn't runnable, check if it's current on some core.
+ if (thread->GetRawState() != ThreadState::Runnable) {
+ bool current = false;
+ for (auto i = 0; i < static_cast<s32>(Core::Hardware::NUM_CPU_CORES); ++i) {
+ if (thread.GetPointerUnsafe() == kernel.Scheduler(i).GetSchedulerCurrentThread()) {
+ current = true;
+ break;
+ }
+ }
+
+ // If the thread is current, retry until it isn't.
+ if (current) {
+ continue;
+ }
+ }
+
+ // Get the thread context.
+ std::vector<u8> context;
+ R_TRY(thread->GetThreadContext3(context));
+
+ // Copy the thread context to user space.
+ system.Memory().WriteBlock(out_context, context.data(), context.size());
+
+ return ResultSuccess;
+ }
+
+ return ResultSuccess;
+}
+
+/// Gets the priority for the specified thread
+Result GetThreadPriority(Core::System& system, s32* out_priority, Handle handle) {
+ LOG_TRACE(Kernel_SVC, "called");
+
+ // Get the thread from its handle.
+ KScopedAutoObject thread =
+ GetCurrentProcess(system.Kernel()).GetHandleTable().GetObject<KThread>(handle);
+ R_UNLESS(thread.IsNotNull(), ResultInvalidHandle);
+
+ // Get the thread's priority.
+ *out_priority = thread->GetPriority();
+ return ResultSuccess;
+}
+
+/// Sets the priority for the specified thread
+Result SetThreadPriority(Core::System& system, Handle thread_handle, s32 priority) {
+ // Get the current process.
+ KProcess& process = GetCurrentProcess(system.Kernel());
+
+ // Validate the priority.
+ R_UNLESS(HighestThreadPriority <= priority && priority <= LowestThreadPriority,
+ ResultInvalidPriority);
+ R_UNLESS(process.CheckThreadPriority(priority), ResultInvalidPriority);
+
+ // Get the thread from its handle.
+ KScopedAutoObject thread = process.GetHandleTable().GetObject<KThread>(thread_handle);
+ R_UNLESS(thread.IsNotNull(), ResultInvalidHandle);
+
+ // Set the thread priority.
+ thread->SetBasePriority(priority);
+ return ResultSuccess;
+}
+
+Result GetThreadList(Core::System& system, s32* out_num_threads, VAddr out_thread_ids,
+ s32 out_thread_ids_size, Handle debug_handle) {
+ // TODO: Handle this case when debug events are supported.
+ UNIMPLEMENTED_IF(debug_handle != InvalidHandle);
+
+ LOG_DEBUG(Kernel_SVC, "called. out_thread_ids=0x{:016X}, out_thread_ids_size={}",
+ out_thread_ids, out_thread_ids_size);
+
+ // If the size is negative or larger than INT32_MAX / sizeof(u64)
+ if ((out_thread_ids_size & 0xF0000000) != 0) {
+ LOG_ERROR(Kernel_SVC, "Supplied size outside [0, 0x0FFFFFFF] range. size={}",
+ out_thread_ids_size);
+ return ResultOutOfRange;
+ }
+
+ auto* const current_process = GetCurrentProcessPointer(system.Kernel());
+ const auto total_copy_size = out_thread_ids_size * sizeof(u64);
+
+ if (out_thread_ids_size > 0 &&
+ !current_process->PageTable().IsInsideAddressSpace(out_thread_ids, total_copy_size)) {
+ LOG_ERROR(Kernel_SVC, "Address range outside address space. begin=0x{:016X}, end=0x{:016X}",
+ out_thread_ids, out_thread_ids + total_copy_size);
+ return ResultInvalidCurrentMemory;
+ }
+
+ auto& memory = system.Memory();
+ const auto& thread_list = current_process->GetThreadList();
+ const auto num_threads = thread_list.size();
+ const auto copy_amount = std::min(static_cast<std::size_t>(out_thread_ids_size), num_threads);
+
+ auto list_iter = thread_list.cbegin();
+ for (std::size_t i = 0; i < copy_amount; ++i, ++list_iter) {
+ memory.Write64(out_thread_ids, (*list_iter)->GetThreadID());
+ out_thread_ids += sizeof(u64);
+ }
+
+ *out_num_threads = static_cast<u32>(num_threads);
+ return ResultSuccess;
+}
+
+Result GetThreadCoreMask(Core::System& system, s32* out_core_id, u64* out_affinity_mask,
+ Handle thread_handle) {
+ LOG_TRACE(Kernel_SVC, "called, handle=0x{:08X}", thread_handle);
+
+ // Get the thread from its handle.
+ KScopedAutoObject thread =
+ GetCurrentProcess(system.Kernel()).GetHandleTable().GetObject<KThread>(thread_handle);
+ R_UNLESS(thread.IsNotNull(), ResultInvalidHandle);
+
+ // Get the core mask.
+ R_TRY(thread->GetCoreMask(out_core_id, out_affinity_mask));
+
+ return ResultSuccess;
+}
+
+Result SetThreadCoreMask(Core::System& system, Handle thread_handle, s32 core_id,
+ u64 affinity_mask) {
+ // Determine the core id/affinity mask.
+ if (core_id == IdealCoreUseProcessValue) {
+ core_id = GetCurrentProcess(system.Kernel()).GetIdealCoreId();
+ affinity_mask = (1ULL << core_id);
+ } else {
+ // Validate the affinity mask.
+ const u64 process_core_mask = GetCurrentProcess(system.Kernel()).GetCoreMask();
+ R_UNLESS((affinity_mask | process_core_mask) == process_core_mask, ResultInvalidCoreId);
+ R_UNLESS(affinity_mask != 0, ResultInvalidCombination);
+
+ // Validate the core id.
+ if (IsValidVirtualCoreId(core_id)) {
+ R_UNLESS(((1ULL << core_id) & affinity_mask) != 0, ResultInvalidCombination);
+ } else {
+ R_UNLESS(core_id == IdealCoreNoUpdate || core_id == IdealCoreDontCare,
+ ResultInvalidCoreId);
+ }
+ }
+
+ // Get the thread from its handle.
+ KScopedAutoObject thread =
+ GetCurrentProcess(system.Kernel()).GetHandleTable().GetObject<KThread>(thread_handle);
+ R_UNLESS(thread.IsNotNull(), ResultInvalidHandle);
+
+ // Set the core mask.
+ R_TRY(thread->SetCoreMask(core_id, affinity_mask));
+
+ return ResultSuccess;
+}
+
+/// Get the ID for the specified thread.
+Result GetThreadId(Core::System& system, u64* out_thread_id, Handle thread_handle) {
+ // Get the thread from its handle.
+ KScopedAutoObject thread =
+ GetCurrentProcess(system.Kernel()).GetHandleTable().GetObject<KThread>(thread_handle);
+ R_UNLESS(thread.IsNotNull(), ResultInvalidHandle);
+
+ // Get the thread's id.
+ *out_thread_id = thread->GetId();
+ return ResultSuccess;
+}
+
+Result CreateThread64(Core::System& system, Handle* out_handle, uint64_t func, uint64_t arg,
+ uint64_t stack_bottom, int32_t priority, int32_t core_id) {
+ R_RETURN(CreateThread(system, out_handle, func, arg, stack_bottom, priority, core_id));
+}
+
+Result StartThread64(Core::System& system, Handle thread_handle) {
+ R_RETURN(StartThread(system, thread_handle));
+}
+
+void ExitThread64(Core::System& system) {
+ return ExitThread(system);
+}
+
+void SleepThread64(Core::System& system, int64_t ns) {
+ return SleepThread(system, ns);
+}
+
+Result GetThreadPriority64(Core::System& system, int32_t* out_priority, Handle thread_handle) {
+ R_RETURN(GetThreadPriority(system, out_priority, thread_handle));
+}
+
+Result SetThreadPriority64(Core::System& system, Handle thread_handle, int32_t priority) {
+ R_RETURN(SetThreadPriority(system, thread_handle, priority));
+}
+
+Result GetThreadCoreMask64(Core::System& system, int32_t* out_core_id, uint64_t* out_affinity_mask,
+ Handle thread_handle) {
+ R_RETURN(GetThreadCoreMask(system, out_core_id, out_affinity_mask, thread_handle));
+}
+
+Result SetThreadCoreMask64(Core::System& system, Handle thread_handle, int32_t core_id,
+ uint64_t affinity_mask) {
+ R_RETURN(SetThreadCoreMask(system, thread_handle, core_id, affinity_mask));
+}
+
+Result GetThreadId64(Core::System& system, uint64_t* out_thread_id, Handle thread_handle) {
+ R_RETURN(GetThreadId(system, out_thread_id, thread_handle));
+}
+
+Result GetThreadContext364(Core::System& system, uint64_t out_context, Handle thread_handle) {
+ R_RETURN(GetThreadContext3(system, out_context, thread_handle));
+}
+
+Result GetThreadList64(Core::System& system, int32_t* out_num_threads, uint64_t out_thread_ids,
+ int32_t max_out_count, Handle debug_handle) {
+ R_RETURN(GetThreadList(system, out_num_threads, out_thread_ids, max_out_count, debug_handle));
+}
+
+Result CreateThread64From32(Core::System& system, Handle* out_handle, uint32_t func, uint32_t arg,
+ uint32_t stack_bottom, int32_t priority, int32_t core_id) {
+ R_RETURN(CreateThread(system, out_handle, func, arg, stack_bottom, priority, core_id));
+}
+
+Result StartThread64From32(Core::System& system, Handle thread_handle) {
+ R_RETURN(StartThread(system, thread_handle));
+}
+
+void ExitThread64From32(Core::System& system) {
+ return ExitThread(system);
+}
+
+void SleepThread64From32(Core::System& system, int64_t ns) {
+ return SleepThread(system, ns);
+}
+
+Result GetThreadPriority64From32(Core::System& system, int32_t* out_priority,
+ Handle thread_handle) {
+ R_RETURN(GetThreadPriority(system, out_priority, thread_handle));
+}
+
+Result SetThreadPriority64From32(Core::System& system, Handle thread_handle, int32_t priority) {
+ R_RETURN(SetThreadPriority(system, thread_handle, priority));
+}
+
+Result GetThreadCoreMask64From32(Core::System& system, int32_t* out_core_id,
+ uint64_t* out_affinity_mask, Handle thread_handle) {
+ R_RETURN(GetThreadCoreMask(system, out_core_id, out_affinity_mask, thread_handle));
+}
+
+Result SetThreadCoreMask64From32(Core::System& system, Handle thread_handle, int32_t core_id,
+ uint64_t affinity_mask) {
+ R_RETURN(SetThreadCoreMask(system, thread_handle, core_id, affinity_mask));
+}
+
+Result GetThreadId64From32(Core::System& system, uint64_t* out_thread_id, Handle thread_handle) {
+ R_RETURN(GetThreadId(system, out_thread_id, thread_handle));
+}
+
+Result GetThreadContext364From32(Core::System& system, uint32_t out_context, Handle thread_handle) {
+ R_RETURN(GetThreadContext3(system, out_context, thread_handle));
+}
+
+Result GetThreadList64From32(Core::System& system, int32_t* out_num_threads,
+ uint32_t out_thread_ids, int32_t max_out_count, Handle debug_handle) {
+ R_RETURN(GetThreadList(system, out_num_threads, out_thread_ids, max_out_count, debug_handle));
+}
+
+} // 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..40de7708b
--- /dev/null
+++ b/src/core/hle/kernel/svc/svc_thread_profiler.cpp
@@ -0,0 +1,60 @@
+// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include "core/hle/kernel/svc.h"
+#include "core/hle/kernel/svc_results.h"
+
+namespace Kernel::Svc {
+
+Result GetDebugFutureThreadInfo(Core::System& system, lp64::LastThreadContext* out_context,
+ uint64_t* out_thread_id, Handle debug_handle, int64_t ns) {
+ UNIMPLEMENTED();
+ R_THROW(ResultNotImplemented);
+}
+
+Result GetLastThreadInfo(Core::System& system, lp64::LastThreadContext* out_context,
+ uint64_t* out_tls_address, uint32_t* out_flags) {
+ UNIMPLEMENTED();
+ R_THROW(ResultNotImplemented);
+}
+
+Result GetDebugFutureThreadInfo64(Core::System& system, lp64::LastThreadContext* out_context,
+ uint64_t* out_thread_id, Handle debug_handle, int64_t ns) {
+ R_RETURN(GetDebugFutureThreadInfo(system, out_context, out_thread_id, debug_handle, ns));
+}
+
+Result GetLastThreadInfo64(Core::System& system, lp64::LastThreadContext* out_context,
+ uint64_t* out_tls_address, uint32_t* out_flags) {
+ R_RETURN(GetLastThreadInfo(system, out_context, out_tls_address, out_flags));
+}
+
+Result GetDebugFutureThreadInfo64From32(Core::System& system, ilp32::LastThreadContext* out_context,
+ uint64_t* out_thread_id, Handle debug_handle, int64_t ns) {
+ lp64::LastThreadContext context{};
+ R_TRY(
+ GetDebugFutureThreadInfo(system, std::addressof(context), out_thread_id, debug_handle, ns));
+
+ *out_context = {
+ .fp = static_cast<u32>(context.fp),
+ .sp = static_cast<u32>(context.sp),
+ .lr = static_cast<u32>(context.lr),
+ .pc = static_cast<u32>(context.pc),
+ };
+ R_SUCCEED();
+}
+
+Result GetLastThreadInfo64From32(Core::System& system, ilp32::LastThreadContext* out_context,
+ uint64_t* out_tls_address, uint32_t* out_flags) {
+ lp64::LastThreadContext context{};
+ R_TRY(GetLastThreadInfo(system, std::addressof(context), out_tls_address, out_flags));
+
+ *out_context = {
+ .fp = static_cast<u32>(context.fp),
+ .sp = static_cast<u32>(context.sp),
+ .lr = static_cast<u32>(context.lr),
+ .pc = static_cast<u32>(context.pc),
+ };
+ R_SUCCEED();
+}
+
+} // 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..561336482
--- /dev/null
+++ b/src/core/hle/kernel/svc/svc_tick.cpp
@@ -0,0 +1,35 @@
+// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include "core/core.h"
+#include "core/core_timing.h"
+#include "core/hle/kernel/kernel.h"
+#include "core/hle/kernel/svc.h"
+
+namespace Kernel::Svc {
+
+/// This returns the total CPU ticks elapsed since the CPU was powered-on
+int64_t GetSystemTick(Core::System& system) {
+ LOG_TRACE(Kernel_SVC, "called");
+
+ auto& core_timing = system.CoreTiming();
+
+ // Returns the value of cntpct_el0 (https://switchbrew.org/wiki/SVC#svcGetSystemTick)
+ const u64 result{core_timing.GetClockTicks()};
+
+ if (!system.Kernel().IsMulticore()) {
+ core_timing.AddTicks(400U);
+ }
+
+ return static_cast<int64_t>(result);
+}
+
+int64_t GetSystemTick64(Core::System& system) {
+ return GetSystemTick(system);
+}
+
+int64_t GetSystemTick64From32(Core::System& system) {
+ return GetSystemTick(system);
+}
+
+} // 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..7ffc24adf
--- /dev/null
+++ b/src/core/hle/kernel/svc/svc_transfer_memory.cpp
@@ -0,0 +1,117 @@
+// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include "common/scope_exit.h"
+#include "core/core.h"
+#include "core/hle/kernel/k_process.h"
+#include "core/hle/kernel/k_scoped_resource_reservation.h"
+#include "core/hle/kernel/k_transfer_memory.h"
+#include "core/hle/kernel/svc.h"
+
+namespace Kernel::Svc {
+namespace {
+
+constexpr bool IsValidTransferMemoryPermission(MemoryPermission perm) {
+ switch (perm) {
+ case MemoryPermission::None:
+ case MemoryPermission::Read:
+ case MemoryPermission::ReadWrite:
+ return true;
+ default:
+ return false;
+ }
+}
+
+} // Anonymous namespace
+
+/// Creates a TransferMemory object
+Result CreateTransferMemory(Core::System& system, Handle* out, VAddr address, u64 size,
+ MemoryPermission map_perm) {
+ auto& kernel = system.Kernel();
+
+ // Validate the size.
+ R_UNLESS(Common::IsAligned(address, PageSize), ResultInvalidAddress);
+ R_UNLESS(Common::IsAligned(size, PageSize), ResultInvalidSize);
+ R_UNLESS(size > 0, ResultInvalidSize);
+ R_UNLESS((address < address + size), ResultInvalidCurrentMemory);
+
+ // Validate the permissions.
+ R_UNLESS(IsValidTransferMemoryPermission(map_perm), ResultInvalidNewMemoryPermission);
+
+ // Get the current process and handle table.
+ auto& process = GetCurrentProcess(kernel);
+ auto& handle_table = process.GetHandleTable();
+
+ // Reserve a new transfer memory from the process resource limit.
+ KScopedResourceReservation trmem_reservation(&process,
+ LimitableResource::TransferMemoryCountMax);
+ R_UNLESS(trmem_reservation.Succeeded(), ResultLimitReached);
+
+ // Create the transfer memory.
+ KTransferMemory* trmem = KTransferMemory::Create(kernel);
+ R_UNLESS(trmem != nullptr, ResultOutOfResource);
+
+ // Ensure the only reference is in the handle table when we're done.
+ SCOPE_EXIT({ trmem->Close(); });
+
+ // Ensure that the region is in range.
+ R_UNLESS(process.PageTable().Contains(address, size), ResultInvalidCurrentMemory);
+
+ // Initialize the transfer memory.
+ R_TRY(trmem->Initialize(address, size, map_perm));
+
+ // Commit the reservation.
+ trmem_reservation.Commit();
+
+ // Register the transfer memory.
+ KTransferMemory::Register(kernel, trmem);
+
+ // Add the transfer memory to the handle table.
+ R_TRY(handle_table.Add(out, trmem));
+
+ return ResultSuccess;
+}
+
+Result MapTransferMemory(Core::System& system, Handle trmem_handle, uint64_t address, uint64_t size,
+ MemoryPermission owner_perm) {
+ UNIMPLEMENTED();
+ R_THROW(ResultNotImplemented);
+}
+
+Result UnmapTransferMemory(Core::System& system, Handle trmem_handle, uint64_t address,
+ uint64_t size) {
+ UNIMPLEMENTED();
+ R_THROW(ResultNotImplemented);
+}
+
+Result MapTransferMemory64(Core::System& system, Handle trmem_handle, uint64_t address,
+ uint64_t size, MemoryPermission owner_perm) {
+ R_RETURN(MapTransferMemory(system, trmem_handle, address, size, owner_perm));
+}
+
+Result UnmapTransferMemory64(Core::System& system, Handle trmem_handle, uint64_t address,
+ uint64_t size) {
+ R_RETURN(UnmapTransferMemory(system, trmem_handle, address, size));
+}
+
+Result CreateTransferMemory64(Core::System& system, Handle* out_handle, uint64_t address,
+ uint64_t size, MemoryPermission map_perm) {
+ R_RETURN(CreateTransferMemory(system, out_handle, address, size, map_perm));
+}
+
+Result MapTransferMemory64From32(Core::System& system, Handle trmem_handle, uint32_t address,
+ uint32_t size, MemoryPermission owner_perm) {
+ R_RETURN(MapTransferMemory(system, trmem_handle, address, size, owner_perm));
+}
+
+Result UnmapTransferMemory64From32(Core::System& system, Handle trmem_handle, uint32_t address,
+ uint32_t size) {
+ R_RETURN(UnmapTransferMemory(system, trmem_handle, address, size));
+}
+
+Result CreateTransferMemory64From32(Core::System& system, Handle* out_handle, uint32_t address,
+ uint32_t size, MemoryPermission map_perm) {
+ R_RETURN(CreateTransferMemory(system, out_handle, address, size, map_perm));
+}
+
+} // namespace Kernel::Svc
diff --git a/src/core/hle/kernel/svc_generator.py b/src/core/hle/kernel/svc_generator.py
new file mode 100644
index 000000000..34d2ac659
--- /dev/null
+++ b/src/core/hle/kernel/svc_generator.py
@@ -0,0 +1,716 @@
+# SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
+# SPDX-License-Identifier: GPL-2.0-or-later
+
+# Raw SVC definitions from the kernel.
+#
+# Avoid modifying the prototypes; see below for how to customize generation
+# for a given typename.
+SVCS = [
+ [0x01, "Result SetHeapSize(Address* out_address, Size size);"],
+ [0x02, "Result SetMemoryPermission(Address address, Size size, MemoryPermission perm);"],
+ [0x03, "Result SetMemoryAttribute(Address address, Size size, uint32_t mask, uint32_t attr);"],
+ [0x04, "Result MapMemory(Address dst_address, Address src_address, Size size);"],
+ [0x05, "Result UnmapMemory(Address dst_address, Address src_address, Size size);"],
+ [0x06, "Result QueryMemory(Address out_memory_info, PageInfo* out_page_info, Address address);"],
+ [0x07, "void ExitProcess();"],
+ [0x08, "Result CreateThread(Handle* out_handle, ThreadFunc func, Address arg, Address stack_bottom, int32_t priority, int32_t core_id);"],
+ [0x09, "Result StartThread(Handle thread_handle);"],
+ [0x0A, "void ExitThread();"],
+ [0x0B, "void SleepThread(int64_t ns);"],
+ [0x0C, "Result GetThreadPriority(int32_t* out_priority, Handle thread_handle);"],
+ [0x0D, "Result SetThreadPriority(Handle thread_handle, int32_t priority);"],
+ [0x0E, "Result GetThreadCoreMask(int32_t* out_core_id, uint64_t* out_affinity_mask, Handle thread_handle);"],
+ [0x0F, "Result SetThreadCoreMask(Handle thread_handle, int32_t core_id, uint64_t affinity_mask);"],
+ [0x10, "int32_t GetCurrentProcessorNumber();"],
+ [0x11, "Result SignalEvent(Handle event_handle);"],
+ [0x12, "Result ClearEvent(Handle event_handle);"],
+ [0x13, "Result MapSharedMemory(Handle shmem_handle, Address address, Size size, MemoryPermission map_perm);"],
+ [0x14, "Result UnmapSharedMemory(Handle shmem_handle, Address address, Size size);"],
+ [0x15, "Result CreateTransferMemory(Handle* out_handle, Address address, Size size, MemoryPermission map_perm);"],
+ [0x16, "Result CloseHandle(Handle handle);"],
+ [0x17, "Result ResetSignal(Handle handle);"],
+ [0x18, "Result WaitSynchronization(int32_t* out_index, Address handles, int32_t num_handles, int64_t timeout_ns);"],
+ [0x19, "Result CancelSynchronization(Handle handle);"],
+ [0x1A, "Result ArbitrateLock(Handle thread_handle, Address address, uint32_t tag);"],
+ [0x1B, "Result ArbitrateUnlock(Address address);"],
+ [0x1C, "Result WaitProcessWideKeyAtomic(Address address, Address cv_key, uint32_t tag, int64_t timeout_ns);"],
+ [0x1D, "void SignalProcessWideKey(Address cv_key, int32_t count);"],
+ [0x1E, "int64_t GetSystemTick();"],
+ [0x1F, "Result ConnectToNamedPort(Handle* out_handle, Address name);"],
+ [0x20, "Result SendSyncRequestLight(Handle session_handle);"],
+ [0x21, "Result SendSyncRequest(Handle session_handle);"],
+ [0x22, "Result SendSyncRequestWithUserBuffer(Address message_buffer, Size message_buffer_size, Handle session_handle);"],
+ [0x23, "Result SendAsyncRequestWithUserBuffer(Handle* out_event_handle, Address message_buffer, Size message_buffer_size, Handle session_handle);"],
+ [0x24, "Result GetProcessId(uint64_t* out_process_id, Handle process_handle);"],
+ [0x25, "Result GetThreadId(uint64_t* out_thread_id, Handle thread_handle);"],
+ [0x26, "void Break(BreakReason break_reason, Address arg, Size size);"],
+ [0x27, "Result OutputDebugString(Address debug_str, Size len);"],
+ [0x28, "void ReturnFromException(Result result);"],
+ [0x29, "Result GetInfo(uint64_t* out, InfoType info_type, Handle handle, uint64_t info_subtype);"],
+ [0x2A, "void FlushEntireDataCache();"],
+ [0x2B, "Result FlushDataCache(Address address, Size size);"],
+ [0x2C, "Result MapPhysicalMemory(Address address, Size size);"],
+ [0x2D, "Result UnmapPhysicalMemory(Address address, Size size);"],
+ [0x2E, "Result GetDebugFutureThreadInfo(LastThreadContext* out_context, uint64_t* out_thread_id, Handle debug_handle, int64_t ns);"],
+ [0x2F, "Result GetLastThreadInfo(LastThreadContext* out_context, Address* out_tls_address, uint32_t* out_flags);"],
+ [0x30, "Result GetResourceLimitLimitValue(int64_t* out_limit_value, Handle resource_limit_handle, LimitableResource which);"],
+ [0x31, "Result GetResourceLimitCurrentValue(int64_t* out_current_value, Handle resource_limit_handle, LimitableResource which);"],
+ [0x32, "Result SetThreadActivity(Handle thread_handle, ThreadActivity thread_activity);"],
+ [0x33, "Result GetThreadContext3(Address out_context, Handle thread_handle);"],
+ [0x34, "Result WaitForAddress(Address address, ArbitrationType arb_type, int32_t value, int64_t timeout_ns);"],
+ [0x35, "Result SignalToAddress(Address address, SignalType signal_type, int32_t value, int32_t count);"],
+ [0x36, "void SynchronizePreemptionState();"],
+ [0x37, "Result GetResourceLimitPeakValue(int64_t* out_peak_value, Handle resource_limit_handle, LimitableResource which);"],
+
+ [0x39, "Result CreateIoPool(Handle* out_handle, IoPoolType which);"],
+ [0x3A, "Result CreateIoRegion(Handle* out_handle, Handle io_pool, PhysicalAddress physical_address, Size size, MemoryMapping mapping, MemoryPermission perm);"],
+
+ [0x3C, "void KernelDebug(KernelDebugType kern_debug_type, uint64_t arg0, uint64_t arg1, uint64_t arg2);"],
+ [0x3D, "void ChangeKernelTraceState(KernelTraceState kern_trace_state);"],
+
+ [0x40, "Result CreateSession(Handle* out_server_session_handle, Handle* out_client_session_handle, bool is_light, Address name);"],
+ [0x41, "Result AcceptSession(Handle* out_handle, Handle port);"],
+ [0x42, "Result ReplyAndReceiveLight(Handle handle);"],
+ [0x43, "Result ReplyAndReceive(int32_t* out_index, Address handles, int32_t num_handles, Handle reply_target, int64_t timeout_ns);"],
+ [0x44, "Result ReplyAndReceiveWithUserBuffer(int32_t* out_index, Address message_buffer, Size message_buffer_size, Address handles, int32_t num_handles, Handle reply_target, int64_t timeout_ns);"],
+ [0x45, "Result CreateEvent(Handle* out_write_handle, Handle* out_read_handle);"],
+ [0x46, "Result MapIoRegion(Handle io_region, Address address, Size size, MemoryPermission perm);"],
+ [0x47, "Result UnmapIoRegion(Handle io_region, Address address, Size size);"],
+ [0x48, "Result MapPhysicalMemoryUnsafe(Address address, Size size);"],
+ [0x49, "Result UnmapPhysicalMemoryUnsafe(Address address, Size size);"],
+ [0x4A, "Result SetUnsafeLimit(Size limit);"],
+ [0x4B, "Result CreateCodeMemory(Handle* out_handle, Address address, Size size);"],
+ [0x4C, "Result ControlCodeMemory(Handle code_memory_handle, CodeMemoryOperation operation, uint64_t address, uint64_t size, MemoryPermission perm);"],
+ [0x4D, "void SleepSystem();"],
+ [0x4E, "Result ReadWriteRegister(uint32_t* out_value, PhysicalAddress address, uint32_t mask, uint32_t value);"],
+ [0x4F, "Result SetProcessActivity(Handle process_handle, ProcessActivity process_activity);"],
+ [0x50, "Result CreateSharedMemory(Handle* out_handle, Size size, MemoryPermission owner_perm, MemoryPermission remote_perm);"],
+ [0x51, "Result MapTransferMemory(Handle trmem_handle, Address address, Size size, MemoryPermission owner_perm);"],
+ [0x52, "Result UnmapTransferMemory(Handle trmem_handle, Address address, Size size);"],
+ [0x53, "Result CreateInterruptEvent(Handle* out_read_handle, int32_t interrupt_id, InterruptType interrupt_type);"],
+ [0x54, "Result QueryPhysicalAddress(PhysicalMemoryInfo* out_info, Address address);"],
+ [0x55, "Result QueryIoMapping(Address* out_address, Size* out_size, PhysicalAddress physical_address, Size size);"],
+ [0x56, "Result CreateDeviceAddressSpace(Handle* out_handle, uint64_t das_address, uint64_t das_size);"],
+ [0x57, "Result AttachDeviceAddressSpace(DeviceName device_name, Handle das_handle);"],
+ [0x58, "Result DetachDeviceAddressSpace(DeviceName device_name, Handle das_handle);"],
+ [0x59, "Result MapDeviceAddressSpaceByForce(Handle das_handle, Handle process_handle, uint64_t process_address, Size size, uint64_t device_address, uint32_t option);"],
+ [0x5A, "Result MapDeviceAddressSpaceAligned(Handle das_handle, Handle process_handle, uint64_t process_address, Size size, uint64_t device_address, uint32_t option);"],
+ [0x5C, "Result UnmapDeviceAddressSpace(Handle das_handle, Handle process_handle, uint64_t process_address, Size size, uint64_t device_address);"],
+ [0x5D, "Result InvalidateProcessDataCache(Handle process_handle, uint64_t address, uint64_t size);"],
+ [0x5E, "Result StoreProcessDataCache(Handle process_handle, uint64_t address, uint64_t size);"],
+ [0x5F, "Result FlushProcessDataCache(Handle process_handle, uint64_t address, uint64_t size);"],
+ [0x60, "Result DebugActiveProcess(Handle* out_handle, uint64_t process_id);"],
+ [0x61, "Result BreakDebugProcess(Handle debug_handle);"],
+ [0x62, "Result TerminateDebugProcess(Handle debug_handle);"],
+ [0x63, "Result GetDebugEvent(Address out_info, Handle debug_handle);"],
+ [0x64, "Result ContinueDebugEvent(Handle debug_handle, uint32_t flags, Address thread_ids, int32_t num_thread_ids);"],
+ [0x65, "Result GetProcessList(int32_t* out_num_processes, Address out_process_ids, int32_t max_out_count);"],
+ [0x66, "Result GetThreadList(int32_t* out_num_threads, Address out_thread_ids, int32_t max_out_count, Handle debug_handle);"],
+ [0x67, "Result GetDebugThreadContext(Address out_context, Handle debug_handle, uint64_t thread_id, uint32_t context_flags);"],
+ [0x68, "Result SetDebugThreadContext(Handle debug_handle, uint64_t thread_id, Address context, uint32_t context_flags);"],
+ [0x69, "Result QueryDebugProcessMemory(Address out_memory_info, PageInfo* out_page_info, Handle process_handle, Address address);"],
+ [0x6A, "Result ReadDebugProcessMemory(Address buffer, Handle debug_handle, Address address, Size size);"],
+ [0x6B, "Result WriteDebugProcessMemory(Handle debug_handle, Address buffer, Address address, Size size);"],
+ [0x6C, "Result SetHardwareBreakPoint(HardwareBreakPointRegisterName name, uint64_t flags, uint64_t value);"],
+ [0x6D, "Result GetDebugThreadParam(uint64_t* out_64, uint32_t* out_32, Handle debug_handle, uint64_t thread_id, DebugThreadParam param);"],
+
+ [0x6F, "Result GetSystemInfo(uint64_t* out, SystemInfoType info_type, Handle handle, uint64_t info_subtype);"],
+ [0x70, "Result CreatePort(Handle* out_server_handle, Handle* out_client_handle, int32_t max_sessions, bool is_light, Address name);"],
+ [0x71, "Result ManageNamedPort(Handle* out_server_handle, Address name, int32_t max_sessions);"],
+ [0x72, "Result ConnectToPort(Handle* out_handle, Handle port);"],
+ [0x73, "Result SetProcessMemoryPermission(Handle process_handle, uint64_t address, uint64_t size, MemoryPermission perm);"],
+ [0x74, "Result MapProcessMemory(Address dst_address, Handle process_handle, uint64_t src_address, Size size);"],
+ [0x75, "Result UnmapProcessMemory(Address dst_address, Handle process_handle, uint64_t src_address, Size size);"],
+ [0x76, "Result QueryProcessMemory(Address out_memory_info, PageInfo* out_page_info, Handle process_handle, uint64_t address);"],
+ [0x77, "Result MapProcessCodeMemory(Handle process_handle, uint64_t dst_address, uint64_t src_address, uint64_t size);"],
+ [0x78, "Result UnmapProcessCodeMemory(Handle process_handle, uint64_t dst_address, uint64_t src_address, uint64_t size);"],
+ [0x79, "Result CreateProcess(Handle* out_handle, Address parameters, Address caps, int32_t num_caps);"],
+ [0x7A, "Result StartProcess(Handle process_handle, int32_t priority, int32_t core_id, uint64_t main_thread_stack_size);"],
+ [0x7B, "Result TerminateProcess(Handle process_handle);"],
+ [0x7C, "Result GetProcessInfo(int64_t* out_info, Handle process_handle, ProcessInfoType info_type);"],
+ [0x7D, "Result CreateResourceLimit(Handle* out_handle);"],
+ [0x7E, "Result SetResourceLimitLimitValue(Handle resource_limit_handle, LimitableResource which, int64_t limit_value);"],
+ [0x7F, "void CallSecureMonitor(SecureMonitorArguments args);"],
+
+ [0x90, "Result MapInsecureMemory(Address address, Size size);"],
+ [0x91, "Result UnmapInsecureMemory(Address address, Size size);"],
+]
+
+# These use a custom ABI, and therefore require custom wrappers
+SKIP_WRAPPERS = {
+ 0x20: "SendSyncRequestLight",
+ 0x42: "ReplyAndReceiveLight",
+ 0x7F: "CallSecureMonitor",
+}
+
+BIT_32 = 0
+BIT_64 = 1
+
+REG_SIZES = [4, 8]
+SUFFIX_NAMES = ["64From32", "64"]
+TYPE_SIZES = {
+ # SVC types
+ "ArbitrationType": 4,
+ "BreakReason": 4,
+ "CodeMemoryOperation": 4,
+ "DebugThreadParam": 4,
+ "DeviceName": 4,
+ "HardwareBreakPointRegisterName": 4,
+ "Handle": 4,
+ "InfoType": 4,
+ "InterruptType": 4,
+ "IoPoolType": 4,
+ "KernelDebugType": 4,
+ "KernelTraceState": 4,
+ "LimitableResource": 4,
+ "MemoryMapping": 4,
+ "MemoryPermission": 4,
+ "PageInfo": 4,
+ "ProcessActivity": 4,
+ "ProcessInfoType": 4,
+ "Result": 4,
+ "SignalType": 4,
+ "SystemInfoType": 4,
+ "ThreadActivity": 4,
+
+ # Arch-specific types
+ "ilp32::LastThreadContext": 16,
+ "ilp32::PhysicalMemoryInfo": 16,
+ "ilp32::SecureMonitorArguments": 32,
+ "lp64::LastThreadContext": 32,
+ "lp64::PhysicalMemoryInfo": 24,
+ "lp64::SecureMonitorArguments": 64,
+
+ # Generic types
+ "bool": 1,
+ "int32_t": 4,
+ "int64_t": 8,
+ "uint32_t": 4,
+ "uint64_t": 8,
+ "void": 0,
+}
+
+TYPE_REPLACEMENTS = {
+ "Address": ["uint32_t", "uint64_t"],
+ "LastThreadContext": ["ilp32::LastThreadContext", "lp64::LastThreadContext"],
+ "PhysicalAddress": ["uint64_t", "uint64_t"],
+ "PhysicalMemoryInfo": ["ilp32::PhysicalMemoryInfo", "lp64::PhysicalMemoryInfo"],
+ "SecureMonitorArguments": ["ilp32::SecureMonitorArguments", "lp64::SecureMonitorArguments"],
+ "Size": ["uint32_t", "uint64_t"],
+ "ThreadFunc": ["uint32_t", "uint64_t"],
+}
+
+# Statically verify that the hardcoded sizes match the intended
+# sizes in C++.
+def emit_size_check():
+ lines = []
+
+ for type, size in TYPE_SIZES.items():
+ if type != "void":
+ lines.append(f"static_assert(sizeof({type}) == {size});")
+
+ return "\n".join(lines)
+
+
+# Replaces a type with an arch-specific one, if it exists.
+def substitute_type(name, bitness):
+ if name in TYPE_REPLACEMENTS:
+ return TYPE_REPLACEMENTS[name][bitness]
+ else:
+ return name
+
+
+class Argument:
+ def __init__(self, type_name, var_name, is_output, is_outptr, is_address):
+ self.type_name = type_name
+ self.var_name = var_name
+ self.is_output = is_output
+ self.is_outptr = is_outptr
+ self.is_address = is_address
+
+
+# Parses C-style string declarations for SVCs.
+def parse_declaration(declaration, bitness):
+ return_type, rest = declaration.split(" ", 1)
+ func_name, rest = rest.split("(", 1)
+ arg_names, rest = rest.split(")", 1)
+ argument_types = []
+
+ return_type = substitute_type(return_type, bitness)
+ assert return_type in TYPE_SIZES, f"Unknown type '{return_type}'"
+
+ if arg_names:
+ for arg_name in arg_names.split(", "):
+ type_name, var_name = arg_name.replace("*", "").split(" ", 1)
+
+ # All outputs must contain out_ in the name.
+ is_output = var_name == "out" or var_name.find("out_") != -1
+
+ # User-pointer outputs are not written to registers.
+ is_outptr = is_output and arg_name.find("*") == -1
+
+ # Special handling is performed for output addresses to avoid awkwardness
+ # in conversion for the 32-bit equivalents.
+ is_address = is_output and not is_outptr and \
+ type_name in ["Address", "Size"]
+ type_name = substitute_type(type_name, bitness)
+
+ assert type_name in TYPE_SIZES, f"Unknown type '{type_name}'"
+
+ argument_types.append(
+ Argument(type_name, var_name, is_output, is_outptr, is_address))
+
+ return (return_type, func_name, argument_types)
+
+
+class RegisterAllocator:
+ def __init__(self, num_regs, byte_size, parameter_count):
+ self.registers = {}
+ self.num_regs = num_regs
+ self.byte_size = byte_size
+ self.parameter_count = parameter_count
+
+ # Mark the given register as allocated, for use in layout
+ # calculation if the NGRN exceeds the ABI parameter count.
+ def allocate(self, i):
+ assert i not in self.registers, f"Register R{i} already allocated"
+ self.registers[i] = True
+ return i
+
+ # Calculate the next available location for a register;
+ # the NGRN has exceeded the ABI parameter count.
+ def allocate_first_free(self):
+ for i in range(0, self.num_regs):
+ if i in self.registers:
+ continue
+
+ self.allocate(i)
+ return i
+
+ assert False, "No registers available"
+
+ # Add a single register at the given NGRN.
+ # If the index exceeds the ABI parameter count, try to find a
+ # location to add it. Returns the output location and increment.
+ def add_single(self, ngrn):
+ if ngrn >= self.parameter_count:
+ return (self.allocate_first_free(), 0)
+ else:
+ return (self.allocate(ngrn), 1)
+
+ # Add registers at the given NGRN for a data type of
+ # the given size. Returns the output locations and increment.
+ def add(self, ngrn, data_size, align=True):
+ if data_size <= self.byte_size:
+ r, i = self.add_single(ngrn)
+ return ([r], i)
+
+ regs = []
+ inc = ngrn % 2 if align else 0
+ remaining_size = data_size
+ while remaining_size > 0:
+ r, i = self.add_single(ngrn + inc)
+ regs.append(r)
+ inc += i
+ remaining_size -= self.byte_size
+
+ return (regs, inc)
+
+
+def reg_alloc(bitness):
+ if bitness == 0:
+ # aapcs32: 4 4-byte registers
+ return RegisterAllocator(8, 4, 4)
+ elif bitness == 1:
+ # aapcs64: 8 8-byte registers
+ return RegisterAllocator(8, 8, 8)
+
+
+# Converts a parsed SVC declaration into register lists for
+# the return value, outputs, and inputs.
+def get_registers(parse_result, bitness):
+ output_alloc = reg_alloc(bitness)
+ input_alloc = reg_alloc(bitness)
+ return_type, _, arguments = parse_result
+
+ return_write = []
+ output_writes = []
+ input_reads = []
+
+ input_ngrn = 0
+ output_ngrn = 0
+
+ # Run the input calculation.
+ for arg in arguments:
+ if arg.is_output and not arg.is_outptr:
+ input_ngrn += 1
+ continue
+
+ regs, increment = input_alloc.add(
+ input_ngrn, TYPE_SIZES[arg.type_name], align=True)
+ input_reads.append([arg.type_name, arg.var_name, regs])
+ input_ngrn += increment
+
+ # Include the return value if this SVC returns a value.
+ if return_type != "void":
+ regs, increment = output_alloc.add(
+ output_ngrn, TYPE_SIZES[return_type], align=False)
+ return_write.append([return_type, regs])
+ output_ngrn += increment
+
+ # Run the output calculation.
+ for arg in arguments:
+ if not arg.is_output or arg.is_outptr:
+ continue
+
+ regs, increment = output_alloc.add(
+ output_ngrn, TYPE_SIZES[arg.type_name], align=False)
+ output_writes.append(
+ [arg.type_name, arg.var_name, regs, arg.is_address])
+ output_ngrn += increment
+
+ return (return_write, output_writes, input_reads)
+
+
+# Collects possibly multiple source registers into the named C++ value.
+def emit_gather(sources, name, type_name, reg_size):
+ get_fn = f"GetReg{reg_size*8}"
+
+ if len(sources) == 1:
+ s, = sources
+ line = f"{name} = Convert<{type_name}>({get_fn}(system, {s}));"
+ return [line]
+
+ var_type = f"std::array<uint{reg_size*8}_t, {len(sources)}>"
+ lines = [
+ f"{var_type} {name}_gather{{}};"
+ ]
+ for i in range(0, len(sources)):
+ lines.append(
+ f"{name}_gather[{i}] = {get_fn}(system, {sources[i]});")
+
+ lines.append(f"{name} = Convert<{type_name}>({name}_gather);")
+ return lines
+
+
+# Produces one or more statements which assign the named C++ value
+# into possibly multiple registers.
+def emit_scatter(destinations, name, reg_size):
+ set_fn = f"SetReg{reg_size*8}"
+ reg_type = f"uint{reg_size*8}_t"
+
+ if len(destinations) == 1:
+ d, = destinations
+ line = f"{set_fn}(system, {d}, Convert<{reg_type}>({name}));"
+ return [line]
+
+ var_type = f"std::array<{reg_type}, {len(destinations)}>"
+ lines = [
+ f"auto {name}_scatter = Convert<{var_type}>({name});"
+ ]
+
+ for i in range(0, len(destinations)):
+ lines.append(
+ f"{set_fn}(system, {destinations[i]}, {name}_scatter[{i}]);")
+
+ return lines
+
+
+def emit_lines(lines, indent=' '):
+ output_lines = []
+ first = True
+ for line in lines:
+ if line and not first:
+ output_lines.append(indent + line)
+ else:
+ output_lines.append(line)
+ first = False
+
+ return "\n".join(output_lines)
+
+
+# Emit a C++ function to wrap a guest SVC.
+def emit_wrapper(wrapped_fn, suffix, register_info, arguments, byte_size):
+ return_write, output_writes, input_reads = register_info
+ lines = [
+ f"static void SvcWrap_{wrapped_fn}{suffix}(Core::System& system) {{"
+ ]
+
+ # Get everything ready.
+ for return_type, _ in return_write:
+ lines.append(f"{return_type} ret{{}};")
+ if return_write:
+ lines.append("")
+
+ for output_type, var_name, _, is_address in output_writes:
+ output_type = "uintptr_t" if is_address else output_type
+ lines.append(f"{output_type} {var_name}{{}};")
+ for input_type, var_name, _ in input_reads:
+ lines.append(f"{input_type} {var_name}{{}};")
+
+ if output_writes or input_reads:
+ lines.append("")
+
+ for input_type, var_name, sources in input_reads:
+ lines += emit_gather(sources, var_name, input_type, byte_size)
+ if input_reads:
+ lines.append("")
+
+ # Build the call.
+ call_arguments = ["system"]
+ for arg in arguments:
+ if arg.is_output and not arg.is_outptr:
+ call_arguments.append(f"&{arg.var_name}")
+ else:
+ call_arguments.append(arg.var_name)
+
+ line = ""
+ if return_write:
+ line += "ret = "
+
+ line += f"{wrapped_fn}{suffix}({', '.join(call_arguments)});"
+ lines.append(line)
+
+ if return_write or output_writes:
+ lines.append("")
+
+ # Write back the return value and outputs.
+ for _, destinations in return_write:
+ lines += emit_scatter(destinations, "ret", byte_size)
+ for _, var_name, destinations, _ in output_writes:
+ lines += emit_scatter(destinations, var_name, byte_size)
+
+ # Finish.
+ return emit_lines(lines) + "\n}"
+
+
+COPYRIGHT = """\
+// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+// This file is automatically generated using svc_generator.py.
+"""
+
+PROLOGUE_H = """
+#pragma once
+
+namespace Core {
+class System;
+}
+
+#include "common/common_types.h"
+#include "core/hle/kernel/svc_types.h"
+#include "core/hle/result.h"
+
+namespace Kernel::Svc {
+
+// clang-format off
+"""
+
+EPILOGUE_H = """
+// clang-format on
+
+// Custom ABI.
+Result ReplyAndReceiveLight(Core::System& system, Handle handle, uint32_t* args);
+Result ReplyAndReceiveLight64From32(Core::System& system, Handle handle, uint32_t* args);
+Result ReplyAndReceiveLight64(Core::System& system, Handle handle, uint32_t* args);
+
+Result SendSyncRequestLight(Core::System& system, Handle session_handle, uint32_t* args);
+Result SendSyncRequestLight64From32(Core::System& system, Handle session_handle, uint32_t* args);
+Result SendSyncRequestLight64(Core::System& system, Handle session_handle, uint32_t* args);
+
+void CallSecureMonitor(Core::System& system, lp64::SecureMonitorArguments* args);
+void CallSecureMonitor64From32(Core::System& system, ilp32::SecureMonitorArguments* args);
+void CallSecureMonitor64(Core::System& system, lp64::SecureMonitorArguments* args);
+
+// Defined in svc_light_ipc.cpp.
+void SvcWrap_ReplyAndReceiveLight64From32(Core::System& system);
+void SvcWrap_ReplyAndReceiveLight64(Core::System& system);
+
+void SvcWrap_SendSyncRequestLight64From32(Core::System& system);
+void SvcWrap_SendSyncRequestLight64(Core::System& system);
+
+// Defined in svc_secure_monitor_call.cpp.
+void SvcWrap_CallSecureMonitor64From32(Core::System& system);
+void SvcWrap_CallSecureMonitor64(Core::System& system);
+
+// Perform a supervisor call by index.
+void Call(Core::System& system, u32 imm);
+
+} // namespace Kernel::Svc
+"""
+
+PROLOGUE_CPP = """
+#include <type_traits>
+
+#include "core/arm/arm_interface.h"
+#include "core/core.h"
+#include "core/hle/kernel/k_process.h"
+#include "core/hle/kernel/svc.h"
+
+namespace Kernel::Svc {
+
+static uint32_t GetReg32(Core::System& system, int n) {
+ return static_cast<uint32_t>(system.CurrentArmInterface().GetReg(n));
+}
+
+static void SetReg32(Core::System& system, int n, uint32_t result) {
+ system.CurrentArmInterface().SetReg(n, static_cast<uint64_t>(result));
+}
+
+static uint64_t GetReg64(Core::System& system, int n) {
+ return system.CurrentArmInterface().GetReg(n);
+}
+
+static void SetReg64(Core::System& system, int n, uint64_t result) {
+ system.CurrentArmInterface().SetReg(n, result);
+}
+
+// Like bit_cast, but handles the case when the source and dest
+// are differently-sized.
+template <typename To, typename From>
+ requires(std::is_trivial_v<To> && std::is_trivially_copyable_v<From>)
+static To Convert(const From& from) {
+ To to{};
+
+ if constexpr (sizeof(To) >= sizeof(From)) {
+ std::memcpy(&to, &from, sizeof(From));
+ } else {
+ std::memcpy(&to, &from, sizeof(To));
+ }
+
+ return to;
+}
+
+// clang-format off
+"""
+
+EPILOGUE_CPP = """
+// clang-format on
+
+void Call(Core::System& system, u32 imm) {
+ auto& kernel = system.Kernel();
+ kernel.EnterSVCProfile();
+
+ if (GetCurrentProcess(system.Kernel()).Is64BitProcess()) {
+ Call64(system, imm);
+ } else {
+ Call32(system, imm);
+ }
+
+ kernel.ExitSVCProfile();
+}
+
+} // namespace Kernel::Svc
+"""
+
+
+def emit_call(bitness, names, suffix):
+ bit_size = REG_SIZES[bitness]*8
+ indent = " "
+ lines = [
+ f"static void Call{bit_size}(Core::System& system, u32 imm) {{",
+ f"{indent}switch (static_cast<SvcId>(imm)) {{"
+ ]
+
+ for _, name in names:
+ lines.append(f"{indent}case SvcId::{name}:")
+ lines.append(f"{indent*2}return SvcWrap_{name}{suffix}(system);")
+
+ lines.append(f"{indent}default:")
+ lines.append(
+ f"{indent*2}LOG_CRITICAL(Kernel_SVC, \"Unknown SVC {{:x}}!\", imm);")
+ lines.append(f"{indent*2}break;")
+ lines.append(f"{indent}}}")
+ lines.append("}")
+
+ return "\n".join(lines)
+
+
+def build_fn_declaration(return_type, name, arguments):
+ arg_list = ["Core::System& system"]
+ for arg in arguments:
+ type_name = "uintptr_t" if arg.is_address else arg.type_name
+ pointer = "*" if arg.is_output and not arg.is_outptr else ""
+ arg_list.append(f"{type_name}{pointer} {arg.var_name}")
+
+ return f"{return_type} {name}({', '.join(arg_list)});"
+
+
+def build_enum_declarations():
+ lines = ["enum class SvcId : u32 {"]
+ indent = " "
+
+ for imm, decl in SVCS:
+ _, name, _ = parse_declaration(decl, BIT_64)
+ lines.append(f"{indent}{name} = {hex(imm)},")
+
+ lines.append("};")
+ return "\n".join(lines)
+
+
+def main():
+ arch_fw_declarations = [[], []]
+ svc_fw_declarations = []
+ wrapper_fns = []
+ names = []
+
+ for imm, decl in SVCS:
+ return_type, name, arguments = parse_declaration(decl, BIT_64)
+
+ if imm not in SKIP_WRAPPERS:
+ svc_fw_declarations.append(
+ build_fn_declaration(return_type, name, arguments))
+
+ names.append([imm, name])
+
+ for bitness in range(2):
+ byte_size = REG_SIZES[bitness]
+ suffix = SUFFIX_NAMES[bitness]
+
+ for imm, decl in SVCS:
+ if imm in SKIP_WRAPPERS:
+ continue
+
+ parse_result = parse_declaration(decl, bitness)
+ return_type, name, arguments = parse_result
+
+ register_info = get_registers(parse_result, bitness)
+ wrapper_fns.append(
+ emit_wrapper(name, suffix, register_info, arguments, byte_size))
+ arch_fw_declarations[bitness].append(
+ build_fn_declaration(return_type, name + suffix, arguments))
+
+ call_32 = emit_call(BIT_32, names, SUFFIX_NAMES[BIT_32])
+ call_64 = emit_call(BIT_64, names, SUFFIX_NAMES[BIT_64])
+ enum_decls = build_enum_declarations()
+
+ with open("svc.h", "w") as f:
+ f.write(COPYRIGHT)
+ f.write(PROLOGUE_H)
+ f.write("\n".join(svc_fw_declarations))
+ f.write("\n\n")
+ f.write("\n".join(arch_fw_declarations[BIT_32]))
+ f.write("\n\n")
+ f.write("\n".join(arch_fw_declarations[BIT_64]))
+ f.write("\n\n")
+ f.write(enum_decls)
+ f.write(EPILOGUE_H)
+
+ with open("svc.cpp", "w") as f:
+ f.write(COPYRIGHT)
+ f.write(PROLOGUE_CPP)
+ f.write(emit_size_check())
+ f.write("\n\n")
+ f.write("\n\n".join(wrapper_fns))
+ f.write("\n\n")
+ f.write(call_32)
+ f.write("\n\n")
+ f.write(call_64)
+ f.write(EPILOGUE_CPP)
+
+ print(f"Done (emitted {len(names)} definitions)")
+
+
+if __name__ == "__main__":
+ main()
diff --git a/src/core/hle/kernel/svc_results.h b/src/core/hle/kernel/svc_results.h
index b7ca53085..e1ad78607 100644
--- a/src/core/hle/kernel/svc_results.h
+++ b/src/core/hle/kernel/svc_results.h
@@ -11,6 +11,7 @@ namespace Kernel {
constexpr Result ResultOutOfSessions{ErrorModule::Kernel, 7};
constexpr Result ResultInvalidArgument{ErrorModule::Kernel, 14};
+constexpr Result ResultNotImplemented{ErrorModule::Kernel, 33};
constexpr Result ResultNoSynchronizationObject{ErrorModule::Kernel, 57};
constexpr Result ResultTerminationRequested{ErrorModule::Kernel, 59};
constexpr Result ResultInvalidSize{ErrorModule::Kernel, 101};
diff --git a/src/core/hle/kernel/svc_types.h b/src/core/hle/kernel/svc_types.h
index 33eebcef6..542c13461 100644
--- a/src/core/hle/kernel/svc_types.h
+++ b/src/core/hle/kernel/svc_types.h
@@ -3,6 +3,9 @@
#pragma once
+#include <bitset>
+
+#include "common/bit_field.h"
#include "common/common_funcs.h"
#include "common/common_types.h"
@@ -165,6 +168,7 @@ enum class BreakReason : u32 {
NotificationOnlyFlag = 0x80000000,
};
+DECLARE_ENUM_FLAG_OPERATORS(BreakReason);
enum class DebugEvent : u32 {
CreateProcess = 0,
@@ -496,6 +500,19 @@ enum class MemoryMapping : u32 {
Memory = 2,
};
+enum class MapDeviceAddressSpaceFlag : u32 {
+ None = (0U << 0),
+ NotIoRegister = (1U << 0),
+};
+DECLARE_ENUM_FLAG_OPERATORS(MapDeviceAddressSpaceFlag);
+
+union MapDeviceAddressSpaceOption {
+ u32 raw;
+ BitField<0, 16, MemoryPermission> permission;
+ BitField<16, 1, MapDeviceAddressSpaceFlag> flags;
+ BitField<17, 15, u32> reserved;
+};
+
enum class KernelDebugType : u32 {
Thread = 0,
ThreadCallStack = 1,
@@ -580,6 +597,11 @@ enum class ProcessInfoType : u32 {
ProcessState = 0,
};
+enum class ProcessActivity : u32 {
+ Runnable,
+ Paused,
+};
+
struct CreateProcessParameter {
std::array<char, 12> name;
u32 version;
@@ -592,4 +614,12 @@ struct CreateProcessParameter {
};
static_assert(sizeof(CreateProcessParameter) == 0x30);
+constexpr size_t NumSupervisorCalls = 0xC0;
+using SvcAccessFlagSet = std::bitset<NumSupervisorCalls>;
+
+enum class InitialProcessIdRangeInfo : u64 {
+ Minimum = 0,
+ Maximum = 1,
+};
+
} // 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..3eb95aa7b
--- /dev/null
+++ b/src/core/hle/kernel/svc_version.h
@@ -0,0 +1,58 @@
+// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#pragma once
+
+#include "common/bit_field.h"
+#include "common/common_types.h"
+#include "common/literals.h"
+
+namespace Kernel::Svc {
+
+constexpr inline u32 ConvertToSvcMajorVersion(u32 sdk) {
+ return sdk + 4;
+}
+constexpr inline u32 ConvertToSdkMajorVersion(u32 svc) {
+ return svc - 4;
+}
+
+constexpr inline u32 ConvertToSvcMinorVersion(u32 sdk) {
+ return sdk;
+}
+constexpr inline u32 ConvertToSdkMinorVersion(u32 svc) {
+ return svc;
+}
+
+union KernelVersion {
+ u32 value;
+ BitField<0, 4, u32> minor_version;
+ BitField<4, 13, u32> major_version;
+};
+
+constexpr inline u32 EncodeKernelVersion(u32 major, u32 minor) {
+ return decltype(KernelVersion::minor_version)::FormatValue(minor) |
+ decltype(KernelVersion::major_version)::FormatValue(major);
+}
+
+constexpr inline u32 GetKernelMajorVersion(u32 encoded) {
+ return decltype(KernelVersion::major_version)::ExtractValue(encoded);
+}
+
+constexpr inline u32 GetKernelMinorVersion(u32 encoded) {
+ return decltype(KernelVersion::minor_version)::ExtractValue(encoded);
+}
+
+// Nintendo doesn't support programs targeting SVC versions < 3.0.
+constexpr inline u32 RequiredKernelMajorVersion = 3;
+constexpr inline u32 RequiredKernelMinorVersion = 0;
+constexpr inline u32 RequiredKernelVersion =
+ EncodeKernelVersion(RequiredKernelMajorVersion, RequiredKernelMinorVersion);
+
+// This is the highest SVC version supported, to be updated on new kernel releases.
+// NOTE: Official kernel versions have SVC major = SDK major + 4, SVC minor = SDK minor.
+constexpr inline u32 SupportedKernelMajorVersion = ConvertToSvcMajorVersion(15);
+constexpr inline u32 SupportedKernelMinorVersion = ConvertToSvcMinorVersion(3);
+constexpr inline u32 SupportedKernelVersion =
+ EncodeKernelVersion(SupportedKernelMajorVersion, SupportedKernelMinorVersion);
+
+} // namespace Kernel::Svc
diff --git a/src/core/hle/kernel/svc_wrap.h b/src/core/hle/kernel/svc_wrap.h
deleted file mode 100644
index 1ea8c7fbc..000000000
--- a/src/core/hle/kernel/svc_wrap.h
+++ /dev/null
@@ -1,733 +0,0 @@
-// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
-// SPDX-License-Identifier: GPL-2.0-or-later
-
-#pragma once
-
-#include "common/common_types.h"
-#include "core/arm/arm_interface.h"
-#include "core/core.h"
-#include "core/hle/kernel/svc_types.h"
-#include "core/hle/result.h"
-#include "core/memory.h"
-
-namespace Kernel {
-
-static inline u64 Param(const Core::System& system, int n) {
- return system.CurrentArmInterface().GetReg(n);
-}
-
-static inline u32 Param32(const Core::System& system, int n) {
- return static_cast<u32>(system.CurrentArmInterface().GetReg(n));
-}
-
-/**
- * HLE a function return from the current ARM userland process
- * @param system System context
- * @param result Result to return
- */
-static inline void FuncReturn(Core::System& system, u64 result) {
- system.CurrentArmInterface().SetReg(0, result);
-}
-
-static inline void FuncReturn32(Core::System& system, u32 result) {
- system.CurrentArmInterface().SetReg(0, (u64)result);
-}
-
-////////////////////////////////////////////////////////////////////////////////////////////////////
-// Function wrappers that return type Result
-
-template <Result func(Core::System&, u64)>
-void SvcWrap64(Core::System& system) {
- FuncReturn(system, func(system, Param(system, 0)).raw);
-}
-
-template <Result func(Core::System&, u64, u64)>
-void SvcWrap64(Core::System& system) {
- FuncReturn(system, func(system, Param(system, 0), Param(system, 1)).raw);
-}
-
-template <Result func(Core::System&, u32)>
-void SvcWrap64(Core::System& system) {
- FuncReturn(system, func(system, static_cast<u32>(Param(system, 0))).raw);
-}
-
-template <Result func(Core::System&, u32, u32)>
-void SvcWrap64(Core::System& system) {
- FuncReturn(
- system,
- func(system, static_cast<u32>(Param(system, 0)), static_cast<u32>(Param(system, 1))).raw);
-}
-
-// Used by SetThreadActivity
-template <Result func(Core::System&, Handle, Svc::ThreadActivity)>
-void SvcWrap64(Core::System& system) {
- FuncReturn(system, func(system, static_cast<u32>(Param(system, 0)),
- static_cast<Svc::ThreadActivity>(Param(system, 1)))
- .raw);
-}
-
-template <Result func(Core::System&, u32, u64, u64, u64)>
-void SvcWrap64(Core::System& system) {
- FuncReturn(system, func(system, static_cast<u32>(Param(system, 0)), Param(system, 1),
- Param(system, 2), Param(system, 3))
- .raw);
-}
-
-// Used by MapProcessMemory and UnmapProcessMemory
-template <Result func(Core::System&, u64, u32, u64, u64)>
-void SvcWrap64(Core::System& system) {
- FuncReturn(system, func(system, Param(system, 0), static_cast<u32>(Param(system, 1)),
- Param(system, 2), Param(system, 3))
- .raw);
-}
-
-// Used by ControlCodeMemory
-template <Result func(Core::System&, Handle, u32, VAddr, size_t, Svc::MemoryPermission)>
-void SvcWrap64(Core::System& system) {
- FuncReturn(system, func(system, static_cast<Handle>(Param(system, 0)),
- static_cast<u32>(Param(system, 1)), Param(system, 2), Param(system, 3),
- static_cast<Svc::MemoryPermission>(Param(system, 4)))
- .raw);
-}
-
-template <Result func(Core::System&, u32*)>
-void SvcWrap64(Core::System& system) {
- u32 param = 0;
- const u32 retval = func(system, &param).raw;
- system.CurrentArmInterface().SetReg(1, param);
- FuncReturn(system, retval);
-}
-
-template <Result func(Core::System&, u32*, u32)>
-void SvcWrap64(Core::System& system) {
- u32 param_1 = 0;
- const u32 retval = func(system, &param_1, static_cast<u32>(Param(system, 1))).raw;
- system.CurrentArmInterface().SetReg(1, param_1);
- FuncReturn(system, retval);
-}
-
-template <Result func(Core::System&, u32*, u32*)>
-void SvcWrap64(Core::System& system) {
- u32 param_1 = 0;
- u32 param_2 = 0;
- const u32 retval = func(system, &param_1, &param_2).raw;
-
- auto& arm_interface = system.CurrentArmInterface();
- arm_interface.SetReg(1, param_1);
- arm_interface.SetReg(2, param_2);
-
- FuncReturn(system, retval);
-}
-
-template <Result func(Core::System&, u32*, u64)>
-void SvcWrap64(Core::System& system) {
- u32 param_1 = 0;
- const u32 retval = func(system, &param_1, Param(system, 1)).raw;
- system.CurrentArmInterface().SetReg(1, param_1);
- FuncReturn(system, retval);
-}
-
-template <Result func(Core::System&, u32*, u64, u32)>
-void SvcWrap64(Core::System& system) {
- u32 param_1 = 0;
- const u32 retval =
- func(system, &param_1, Param(system, 1), static_cast<u32>(Param(system, 2))).raw;
-
- system.CurrentArmInterface().SetReg(1, param_1);
- FuncReturn(system, retval);
-}
-
-template <Result func(Core::System&, u64*, u32)>
-void SvcWrap64(Core::System& system) {
- u64 param_1 = 0;
- const u32 retval = func(system, &param_1, static_cast<u32>(Param(system, 1))).raw;
-
- system.CurrentArmInterface().SetReg(1, param_1);
- FuncReturn(system, retval);
-}
-
-template <Result func(Core::System&, u64, u32)>
-void SvcWrap64(Core::System& system) {
- FuncReturn(system, func(system, Param(system, 0), static_cast<u32>(Param(system, 1))).raw);
-}
-
-template <Result func(Core::System&, u64*, u64)>
-void SvcWrap64(Core::System& system) {
- u64 param_1 = 0;
- const u32 retval = func(system, &param_1, Param(system, 1)).raw;
-
- system.CurrentArmInterface().SetReg(1, param_1);
- FuncReturn(system, retval);
-}
-
-template <Result func(Core::System&, u64*, u32, u32)>
-void SvcWrap64(Core::System& system) {
- u64 param_1 = 0;
- const u32 retval = func(system, &param_1, static_cast<u32>(Param(system, 1)),
- static_cast<u32>(Param(system, 2)))
- .raw;
-
- system.CurrentArmInterface().SetReg(1, param_1);
- FuncReturn(system, retval);
-}
-
-// Used by GetResourceLimitLimitValue.
-template <Result func(Core::System&, u64*, Handle, LimitableResource)>
-void SvcWrap64(Core::System& system) {
- u64 param_1 = 0;
- const u32 retval = func(system, &param_1, static_cast<Handle>(Param(system, 1)),
- static_cast<LimitableResource>(Param(system, 2)))
- .raw;
-
- system.CurrentArmInterface().SetReg(1, param_1);
- FuncReturn(system, retval);
-}
-
-template <Result func(Core::System&, u32, u64)>
-void SvcWrap64(Core::System& system) {
- FuncReturn(system, func(system, static_cast<u32>(Param(system, 0)), Param(system, 1)).raw);
-}
-
-// Used by SetResourceLimitLimitValue
-template <Result func(Core::System&, Handle, LimitableResource, u64)>
-void SvcWrap64(Core::System& system) {
- FuncReturn(system, func(system, static_cast<Handle>(Param(system, 0)),
- static_cast<LimitableResource>(Param(system, 1)), Param(system, 2))
- .raw);
-}
-
-// Used by SetThreadCoreMask
-template <Result func(Core::System&, Handle, s32, u64)>
-void SvcWrap64(Core::System& system) {
- FuncReturn(system, func(system, static_cast<u32>(Param(system, 0)),
- static_cast<s32>(Param(system, 1)), Param(system, 2))
- .raw);
-}
-
-// Used by GetThreadCoreMask
-template <Result func(Core::System&, Handle, s32*, u64*)>
-void SvcWrap64(Core::System& system) {
- s32 param_1 = 0;
- u64 param_2 = 0;
- const Result retval = func(system, static_cast<u32>(Param(system, 2)), &param_1, &param_2);
-
- system.CurrentArmInterface().SetReg(1, param_1);
- system.CurrentArmInterface().SetReg(2, param_2);
- FuncReturn(system, retval.raw);
-}
-
-template <Result func(Core::System&, u64, u64, u32, u32)>
-void SvcWrap64(Core::System& system) {
- FuncReturn(system, func(system, Param(system, 0), Param(system, 1),
- static_cast<u32>(Param(system, 2)), static_cast<u32>(Param(system, 3)))
- .raw);
-}
-
-template <Result func(Core::System&, u64, u64, u32, u64)>
-void SvcWrap64(Core::System& system) {
- FuncReturn(system, func(system, Param(system, 0), Param(system, 1),
- static_cast<u32>(Param(system, 2)), Param(system, 3))
- .raw);
-}
-
-template <Result func(Core::System&, u32, u64, u32)>
-void SvcWrap64(Core::System& system) {
- FuncReturn(system, func(system, static_cast<u32>(Param(system, 0)), Param(system, 1),
- static_cast<u32>(Param(system, 2)))
- .raw);
-}
-
-template <Result func(Core::System&, u64, u64, u64)>
-void SvcWrap64(Core::System& system) {
- FuncReturn(system, func(system, Param(system, 0), Param(system, 1), Param(system, 2)).raw);
-}
-
-template <Result func(Core::System&, u64, u64, u32)>
-void SvcWrap64(Core::System& system) {
- FuncReturn(
- system,
- func(system, Param(system, 0), Param(system, 1), static_cast<u32>(Param(system, 2))).raw);
-}
-
-// Used by SetMemoryPermission
-template <Result func(Core::System&, u64, u64, Svc::MemoryPermission)>
-void SvcWrap64(Core::System& system) {
- FuncReturn(system, func(system, Param(system, 0), Param(system, 1),
- static_cast<Svc::MemoryPermission>(Param(system, 2)))
- .raw);
-}
-
-// Used by MapSharedMemory
-template <Result func(Core::System&, Handle, u64, u64, Svc::MemoryPermission)>
-void SvcWrap64(Core::System& system) {
- FuncReturn(system, func(system, static_cast<Handle>(Param(system, 0)), Param(system, 1),
- Param(system, 2), static_cast<Svc::MemoryPermission>(Param(system, 3)))
- .raw);
-}
-
-template <Result func(Core::System&, u32, u64, u64)>
-void SvcWrap64(Core::System& system) {
- FuncReturn(
- system,
- func(system, static_cast<u32>(Param(system, 0)), Param(system, 1), Param(system, 2)).raw);
-}
-
-// Used by WaitSynchronization
-template <Result func(Core::System&, s32*, u64, s32, s64)>
-void SvcWrap64(Core::System& system) {
- s32 param_1 = 0;
- const u32 retval = func(system, &param_1, Param(system, 1), static_cast<s32>(Param(system, 2)),
- static_cast<s64>(Param(system, 3)))
- .raw;
-
- system.CurrentArmInterface().SetReg(1, param_1);
- FuncReturn(system, retval);
-}
-
-template <Result func(Core::System&, u64, u64, u32, s64)>
-void SvcWrap64(Core::System& system) {
- FuncReturn(system, func(system, Param(system, 0), Param(system, 1),
- static_cast<u32>(Param(system, 2)), static_cast<s64>(Param(system, 3)))
- .raw);
-}
-
-// Used by GetInfo
-template <Result func(Core::System&, u64*, u64, Handle, u64)>
-void SvcWrap64(Core::System& system) {
- u64 param_1 = 0;
- const u32 retval = func(system, &param_1, Param(system, 1),
- static_cast<Handle>(Param(system, 2)), Param(system, 3))
- .raw;
-
- system.CurrentArmInterface().SetReg(1, param_1);
- FuncReturn(system, retval);
-}
-
-template <Result func(Core::System&, u32*, u64, u64, u64, u32, s32)>
-void SvcWrap64(Core::System& system) {
- u32 param_1 = 0;
- const u32 retval = func(system, &param_1, Param(system, 1), Param(system, 2), Param(system, 3),
- static_cast<u32>(Param(system, 4)), static_cast<s32>(Param(system, 5)))
- .raw;
-
- system.CurrentArmInterface().SetReg(1, param_1);
- FuncReturn(system, retval);
-}
-
-// Used by CreateTransferMemory
-template <Result func(Core::System&, Handle*, u64, u64, Svc::MemoryPermission)>
-void SvcWrap64(Core::System& system) {
- u32 param_1 = 0;
- const u32 retval = func(system, &param_1, Param(system, 1), Param(system, 2),
- static_cast<Svc::MemoryPermission>(Param(system, 3)))
- .raw;
-
- system.CurrentArmInterface().SetReg(1, param_1);
- FuncReturn(system, retval);
-}
-
-// Used by CreateCodeMemory
-template <Result func(Core::System&, Handle*, VAddr, size_t)>
-void SvcWrap64(Core::System& system) {
- u32 param_1 = 0;
- const u32 retval = func(system, &param_1, Param(system, 1), Param(system, 2)).raw;
-
- system.CurrentArmInterface().SetReg(1, param_1);
- FuncReturn(system, retval);
-}
-
-template <Result func(Core::System&, Handle*, u64, u32, u32)>
-void SvcWrap64(Core::System& system) {
- u32 param_1 = 0;
- const u32 retval = func(system, &param_1, Param(system, 1), static_cast<u32>(Param(system, 2)),
- static_cast<u32>(Param(system, 3)))
- .raw;
-
- system.CurrentArmInterface().SetReg(1, param_1);
- FuncReturn(system, retval);
-}
-
-// Used by CreateSession
-template <Result func(Core::System&, Handle*, Handle*, u32, u64)>
-void SvcWrap64(Core::System& system) {
- Handle param_1 = 0;
- Handle param_2 = 0;
- const u32 retval = func(system, &param_1, &param_2, static_cast<u32>(Param(system, 2)),
- static_cast<u32>(Param(system, 3)))
- .raw;
-
- system.CurrentArmInterface().SetReg(1, param_1);
- system.CurrentArmInterface().SetReg(2, param_2);
- FuncReturn(system, retval);
-}
-
-// Used by ReplyAndReceive
-template <Result func(Core::System&, s32*, Handle*, s32, Handle, s64)>
-void SvcWrap64(Core::System& system) {
- s32 param_1 = 0;
- s32 num_handles = static_cast<s32>(Param(system, 2));
-
- std::vector<Handle> handles(num_handles);
- system.Memory().ReadBlock(Param(system, 1), handles.data(), num_handles * sizeof(Handle));
-
- const u32 retval = func(system, &param_1, handles.data(), num_handles,
- static_cast<s32>(Param(system, 3)), static_cast<s64>(Param(system, 4)))
- .raw;
-
- system.CurrentArmInterface().SetReg(1, param_1);
- FuncReturn(system, retval);
-}
-
-// Used by WaitForAddress
-template <Result func(Core::System&, u64, Svc::ArbitrationType, s32, s64)>
-void SvcWrap64(Core::System& system) {
- FuncReturn(system,
- func(system, Param(system, 0), static_cast<Svc::ArbitrationType>(Param(system, 1)),
- static_cast<s32>(Param(system, 2)), static_cast<s64>(Param(system, 3)))
- .raw);
-}
-
-// Used by SignalToAddress
-template <Result func(Core::System&, u64, Svc::SignalType, s32, s32)>
-void SvcWrap64(Core::System& system) {
- FuncReturn(system,
- func(system, Param(system, 0), static_cast<Svc::SignalType>(Param(system, 1)),
- static_cast<s32>(Param(system, 2)), static_cast<s32>(Param(system, 3)))
- .raw);
-}
-
-////////////////////////////////////////////////////////////////////////////////////////////////////
-// Function wrappers that return type u32
-
-template <u32 func(Core::System&)>
-void SvcWrap64(Core::System& system) {
- FuncReturn(system, func(system));
-}
-
-////////////////////////////////////////////////////////////////////////////////////////////////////
-// Function wrappers that return type u64
-
-template <u64 func(Core::System&)>
-void SvcWrap64(Core::System& system) {
- FuncReturn(system, func(system));
-}
-
-////////////////////////////////////////////////////////////////////////////////////////////////////
-/// Function wrappers that return type void
-
-template <void func(Core::System&)>
-void SvcWrap64(Core::System& system) {
- func(system);
-}
-
-template <void func(Core::System&, u32)>
-void SvcWrap64(Core::System& system) {
- func(system, static_cast<u32>(Param(system, 0)));
-}
-
-template <void func(Core::System&, u32, u64, u64, u64)>
-void SvcWrap64(Core::System& system) {
- func(system, static_cast<u32>(Param(system, 0)), Param(system, 1), Param(system, 2),
- Param(system, 3));
-}
-
-template <void func(Core::System&, s64)>
-void SvcWrap64(Core::System& system) {
- func(system, static_cast<s64>(Param(system, 0)));
-}
-
-template <void func(Core::System&, u64, s32)>
-void SvcWrap64(Core::System& system) {
- func(system, Param(system, 0), static_cast<s32>(Param(system, 1)));
-}
-
-template <void func(Core::System&, u64, u64)>
-void SvcWrap64(Core::System& system) {
- func(system, Param(system, 0), Param(system, 1));
-}
-
-template <void func(Core::System&, u64, u64, u64)>
-void SvcWrap64(Core::System& system) {
- func(system, Param(system, 0), Param(system, 1), Param(system, 2));
-}
-
-template <void func(Core::System&, u32, u64, u64)>
-void SvcWrap64(Core::System& system) {
- func(system, static_cast<u32>(Param(system, 0)), Param(system, 1), Param(system, 2));
-}
-
-// Used by QueryMemory32, ArbitrateLock32
-template <Result func(Core::System&, u32, u32, u32)>
-void SvcWrap32(Core::System& system) {
- FuncReturn32(system,
- func(system, Param32(system, 0), Param32(system, 1), Param32(system, 2)).raw);
-}
-
-// Used by Break32
-template <void func(Core::System&, u32, u32, u32)>
-void SvcWrap32(Core::System& system) {
- func(system, Param32(system, 0), Param32(system, 1), Param32(system, 2));
-}
-
-// Used by ExitProcess32, ExitThread32
-template <void func(Core::System&)>
-void SvcWrap32(Core::System& system) {
- func(system);
-}
-
-// Used by GetCurrentProcessorNumber32
-template <u32 func(Core::System&)>
-void SvcWrap32(Core::System& system) {
- FuncReturn32(system, func(system));
-}
-
-// Used by SleepThread32
-template <void func(Core::System&, u32, u32)>
-void SvcWrap32(Core::System& system) {
- func(system, Param32(system, 0), Param32(system, 1));
-}
-
-// Used by CreateThread32
-template <Result func(Core::System&, Handle*, u32, u32, u32, u32, s32)>
-void SvcWrap32(Core::System& system) {
- Handle param_1 = 0;
-
- const u32 retval = func(system, &param_1, Param32(system, 0), Param32(system, 1),
- Param32(system, 2), Param32(system, 3), Param32(system, 4))
- .raw;
-
- system.CurrentArmInterface().SetReg(1, param_1);
- FuncReturn(system, retval);
-}
-
-// Used by GetInfo32
-template <Result func(Core::System&, u32*, u32*, u32, u32, u32, u32)>
-void SvcWrap32(Core::System& system) {
- u32 param_1 = 0;
- u32 param_2 = 0;
-
- const u32 retval = func(system, &param_1, &param_2, Param32(system, 0), Param32(system, 1),
- Param32(system, 2), Param32(system, 3))
- .raw;
-
- system.CurrentArmInterface().SetReg(1, param_1);
- system.CurrentArmInterface().SetReg(2, param_2);
- FuncReturn(system, retval);
-}
-
-// Used by GetThreadPriority32, ConnectToNamedPort32
-template <Result func(Core::System&, u32*, u32)>
-void SvcWrap32(Core::System& system) {
- u32 param_1 = 0;
- const u32 retval = func(system, &param_1, Param32(system, 1)).raw;
- system.CurrentArmInterface().SetReg(1, param_1);
- FuncReturn(system, retval);
-}
-
-// Used by GetThreadId32
-template <Result func(Core::System&, u32*, u32*, u32)>
-void SvcWrap32(Core::System& system) {
- u32 param_1 = 0;
- u32 param_2 = 0;
-
- const u32 retval = func(system, &param_1, &param_2, Param32(system, 1)).raw;
- system.CurrentArmInterface().SetReg(1, param_1);
- system.CurrentArmInterface().SetReg(2, param_2);
- FuncReturn(system, retval);
-}
-
-// Used by GetSystemTick32
-template <void func(Core::System&, u32*, u32*)>
-void SvcWrap32(Core::System& system) {
- u32 param_1 = 0;
- u32 param_2 = 0;
-
- func(system, &param_1, &param_2);
- system.CurrentArmInterface().SetReg(0, param_1);
- system.CurrentArmInterface().SetReg(1, param_2);
-}
-
-// Used by CreateEvent32
-template <Result func(Core::System&, Handle*, Handle*)>
-void SvcWrap32(Core::System& system) {
- Handle param_1 = 0;
- Handle param_2 = 0;
-
- const u32 retval = func(system, &param_1, &param_2).raw;
- system.CurrentArmInterface().SetReg(1, param_1);
- system.CurrentArmInterface().SetReg(2, param_2);
- FuncReturn(system, retval);
-}
-
-// Used by GetThreadId32
-template <Result func(Core::System&, Handle, u32*, u32*, u32*)>
-void SvcWrap32(Core::System& system) {
- u32 param_1 = 0;
- u32 param_2 = 0;
- u32 param_3 = 0;
-
- const u32 retval = func(system, Param32(system, 2), &param_1, &param_2, &param_3).raw;
- system.CurrentArmInterface().SetReg(1, param_1);
- system.CurrentArmInterface().SetReg(2, param_2);
- system.CurrentArmInterface().SetReg(3, param_3);
- FuncReturn(system, retval);
-}
-
-// Used by GetThreadCoreMask32
-template <Result func(Core::System&, Handle, s32*, u32*, u32*)>
-void SvcWrap32(Core::System& system) {
- s32 param_1 = 0;
- u32 param_2 = 0;
- u32 param_3 = 0;
-
- const u32 retval = func(system, Param32(system, 2), &param_1, &param_2, &param_3).raw;
- system.CurrentArmInterface().SetReg(1, param_1);
- system.CurrentArmInterface().SetReg(2, param_2);
- system.CurrentArmInterface().SetReg(3, param_3);
- FuncReturn(system, retval);
-}
-
-// Used by SignalProcessWideKey32
-template <void func(Core::System&, u32, s32)>
-void SvcWrap32(Core::System& system) {
- func(system, static_cast<u32>(Param(system, 0)), static_cast<s32>(Param(system, 1)));
-}
-
-// Used by SetThreadActivity32
-template <Result func(Core::System&, Handle, Svc::ThreadActivity)>
-void SvcWrap32(Core::System& system) {
- const u32 retval = func(system, static_cast<Handle>(Param(system, 0)),
- static_cast<Svc::ThreadActivity>(Param(system, 1)))
- .raw;
- FuncReturn(system, retval);
-}
-
-// Used by SetThreadPriority32
-template <Result func(Core::System&, Handle, u32)>
-void SvcWrap32(Core::System& system) {
- const u32 retval =
- func(system, static_cast<Handle>(Param(system, 0)), static_cast<u32>(Param(system, 1))).raw;
- FuncReturn(system, retval);
-}
-
-// Used by SetMemoryAttribute32
-template <Result func(Core::System&, Handle, u32, u32, u32)>
-void SvcWrap32(Core::System& system) {
- const u32 retval =
- func(system, static_cast<Handle>(Param(system, 0)), static_cast<u32>(Param(system, 1)),
- static_cast<u32>(Param(system, 2)), static_cast<u32>(Param(system, 3)))
- .raw;
- FuncReturn(system, retval);
-}
-
-// Used by MapSharedMemory32
-template <Result func(Core::System&, Handle, u32, u32, Svc::MemoryPermission)>
-void SvcWrap32(Core::System& system) {
- const u32 retval = func(system, static_cast<Handle>(Param(system, 0)),
- static_cast<u32>(Param(system, 1)), static_cast<u32>(Param(system, 2)),
- static_cast<Svc::MemoryPermission>(Param(system, 3)))
- .raw;
- FuncReturn(system, retval);
-}
-
-// Used by SetThreadCoreMask32
-template <Result func(Core::System&, Handle, s32, u32, u32)>
-void SvcWrap32(Core::System& system) {
- const u32 retval =
- func(system, static_cast<Handle>(Param(system, 0)), static_cast<s32>(Param(system, 1)),
- static_cast<u32>(Param(system, 2)), static_cast<u32>(Param(system, 3)))
- .raw;
- FuncReturn(system, retval);
-}
-
-// Used by WaitProcessWideKeyAtomic32
-template <Result func(Core::System&, u32, u32, Handle, u32, u32)>
-void SvcWrap32(Core::System& system) {
- const u32 retval =
- func(system, static_cast<u32>(Param(system, 0)), static_cast<u32>(Param(system, 1)),
- static_cast<Handle>(Param(system, 2)), static_cast<u32>(Param(system, 3)),
- static_cast<u32>(Param(system, 4)))
- .raw;
- FuncReturn(system, retval);
-}
-
-// Used by WaitForAddress32
-template <Result func(Core::System&, u32, Svc::ArbitrationType, s32, u32, u32)>
-void SvcWrap32(Core::System& system) {
- const u32 retval = func(system, static_cast<u32>(Param(system, 0)),
- static_cast<Svc::ArbitrationType>(Param(system, 1)),
- static_cast<s32>(Param(system, 2)), static_cast<u32>(Param(system, 3)),
- static_cast<u32>(Param(system, 4)))
- .raw;
- FuncReturn(system, retval);
-}
-
-// Used by SignalToAddress32
-template <Result func(Core::System&, u32, Svc::SignalType, s32, s32)>
-void SvcWrap32(Core::System& system) {
- const u32 retval = func(system, static_cast<u32>(Param(system, 0)),
- static_cast<Svc::SignalType>(Param(system, 1)),
- static_cast<s32>(Param(system, 2)), static_cast<s32>(Param(system, 3)))
- .raw;
- FuncReturn(system, retval);
-}
-
-// Used by SendSyncRequest32, ArbitrateUnlock32
-template <Result func(Core::System&, u32)>
-void SvcWrap32(Core::System& system) {
- FuncReturn(system, func(system, static_cast<u32>(Param(system, 0))).raw);
-}
-
-// Used by CreateTransferMemory32
-template <Result func(Core::System&, Handle*, u32, u32, Svc::MemoryPermission)>
-void SvcWrap32(Core::System& system) {
- Handle handle = 0;
- const u32 retval = func(system, &handle, Param32(system, 1), Param32(system, 2),
- static_cast<Svc::MemoryPermission>(Param32(system, 3)))
- .raw;
- system.CurrentArmInterface().SetReg(1, handle);
- FuncReturn(system, retval);
-}
-
-// Used by WaitSynchronization32
-template <Result func(Core::System&, u32, u32, s32, u32, s32*)>
-void SvcWrap32(Core::System& system) {
- s32 param_1 = 0;
- const u32 retval = func(system, Param32(system, 0), Param32(system, 1), Param32(system, 2),
- Param32(system, 3), &param_1)
- .raw;
- system.CurrentArmInterface().SetReg(1, param_1);
- FuncReturn(system, retval);
-}
-
-// Used by CreateCodeMemory32
-template <Result func(Core::System&, Handle*, u32, u32)>
-void SvcWrap32(Core::System& system) {
- Handle handle = 0;
-
- const u32 retval = func(system, &handle, Param32(system, 1), Param32(system, 2)).raw;
-
- system.CurrentArmInterface().SetReg(1, handle);
- FuncReturn(system, retval);
-}
-
-// Used by ControlCodeMemory32
-template <Result func(Core::System&, Handle, u32, u64, u64, Svc::MemoryPermission)>
-void SvcWrap32(Core::System& system) {
- const u32 retval =
- func(system, Param32(system, 0), Param32(system, 1), Param(system, 2), Param(system, 4),
- static_cast<Svc::MemoryPermission>(Param32(system, 6)))
- .raw;
-
- FuncReturn(system, retval);
-}
-
-// Used by Invalidate/Store/FlushProcessDataCache32
-template <Result func(Core::System&, Handle, u64, u64)>
-void SvcWrap32(Core::System& system) {
- const u64 address = (Param(system, 3) << 32) | Param(system, 2);
- const u64 size = (Param(system, 4) << 32) | Param(system, 1);
- FuncReturn32(system, func(system, Param32(system, 0), address, size).raw);
-}
-
-} // namespace Kernel
diff --git a/src/core/hle/service/acc/acc.cpp b/src/core/hle/service/acc/acc.cpp
index 6d1084fd1..1495d64de 100644
--- a/src/core/hle/service/acc/acc.cpp
+++ b/src/core/hle/service/acc/acc.cpp
@@ -762,7 +762,7 @@ Result Module::Interface::InitializeApplicationInfoBase() {
// processes emulated. As we don't actually have pid support we should assume we're just using
// our own process
const auto launch_property =
- system.GetARPManager().GetLaunchProperty(system.GetCurrentProcessProgramID());
+ system.GetARPManager().GetLaunchProperty(system.GetApplicationProcessProgramID());
if (launch_property.Failed()) {
LOG_ERROR(Service_ACC, "Failed to get launch property");
@@ -806,7 +806,7 @@ void Module::Interface::IsUserAccountSwitchLocked(Kernel::HLERequestContext& ctx
bool is_locked = false;
if (res != Loader::ResultStatus::Success) {
- const FileSys::PatchManager pm{system.GetCurrentProcessProgramID(),
+ const FileSys::PatchManager pm{system.GetApplicationProcessProgramID(),
system.GetFileSystemController(),
system.GetContentProvider()};
const auto nacp_unique = pm.GetControlMetadata().first;
diff --git a/src/core/hle/service/am/am.cpp b/src/core/hle/service/am/am.cpp
index 22999c942..beb2da06e 100644
--- a/src/core/hle/service/am/am.cpp
+++ b/src/core/hle/service/am/am.cpp
@@ -24,7 +24,6 @@
#include "core/hle/service/am/idle.h"
#include "core/hle/service/am/omm.h"
#include "core/hle/service/am/spsm.h"
-#include "core/hle/service/am/tcap.h"
#include "core/hle/service/apm/apm_controller.h"
#include "core/hle/service/apm/apm_interface.h"
#include "core/hle/service/bcat/backend/backend.h"
@@ -79,7 +78,7 @@ IWindowController::IWindowController(Core::System& system_)
IWindowController::~IWindowController() = default;
void IWindowController::GetAppletResourceUserId(Kernel::HLERequestContext& ctx) {
- const u64 process_id = system.CurrentProcess()->GetProcessID();
+ const u64 process_id = system.ApplicationProcess()->GetProcessID();
LOG_DEBUG(Service_AM, "called. Process ID=0x{:016X}", process_id);
@@ -1124,7 +1123,7 @@ void IStorageAccessor::Write(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const u64 offset{rp.Pop<u64>()};
- const std::vector<u8> data{ctx.ReadBuffer()};
+ const auto data{ctx.ReadBuffer()};
const std::size_t size{std::min<u64>(data.size(), backing.GetSize() - offset)};
LOG_DEBUG(Service_AM, "called, offset={}, size={}", offset, size);
@@ -1252,7 +1251,7 @@ void ILibraryAppletCreator::CreateTransferMemoryStorage(Kernel::HLERequestContex
}
auto transfer_mem =
- system.CurrentProcess()->GetHandleTable().GetObject<Kernel::KTransferMemory>(handle);
+ system.ApplicationProcess()->GetHandleTable().GetObject<Kernel::KTransferMemory>(handle);
if (transfer_mem.IsNull()) {
LOG_ERROR(Service_AM, "transfer_mem is a nullptr for handle={:08X}", handle);
@@ -1286,7 +1285,7 @@ void ILibraryAppletCreator::CreateHandleStorage(Kernel::HLERequestContext& ctx)
}
auto transfer_mem =
- system.CurrentProcess()->GetHandleTable().GetObject<Kernel::KTransferMemory>(handle);
+ system.ApplicationProcess()->GetHandleTable().GetObject<Kernel::KTransferMemory>(handle);
if (transfer_mem.IsNull()) {
LOG_ERROR(Service_AM, "transfer_mem is a nullptr for handle={:08X}", handle);
@@ -1465,11 +1464,12 @@ void IApplicationFunctions::PopLaunchParameter(Kernel::HLERequestContext& ctx) {
const auto backend = BCAT::CreateBackendFromSettings(system, [this](u64 tid) {
return system.GetFileSystemController().GetBCATDirectory(tid);
});
- const auto build_id_full = system.GetCurrentProcessBuildID();
+ const auto build_id_full = system.GetApplicationProcessBuildID();
u64 build_id{};
std::memcpy(&build_id, build_id_full.data(), sizeof(u64));
- auto data = backend->GetLaunchParameter({system.GetCurrentProcessProgramID(), build_id});
+ auto data =
+ backend->GetLaunchParameter({system.GetApplicationProcessProgramID(), build_id});
if (data.has_value()) {
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(ResultSuccess);
@@ -1521,7 +1521,7 @@ void IApplicationFunctions::EnsureSaveData(Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Service_AM, "called, uid={:016X}{:016X}", user_id[1], user_id[0]);
FileSys::SaveDataAttribute attribute{};
- attribute.title_id = system.GetCurrentProcessProgramID();
+ attribute.title_id = system.GetApplicationProcessProgramID();
attribute.user_id = user_id;
attribute.type = FileSys::SaveDataType::SaveData;
const auto res = system.GetFileSystemController().CreateSaveData(
@@ -1551,7 +1551,7 @@ void IApplicationFunctions::GetDisplayVersion(Kernel::HLERequestContext& ctx) {
std::array<u8, 0x10> version_string{};
const auto res = [this] {
- const auto title_id = system.GetCurrentProcessProgramID();
+ const auto title_id = system.GetApplicationProcessProgramID();
const FileSys::PatchManager pm{title_id, system.GetFileSystemController(),
system.GetContentProvider()};
@@ -1570,7 +1570,7 @@ void IApplicationFunctions::GetDisplayVersion(Kernel::HLERequestContext& ctx) {
const auto& version = res.first->GetVersionString();
std::copy(version.begin(), version.end(), version_string.begin());
} else {
- constexpr char default_version[]{"1.0.0"};
+ static constexpr char default_version[]{"1.0.0"};
std::memcpy(version_string.data(), default_version, sizeof(default_version));
}
@@ -1588,7 +1588,7 @@ void IApplicationFunctions::GetDesiredLanguage(Kernel::HLERequestContext& ctx) {
u32 supported_languages = 0;
const auto res = [this] {
- const auto title_id = system.GetCurrentProcessProgramID();
+ const auto title_id = system.GetApplicationProcessProgramID();
const FileSys::PatchManager pm{title_id, system.GetFileSystemController(),
system.GetContentProvider()};
@@ -1696,7 +1696,8 @@ void IApplicationFunctions::ExtendSaveData(Kernel::HLERequestContext& ctx) {
static_cast<u8>(type), user_id[1], user_id[0], new_normal_size, new_journal_size);
system.GetFileSystemController().WriteSaveDataSize(
- type, system.GetCurrentProcessProgramID(), user_id, {new_normal_size, new_journal_size});
+ type, system.GetApplicationProcessProgramID(), user_id,
+ {new_normal_size, new_journal_size});
IPC::ResponseBuilder rb{ctx, 4};
rb.Push(ResultSuccess);
@@ -1720,7 +1721,7 @@ void IApplicationFunctions::GetSaveDataSize(Kernel::HLERequestContext& ctx) {
user_id[0]);
const auto size = system.GetFileSystemController().ReadSaveDataSize(
- type, system.GetCurrentProcessProgramID(), user_id);
+ type, system.GetApplicationProcessProgramID(), user_id);
IPC::ResponseBuilder rb{ctx, 6};
rb.Push(ResultSuccess);
@@ -1838,7 +1839,6 @@ void InstallInterfaces(SM::ServiceManager& service_manager, NVFlinger::NVFlinger
std::make_shared<IdleSys>(system)->InstallAsService(service_manager);
std::make_shared<OMM>(system)->InstallAsService(service_manager);
std::make_shared<SPSM>(system)->InstallAsService(service_manager);
- std::make_shared<TCAP>(system)->InstallAsService(service_manager);
}
IHomeMenuFunctions::IHomeMenuFunctions(Core::System& system_)
diff --git a/src/core/hle/service/am/applets/applet_error.cpp b/src/core/hle/service/am/applets/applet_error.cpp
index bae0d99a6..b013896b4 100644
--- a/src/core/hle/service/am/applets/applet_error.cpp
+++ b/src/core/hle/service/am/applets/applet_error.cpp
@@ -166,7 +166,7 @@ void Error::Execute() {
}
const auto callback = [this] { DisplayCompleted(); };
- const auto title_id = system.GetCurrentProcessProgramID();
+ const auto title_id = system.GetApplicationProcessProgramID();
const auto& reporter{system.GetReporter()};
switch (mode) {
diff --git a/src/core/hle/service/am/applets/applet_general_backend.cpp b/src/core/hle/service/am/applets/applet_general_backend.cpp
index e50acdaf6..1eefa85e3 100644
--- a/src/core/hle/service/am/applets/applet_general_backend.cpp
+++ b/src/core/hle/service/am/applets/applet_general_backend.cpp
@@ -186,7 +186,7 @@ void PhotoViewer::Execute() {
const auto callback = [this] { ViewFinished(); };
switch (mode) {
case PhotoViewerAppletMode::CurrentApp:
- frontend.ShowPhotosForApplication(system.GetCurrentProcessProgramID(), callback);
+ frontend.ShowPhotosForApplication(system.GetApplicationProcessProgramID(), callback);
break;
case PhotoViewerAppletMode::AllApps:
frontend.ShowAllPhotos(callback);
diff --git a/src/core/hle/service/am/applets/applet_web_browser.cpp b/src/core/hle/service/am/applets/applet_web_browser.cpp
index 14aa6f69e..f061bae80 100644
--- a/src/core/hle/service/am/applets/applet_web_browser.cpp
+++ b/src/core/hle/service/am/applets/applet_web_browser.cpp
@@ -393,7 +393,7 @@ void WebBrowser::InitializeOffline() {
switch (document_kind) {
case DocumentKind::OfflineHtmlPage:
default:
- title_id = system.GetCurrentProcessProgramID();
+ title_id = system.GetApplicationProcessProgramID();
nca_type = FileSys::ContentRecordType::HtmlDocument;
additional_paths = "html-document";
break;
diff --git a/src/core/hle/service/am/tcap.cpp b/src/core/hle/service/am/tcap.cpp
deleted file mode 100644
index 818420e22..000000000
--- a/src/core/hle/service/am/tcap.cpp
+++ /dev/null
@@ -1,22 +0,0 @@
-// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
-// SPDX-License-Identifier: GPL-2.0-or-later
-
-#include "core/hle/service/am/tcap.h"
-
-namespace Service::AM {
-
-TCAP::TCAP(Core::System& system_) : ServiceFramework{system_, "tcap"} {
- // clang-format off
- static const FunctionInfo functions[] = {
- {0, nullptr, "GetContinuousHighSkinTemperatureEvent"},
- {1, nullptr, "SetOperationMode"},
- {2, nullptr, "LoadAndApplySettings"},
- };
- // clang-format on
-
- RegisterHandlers(functions);
-}
-
-TCAP::~TCAP() = default;
-
-} // namespace Service::AM
diff --git a/src/core/hle/service/am/tcap.h b/src/core/hle/service/am/tcap.h
deleted file mode 100644
index 6b2148c29..000000000
--- a/src/core/hle/service/am/tcap.h
+++ /dev/null
@@ -1,20 +0,0 @@
-// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
-// SPDX-License-Identifier: GPL-2.0-or-later
-
-#pragma once
-
-#include "core/hle/service/service.h"
-
-namespace Core {
-class System;
-}
-
-namespace Service::AM {
-
-class TCAP final : public ServiceFramework<TCAP> {
-public:
- explicit TCAP(Core::System& system_);
- ~TCAP() override;
-};
-
-} // namespace Service::AM
diff --git a/src/core/hle/service/aoc/aoc_u.cpp b/src/core/hle/service/aoc/aoc_u.cpp
index 368ccd52f..7264f23f9 100644
--- a/src/core/hle/service/aoc/aoc_u.cpp
+++ b/src/core/hle/service/aoc/aoc_u.cpp
@@ -155,7 +155,7 @@ void AOC_U::CountAddOnContent(Kernel::HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(ResultSuccess);
- const auto current = system.GetCurrentProcessProgramID();
+ const auto current = system.GetApplicationProcessProgramID();
const auto& disabled = Settings::values.disabled_addons[current];
if (std::find(disabled.begin(), disabled.end(), "DLC") != disabled.end()) {
@@ -182,7 +182,7 @@ void AOC_U::ListAddOnContent(Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Service_AOC, "called with offset={}, count={}, process_id={}", offset, count,
process_id);
- const auto current = system.GetCurrentProcessProgramID();
+ const auto current = system.GetApplicationProcessProgramID();
std::vector<u32> out;
const auto& disabled = Settings::values.disabled_addons[current];
@@ -228,7 +228,7 @@ void AOC_U::GetAddOnContentBaseId(Kernel::HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 4};
rb.Push(ResultSuccess);
- const auto title_id = system.GetCurrentProcessProgramID();
+ const auto title_id = system.GetApplicationProcessProgramID();
const FileSys::PatchManager pm{title_id, system.GetFileSystemController(),
system.GetContentProvider()};
diff --git a/src/core/hle/service/apm/apm.cpp b/src/core/hle/service/apm/apm.cpp
index 8a338d9b1..44b2927a6 100644
--- a/src/core/hle/service/apm/apm.cpp
+++ b/src/core/hle/service/apm/apm.cpp
@@ -14,8 +14,6 @@ void InstallInterfaces(Core::System& system) {
auto module_ = std::make_shared<Module>();
std::make_shared<APM>(system, module_, system.GetAPMController(), "apm")
->InstallAsService(system.ServiceManager());
- std::make_shared<APM>(system, module_, system.GetAPMController(), "apm:p")
- ->InstallAsService(system.ServiceManager());
std::make_shared<APM>(system, module_, system.GetAPMController(), "apm:am")
->InstallAsService(system.ServiceManager());
std::make_shared<APM_Sys>(system, system.GetAPMController())
diff --git a/src/core/hle/service/apm/apm_controller.cpp b/src/core/hle/service/apm/apm_controller.cpp
index d6de84066..227fdd0cf 100644
--- a/src/core/hle/service/apm/apm_controller.cpp
+++ b/src/core/hle/service/apm/apm_controller.cpp
@@ -56,7 +56,7 @@ void Controller::SetPerformanceConfiguration(PerformanceMode mode,
}
void Controller::SetFromCpuBoostMode(CpuBoostMode mode) {
- constexpr std::array<PerformanceConfiguration, 3> BOOST_MODE_TO_CONFIG_MAP{{
+ static constexpr std::array<PerformanceConfiguration, 3> BOOST_MODE_TO_CONFIG_MAP{{
PerformanceConfiguration::Config7,
PerformanceConfiguration::Config13,
PerformanceConfiguration::Config15,
diff --git a/src/core/hle/service/audio/auddbg.cpp b/src/core/hle/service/audio/auddbg.cpp
deleted file mode 100644
index 5541af300..000000000
--- a/src/core/hle/service/audio/auddbg.cpp
+++ /dev/null
@@ -1,21 +0,0 @@
-// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
-// SPDX-License-Identifier: GPL-2.0-or-later
-
-#include "core/hle/service/audio/auddbg.h"
-
-namespace Service::Audio {
-
-AudDbg::AudDbg(Core::System& system_, const char* name) : ServiceFramework{system_, name} {
- // clang-format off
- static const FunctionInfo functions[] = {
- {0, nullptr, "RequestSuspendForDebug"},
- {1, nullptr, "RequestResumeForDebug"},
- };
- // clang-format on
-
- RegisterHandlers(functions);
-}
-
-AudDbg::~AudDbg() = default;
-
-} // namespace Service::Audio
diff --git a/src/core/hle/service/audio/auddbg.h b/src/core/hle/service/audio/auddbg.h
deleted file mode 100644
index 8f26be5dc..000000000
--- a/src/core/hle/service/audio/auddbg.h
+++ /dev/null
@@ -1,20 +0,0 @@
-// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
-// SPDX-License-Identifier: GPL-2.0-or-later
-
-#pragma once
-
-#include "core/hle/service/service.h"
-
-namespace Core {
-class System;
-}
-
-namespace Service::Audio {
-
-class AudDbg final : public ServiceFramework<AudDbg> {
-public:
- explicit AudDbg(Core::System& system_, const char* name);
- ~AudDbg() override;
-};
-
-} // namespace Service::Audio
diff --git a/src/core/hle/service/audio/audin_a.cpp b/src/core/hle/service/audio/audin_a.cpp
deleted file mode 100644
index 98f4a6048..000000000
--- a/src/core/hle/service/audio/audin_a.cpp
+++ /dev/null
@@ -1,23 +0,0 @@
-// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
-// SPDX-License-Identifier: GPL-2.0-or-later
-
-#include "core/hle/service/audio/audin_a.h"
-
-namespace Service::Audio {
-
-AudInA::AudInA(Core::System& system_) : ServiceFramework{system_, "audin:a"} {
- // clang-format off
- static const FunctionInfo functions[] = {
- {0, nullptr, "RequestSuspend"},
- {1, nullptr, "RequestResume"},
- {2, nullptr, "GetProcessMasterVolume"},
- {3, nullptr, "SetProcessMasterVolume"},
- };
- // clang-format on
-
- RegisterHandlers(functions);
-}
-
-AudInA::~AudInA() = default;
-
-} // namespace Service::Audio
diff --git a/src/core/hle/service/audio/audin_a.h b/src/core/hle/service/audio/audin_a.h
deleted file mode 100644
index 19a927de5..000000000
--- a/src/core/hle/service/audio/audin_a.h
+++ /dev/null
@@ -1,20 +0,0 @@
-// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
-// SPDX-License-Identifier: GPL-2.0-or-later
-
-#pragma once
-
-#include "core/hle/service/service.h"
-
-namespace Core {
-class System;
-}
-
-namespace Service::Audio {
-
-class AudInA final : public ServiceFramework<AudInA> {
-public:
- explicit AudInA(Core::System& system_);
- ~AudInA() override;
-};
-
-} // namespace Service::Audio
diff --git a/src/core/hle/service/audio/audio.cpp b/src/core/hle/service/audio/audio.cpp
index 97da71dfa..ed36e3448 100644
--- a/src/core/hle/service/audio/audio.cpp
+++ b/src/core/hle/service/audio/audio.cpp
@@ -2,17 +2,12 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#include "core/hle/service/audio/audctl.h"
-#include "core/hle/service/audio/auddbg.h"
-#include "core/hle/service/audio/audin_a.h"
#include "core/hle/service/audio/audin_u.h"
#include "core/hle/service/audio/audio.h"
-#include "core/hle/service/audio/audout_a.h"
#include "core/hle/service/audio/audout_u.h"
#include "core/hle/service/audio/audrec_a.h"
#include "core/hle/service/audio/audrec_u.h"
-#include "core/hle/service/audio/audren_a.h"
#include "core/hle/service/audio/audren_u.h"
-#include "core/hle/service/audio/codecctl.h"
#include "core/hle/service/audio/hwopus.h"
#include "core/hle/service/service.h"
@@ -20,21 +15,12 @@ namespace Service::Audio {
void InstallInterfaces(SM::ServiceManager& service_manager, Core::System& system) {
std::make_shared<AudCtl>(system)->InstallAsService(service_manager);
- std::make_shared<AudOutA>(system)->InstallAsService(service_manager);
std::make_shared<AudOutU>(system)->InstallAsService(service_manager);
- std::make_shared<AudInA>(system)->InstallAsService(service_manager);
std::make_shared<AudInU>(system)->InstallAsService(service_manager);
std::make_shared<AudRecA>(system)->InstallAsService(service_manager);
std::make_shared<AudRecU>(system)->InstallAsService(service_manager);
- std::make_shared<AudRenA>(system)->InstallAsService(service_manager);
std::make_shared<AudRenU>(system)->InstallAsService(service_manager);
- std::make_shared<CodecCtl>(system)->InstallAsService(service_manager);
std::make_shared<HwOpus>(system)->InstallAsService(service_manager);
-
- std::make_shared<AudDbg>(system, "audin:d")->InstallAsService(service_manager);
- std::make_shared<AudDbg>(system, "audout:d")->InstallAsService(service_manager);
- std::make_shared<AudDbg>(system, "audrec:d")->InstallAsService(service_manager);
- std::make_shared<AudDbg>(system, "audren:d")->InstallAsService(service_manager);
}
} // namespace Service::Audio
diff --git a/src/core/hle/service/audio/audout_a.cpp b/src/core/hle/service/audio/audout_a.cpp
deleted file mode 100644
index 5ecb99236..000000000
--- a/src/core/hle/service/audio/audout_a.cpp
+++ /dev/null
@@ -1,25 +0,0 @@
-// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
-// SPDX-License-Identifier: GPL-2.0-or-later
-
-#include "core/hle/service/audio/audout_a.h"
-
-namespace Service::Audio {
-
-AudOutA::AudOutA(Core::System& system_) : ServiceFramework{system_, "audout:a"} {
- // clang-format off
- static const FunctionInfo functions[] = {
- {0, nullptr, "RequestSuspend"},
- {1, nullptr, "RequestResume"},
- {2, nullptr, "GetProcessMasterVolume"},
- {3, nullptr, "SetProcessMasterVolume"},
- {4, nullptr, "GetProcessRecordVolume"},
- {5, nullptr, "SetProcessRecordVolume"},
- };
- // clang-format on
-
- RegisterHandlers(functions);
-}
-
-AudOutA::~AudOutA() = default;
-
-} // namespace Service::Audio
diff --git a/src/core/hle/service/audio/audout_a.h b/src/core/hle/service/audio/audout_a.h
deleted file mode 100644
index f641cffeb..000000000
--- a/src/core/hle/service/audio/audout_a.h
+++ /dev/null
@@ -1,20 +0,0 @@
-// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
-// SPDX-License-Identifier: GPL-2.0-or-later
-
-#pragma once
-
-#include "core/hle/service/service.h"
-
-namespace Core {
-class System;
-}
-
-namespace Service::Audio {
-
-class AudOutA final : public ServiceFramework<AudOutA> {
-public:
- explicit AudOutA(Core::System& system_);
- ~AudOutA() override;
-};
-
-} // namespace Service::Audio
diff --git a/src/core/hle/service/audio/audren_a.cpp b/src/core/hle/service/audio/audren_a.cpp
deleted file mode 100644
index e775ac3bf..000000000
--- a/src/core/hle/service/audio/audren_a.cpp
+++ /dev/null
@@ -1,27 +0,0 @@
-// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
-// SPDX-License-Identifier: GPL-2.0-or-later
-
-#include "core/hle/service/audio/audren_a.h"
-
-namespace Service::Audio {
-
-AudRenA::AudRenA(Core::System& system_) : ServiceFramework{system_, "audren:a"} {
- // clang-format off
- static const FunctionInfo functions[] = {
- {0, nullptr, "RequestSuspend"},
- {1, nullptr, "RequestResume"},
- {2, nullptr, "GetProcessMasterVolume"},
- {3, nullptr, "SetProcessMasterVolume"},
- {4, nullptr, "RegisterAppletResourceUserId"},
- {5, nullptr, "UnregisterAppletResourceUserId"},
- {6, nullptr, "GetProcessRecordVolume"},
- {7, nullptr, "SetProcessRecordVolume"},
- };
- // clang-format on
-
- RegisterHandlers(functions);
-}
-
-AudRenA::~AudRenA() = default;
-
-} // namespace Service::Audio
diff --git a/src/core/hle/service/audio/audren_a.h b/src/core/hle/service/audio/audren_a.h
deleted file mode 100644
index 9e08b4245..000000000
--- a/src/core/hle/service/audio/audren_a.h
+++ /dev/null
@@ -1,20 +0,0 @@
-// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
-// SPDX-License-Identifier: GPL-2.0-or-later
-
-#pragma once
-
-#include "core/hle/service/service.h"
-
-namespace Core {
-class System;
-}
-
-namespace Service::Audio {
-
-class AudRenA final : public ServiceFramework<AudRenA> {
-public:
- explicit AudRenA(Core::System& system_);
- ~AudRenA() override;
-};
-
-} // namespace Service::Audio
diff --git a/src/core/hle/service/audio/audren_u.cpp b/src/core/hle/service/audio/audren_u.cpp
index 3a1c231b6..7d730421d 100644
--- a/src/core/hle/service/audio/audren_u.cpp
+++ b/src/core/hle/service/audio/audren_u.cpp
@@ -112,7 +112,7 @@ private:
void RequestUpdate(Kernel::HLERequestContext& ctx) {
LOG_TRACE(Service_Audio, "called");
- std::vector<u8> input{ctx.ReadBuffer(0)};
+ const auto input{ctx.ReadBuffer(0)};
// These buffers are written manually to avoid an issue with WriteBuffer throwing errors for
// checking size 0. Performance size is 0 for most games.
@@ -455,7 +455,7 @@ void AudRenU::OpenAudioRenderer(Kernel::HLERequestContext& ctx) {
return;
}
- const auto& handle_table{system.CurrentProcess()->GetHandleTable()};
+ const auto& handle_table{system.ApplicationProcess()->GetHandleTable()};
auto process{handle_table.GetObject<Kernel::KProcess>(process_handle)};
auto transfer_memory{
process->GetHandleTable().GetObject<Kernel::KTransferMemory>(transfer_memory_handle)};
diff --git a/src/core/hle/service/audio/codecctl.cpp b/src/core/hle/service/audio/codecctl.cpp
deleted file mode 100644
index 81b956d7e..000000000
--- a/src/core/hle/service/audio/codecctl.cpp
+++ /dev/null
@@ -1,29 +0,0 @@
-// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
-// SPDX-License-Identifier: GPL-2.0-or-later
-
-#include "core/hle/service/audio/codecctl.h"
-
-namespace Service::Audio {
-
-CodecCtl::CodecCtl(Core::System& system_) : ServiceFramework{system_, "codecctl"} {
- static const FunctionInfo functions[] = {
- {0, nullptr, "Initialize"},
- {1, nullptr, "Finalize"},
- {2, nullptr, "Sleep"},
- {3, nullptr, "Wake"},
- {4, nullptr, "SetVolume"},
- {5, nullptr, "GetVolumeMax"},
- {6, nullptr, "GetVolumeMin"},
- {7, nullptr, "SetActiveTarget"},
- {8, nullptr, "GetActiveTarget"},
- {9, nullptr, "BindHeadphoneMicJackInterrupt"},
- {10, nullptr, "IsHeadphoneMicJackInserted"},
- {11, nullptr, "ClearHeadphoneMicJackInterrupt"},
- {12, nullptr, "IsRequested"},
- };
- RegisterHandlers(functions);
-}
-
-CodecCtl::~CodecCtl() = default;
-
-} // namespace Service::Audio
diff --git a/src/core/hle/service/audio/codecctl.h b/src/core/hle/service/audio/codecctl.h
deleted file mode 100644
index 34da98212..000000000
--- a/src/core/hle/service/audio/codecctl.h
+++ /dev/null
@@ -1,20 +0,0 @@
-// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
-// SPDX-License-Identifier: GPL-2.0-or-later
-
-#pragma once
-
-#include "core/hle/service/service.h"
-
-namespace Core {
-class System;
-}
-
-namespace Service::Audio {
-
-class CodecCtl final : public ServiceFramework<CodecCtl> {
-public:
- explicit CodecCtl(Core::System& system_);
- ~CodecCtl() override;
-};
-
-} // namespace Service::Audio
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:
ctx.WriteBuffer(samples);
}
- bool DecodeOpusData(u32& consumed, u32& sample_count, const std::vector<u8>& input,
+ bool DecodeOpusData(u32& consumed, u32& sample_count, std::span<const u8> input,
std::vector<opus_int16>& output, u64* out_performance_time) const {
const auto start_time = std::chrono::steady_clock::now();
const std::size_t raw_output_sz = output.size() * sizeof(opus_int16);
diff --git a/src/core/hle/service/bcat/bcat_module.cpp b/src/core/hle/service/bcat/bcat_module.cpp
index cbe690a5d..6e6fed227 100644
--- a/src/core/hle/service/bcat/bcat_module.cpp
+++ b/src/core/hle/service/bcat/bcat_module.cpp
@@ -176,8 +176,8 @@ private:
void RequestSyncDeliveryCache(Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Service_BCAT, "called");
- backend.Synchronize({system.GetCurrentProcessProgramID(),
- GetCurrentBuildID(system.GetCurrentProcessBuildID())},
+ backend.Synchronize({system.GetApplicationProcessProgramID(),
+ GetCurrentBuildID(system.GetApplicationProcessBuildID())},
GetProgressBackend(SyncType::Normal));
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
@@ -193,8 +193,8 @@ private:
LOG_DEBUG(Service_BCAT, "called, name={}", name);
- backend.SynchronizeDirectory({system.GetCurrentProcessProgramID(),
- GetCurrentBuildID(system.GetCurrentProcessBuildID())},
+ backend.SynchronizeDirectory({system.GetApplicationProcessProgramID(),
+ GetCurrentBuildID(system.GetApplicationProcessBuildID())},
name, GetProgressBackend(SyncType::Directory));
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
@@ -554,7 +554,7 @@ private:
void Module::Interface::CreateDeliveryCacheStorageService(Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Service_BCAT, "called");
- const auto title_id = system.GetCurrentProcessProgramID();
+ const auto title_id = system.GetApplicationProcessProgramID();
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(ResultSuccess);
rb.PushIpcInterface<IDeliveryCacheStorageService>(system, fsc.GetBCATDirectory(title_id));
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:
void ImportTicket(Kernel::HLERequestContext& ctx) {
const auto ticket = ctx.ReadBuffer();
- const auto cert = ctx.ReadBuffer(1);
+ [[maybe_unused]] const auto cert = ctx.ReadBuffer(1);
if (ticket.size() < sizeof(Core::Crypto::Ticket)) {
LOG_ERROR(Service_ETicket, "The input buffer is not large enough!");
diff --git a/src/core/hle/service/fatal/fatal.cpp b/src/core/hle/service/fatal/fatal.cpp
index 27675615b..2e5919330 100644
--- a/src/core/hle/service/fatal/fatal.cpp
+++ b/src/core/hle/service/fatal/fatal.cpp
@@ -63,7 +63,7 @@ enum class FatalType : u32 {
};
static void GenerateErrorReport(Core::System& system, Result error_code, const FatalInfo& info) {
- const auto title_id = system.GetCurrentProcessProgramID();
+ const auto title_id = system.GetApplicationProcessProgramID();
std::string crash_report = fmt::format(
"Yuzu {}-{} crash report\n"
"Title ID: {:016x}\n"
diff --git a/src/core/hle/service/filesystem/filesystem.cpp b/src/core/hle/service/filesystem/filesystem.cpp
index 11c604a0f..177447bc1 100644
--- a/src/core/hle/service/filesystem/filesystem.cpp
+++ b/src/core/hle/service/filesystem/filesystem.cpp
@@ -317,7 +317,7 @@ ResultVal<FileSys::VirtualFile> FileSystemController::OpenRomFSCurrentProcess()
return ResultUnknown;
}
- return romfs_factory->OpenCurrentProcess(system.GetCurrentProcessProgramID());
+ return romfs_factory->OpenCurrentProcess(system.GetApplicationProcessProgramID());
}
ResultVal<FileSys::VirtualFile> FileSystemController::OpenPatchedRomFS(
@@ -502,7 +502,7 @@ FileSys::SaveDataSize FileSystemController::ReadSaveDataSize(FileSys::SaveDataTy
const auto res = system.GetAppLoader().ReadControlData(nacp);
if (res != Loader::ResultStatus::Success) {
- const FileSys::PatchManager pm{system.GetCurrentProcessProgramID(),
+ const FileSys::PatchManager pm{system.GetApplicationProcessProgramID(),
system.GetFileSystemController(),
system.GetContentProvider()};
const auto metadata = pm.GetControlMetadata();
diff --git a/src/core/hle/service/filesystem/fsp_srv.cpp b/src/core/hle/service/filesystem/fsp_srv.cpp
index fbb16a7da..e76346ca9 100644
--- a/src/core/hle/service/filesystem/fsp_srv.cpp
+++ b/src/core/hle/service/filesystem/fsp_srv.cpp
@@ -190,7 +190,7 @@ private:
return;
}
- const std::vector<u8> data = ctx.ReadBuffer();
+ const auto data = ctx.ReadBuffer();
ASSERT_MSG(
static_cast<s64>(data.size()) <= length,
@@ -401,11 +401,8 @@ public:
}
void RenameFile(Kernel::HLERequestContext& ctx) {
- std::vector<u8> buffer = ctx.ReadBuffer(0);
- const std::string src_name = Common::StringFromBuffer(buffer);
-
- buffer = ctx.ReadBuffer(1);
- const std::string dst_name = Common::StringFromBuffer(buffer);
+ const std::string src_name = Common::StringFromBuffer(ctx.ReadBuffer(0));
+ const std::string dst_name = Common::StringFromBuffer(ctx.ReadBuffer(1));
LOG_DEBUG(Service_FS, "called. file '{}' to file '{}'", src_name, dst_name);
@@ -1039,8 +1036,9 @@ void FSP_SRV::OpenDataStorageWithProgramIndex(Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Service_FS, "called, program_index={}", program_index);
- auto patched_romfs = fsc.OpenPatchedRomFSWithProgramIndex(
- system.GetCurrentProcessProgramID(), program_index, FileSys::ContentRecordType::Program);
+ auto patched_romfs =
+ fsc.OpenPatchedRomFSWithProgramIndex(system.GetApplicationProcessProgramID(), program_index,
+ FileSys::ContentRecordType::Program);
if (patched_romfs.Failed()) {
// TODO: Find the right error code to use here
@@ -1086,7 +1084,7 @@ void FSP_SRV::GetGlobalAccessLogMode(Kernel::HLERequestContext& ctx) {
}
void FSP_SRV::OutputAccessLogToSdCard(Kernel::HLERequestContext& ctx) {
- const auto raw = ctx.ReadBuffer();
+ const auto raw = ctx.ReadBufferCopy();
auto log = Common::StringFromFixedZeroTerminatedBuffer(
reinterpret_cast<const char*>(raw.data()), raw.size());
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:
return;
}
- control = ctx.ReadBuffer();
+ // TODO: Can this be a span?
+ control = ctx.ReadBufferCopy();
IPC::ResponseBuilder rb{ctx, 2};
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..ba6f04d8d 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) {
}
break;
case Core::HID::NpadStyleIndex::JoyconLeft:
+ shared_memory->fullkey_color.attribute = ColorAttribute::Ok;
+ shared_memory->fullkey_color.fullkey = body_colors.left;
shared_memory->joycon_color.attribute = ColorAttribute::Ok;
shared_memory->joycon_color.left = body_colors.left;
shared_memory->battery_level_dual = battery_level.left.battery_level;
@@ -285,6 +287,8 @@ void Controller_NPad::InitNewlyAddedController(Core::HID::NpadIdType npad_id) {
shared_memory->sixaxis_left_properties.is_newly_assigned.Assign(1);
break;
case Core::HID::NpadStyleIndex::JoyconRight:
+ shared_memory->fullkey_color.attribute = ColorAttribute::Ok;
+ shared_memory->fullkey_color.fullkey = body_colors.right;
shared_memory->joycon_color.attribute = ColorAttribute::Ok;
shared_memory->joycon_color.right = body_colors.right;
shared_memory->battery_level_right = battery_level.right.battery_level;
@@ -332,6 +336,20 @@ void Controller_NPad::InitNewlyAddedController(Core::HID::NpadIdType npad_id) {
controller.is_connected = true;
controller.device->Connect();
+ controller.device->SetLedPattern();
+ if (controller_type == Core::HID::NpadStyleIndex::JoyconDual) {
+ if (controller.is_dual_left_connected) {
+ controller.device->SetPollingMode(Core::HID::EmulatedDeviceIndex::LeftIndex,
+ Common::Input::PollingMode::Active);
+ }
+ if (controller.is_dual_right_connected) {
+ controller.device->SetPollingMode(Core::HID::EmulatedDeviceIndex::RightIndex,
+ Common::Input::PollingMode::Active);
+ }
+ } else {
+ controller.device->SetPollingMode(Core::HID::EmulatedDeviceIndex::AllDevices,
+ Common::Input::PollingMode::Active);
+ }
SignalStyleSetChangedEvent(npad_id);
WriteEmptyEntry(controller.shared_memory);
}
@@ -410,6 +428,9 @@ void Controller_NPad::RequestPadStateUpdate(Core::HID::NpadIdType npad_id) {
return;
}
+ // This function is unique to yuzu for the turbo buttons to work properly
+ controller.device->TurboButtonUpdate();
+
auto& pad_entry = controller.npad_pad_state;
auto& trigger_entry = controller.npad_trigger_state;
const auto button_state = controller.device->GetNpadButtons();
@@ -737,11 +758,20 @@ Core::HID::NpadStyleTag Controller_NPad::GetSupportedStyleSet() const {
return hid_core.GetSupportedStyleTag();
}
-void Controller_NPad::SetSupportedNpadIdTypes(u8* data, std::size_t length) {
+Result Controller_NPad::SetSupportedNpadIdTypes(std::span<const u8> data) {
+ constexpr std::size_t max_number_npad_ids = 0xa;
+ const auto length = data.size();
ASSERT(length > 0 && (length % sizeof(u32)) == 0);
+ const std::size_t elements = length / sizeof(u32);
+
+ if (elements > max_number_npad_ids) {
+ return InvalidArraySize;
+ }
+
supported_npad_id_types.clear();
- supported_npad_id_types.resize(length / sizeof(u32));
- std::memcpy(supported_npad_id_types.data(), data, length);
+ supported_npad_id_types.resize(elements);
+ std::memcpy(supported_npad_id_types.data(), data.data(), length);
+ return ResultSuccess;
}
void Controller_NPad::GetSupportedNpadIdTypes(u32* data, std::size_t max_length) {
@@ -1102,7 +1132,8 @@ Result Controller_NPad::DisconnectNpad(Core::HID::NpadIdType npad_id) {
return ResultSuccess;
}
Result Controller_NPad::SetGyroscopeZeroDriftMode(
- const Core::HID::SixAxisSensorHandle& sixaxis_handle, GyroscopeZeroDriftMode drift_mode) {
+ const Core::HID::SixAxisSensorHandle& sixaxis_handle,
+ Core::HID::GyroscopeZeroDriftMode drift_mode) {
const auto is_valid = VerifyValidSixAxisSensorHandle(sixaxis_handle);
if (is_valid.IsError()) {
LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw);
@@ -1110,14 +1141,16 @@ Result Controller_NPad::SetGyroscopeZeroDriftMode(
}
auto& sixaxis = GetSixaxisState(sixaxis_handle);
+ auto& controller = GetControllerFromHandle(sixaxis_handle);
sixaxis.gyroscope_zero_drift_mode = drift_mode;
+ controller.device->SetGyroscopeZeroDriftMode(drift_mode);
return ResultSuccess;
}
Result Controller_NPad::GetGyroscopeZeroDriftMode(
const Core::HID::SixAxisSensorHandle& sixaxis_handle,
- GyroscopeZeroDriftMode& drift_mode) const {
+ Core::HID::GyroscopeZeroDriftMode& drift_mode) const {
const auto is_valid = VerifyValidSixAxisSensorHandle(sixaxis_handle);
if (is_valid.IsError()) {
LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw);
diff --git a/src/core/hle/service/hid/controllers/npad.h b/src/core/hle/service/hid/controllers/npad.h
index 1a589cca2..a5998c453 100644
--- a/src/core/hle/service/hid/controllers/npad.h
+++ b/src/core/hle/service/hid/controllers/npad.h
@@ -6,6 +6,7 @@
#include <array>
#include <atomic>
#include <mutex>
+#include <span>
#include "common/bit_field.h"
#include "common/common_types.h"
@@ -51,13 +52,6 @@ public:
// When the controller is requesting a motion update for the shared memory
void OnMotionUpdate(const Core::Timing::CoreTiming& core_timing) override;
- // This is nn::hid::GyroscopeZeroDriftMode
- enum class GyroscopeZeroDriftMode : u32 {
- Loose = 0,
- Standard = 1,
- Tight = 2,
- };
-
// This is nn::hid::NpadJoyHoldType
enum class NpadJoyHoldType : u64 {
Vertical = 0,
@@ -95,7 +89,7 @@ public:
void SetSupportedStyleSet(Core::HID::NpadStyleTag style_set);
Core::HID::NpadStyleTag GetSupportedStyleSet() const;
- void SetSupportedNpadIdTypes(u8* data, std::size_t length);
+ Result SetSupportedNpadIdTypes(std::span<const u8> data);
void GetSupportedNpadIdTypes(u32* data, std::size_t max_length);
std::size_t GetSupportedNpadIdTypesSize() const;
@@ -145,9 +139,9 @@ public:
Result DisconnectNpad(Core::HID::NpadIdType npad_id);
Result SetGyroscopeZeroDriftMode(const Core::HID::SixAxisSensorHandle& sixaxis_handle,
- GyroscopeZeroDriftMode drift_mode);
+ Core::HID::GyroscopeZeroDriftMode drift_mode);
Result GetGyroscopeZeroDriftMode(const Core::HID::SixAxisSensorHandle& sixaxis_handle,
- GyroscopeZeroDriftMode& drift_mode) const;
+ Core::HID::GyroscopeZeroDriftMode& drift_mode) const;
Result IsSixAxisSensorAtRest(const Core::HID::SixAxisSensorHandle& sixaxis_handle,
bool& is_at_rest) const;
Result IsFirmwareUpdateAvailableForSixAxisSensor(
@@ -488,7 +482,8 @@ private:
Core::HID::SixAxisSensorFusionParameters fusion{};
Core::HID::SixAxisSensorCalibrationParameter calibration{};
Core::HID::SixAxisSensorIcInformation ic_information{};
- GyroscopeZeroDriftMode gyroscope_zero_drift_mode{GyroscopeZeroDriftMode::Standard};
+ Core::HID::GyroscopeZeroDriftMode gyroscope_zero_drift_mode{
+ Core::HID::GyroscopeZeroDriftMode::Standard};
};
struct NpadControllerData {
diff --git a/src/core/hle/service/hid/errors.h b/src/core/hle/service/hid/errors.h
index 76208e9a4..9585bdaf0 100644
--- a/src/core/hle/service/hid/errors.h
+++ b/src/core/hle/service/hid/errors.h
@@ -18,6 +18,7 @@ constexpr Result NpadIsDualJoycon{ErrorModule::HID, 601};
constexpr Result NpadIsSameType{ErrorModule::HID, 602};
constexpr Result InvalidNpadId{ErrorModule::HID, 709};
constexpr Result NpadNotConnected{ErrorModule::HID, 710};
+constexpr Result InvalidArraySize{ErrorModule::HID, 715};
constexpr Result InvalidPalmaHandle{ErrorModule::HID, 3302};
} // namespace Service::HID
diff --git a/src/core/hle/service/hid/hid.cpp b/src/core/hle/service/hid/hid.cpp
index bf28440c6..eb3c45a58 100644
--- a/src/core/hle/service/hid/hid.cpp
+++ b/src/core/hle/service/hid/hid.cpp
@@ -712,7 +712,7 @@ void Hid::ResetSixAxisSensorFusionParameters(Kernel::HLERequestContext& ctx) {
void Hid::SetGyroscopeZeroDriftMode(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto sixaxis_handle{rp.PopRaw<Core::HID::SixAxisSensorHandle>()};
- const auto drift_mode{rp.PopEnum<Controller_NPad::GyroscopeZeroDriftMode>()};
+ const auto drift_mode{rp.PopEnum<Core::HID::GyroscopeZeroDriftMode>()};
const auto applet_resource_user_id{rp.Pop<u64>()};
auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad);
@@ -739,7 +739,7 @@ void Hid::GetGyroscopeZeroDriftMode(Kernel::HLERequestContext& ctx) {
const auto parameters{rp.PopRaw<Parameters>()};
- auto drift_mode{Controller_NPad::GyroscopeZeroDriftMode::Standard};
+ auto drift_mode{Core::HID::GyroscopeZeroDriftMode::Standard};
auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad);
const auto result = controller.GetGyroscopeZeroDriftMode(parameters.sixaxis_handle, drift_mode);
@@ -764,7 +764,7 @@ void Hid::ResetGyroscopeZeroDriftMode(Kernel::HLERequestContext& ctx) {
const auto parameters{rp.PopRaw<Parameters>()};
- const auto drift_mode{Controller_NPad::GyroscopeZeroDriftMode::Standard};
+ const auto drift_mode{Core::HID::GyroscopeZeroDriftMode::Standard};
auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad);
const auto result = controller.SetGyroscopeZeroDriftMode(parameters.sixaxis_handle, drift_mode);
@@ -1025,13 +1025,13 @@ void Hid::SetSupportedNpadIdType(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto applet_resource_user_id{rp.Pop<u64>()};
- applet_resource->GetController<Controller_NPad>(HidController::NPad)
- .SetSupportedNpadIdTypes(ctx.ReadBuffer().data(), ctx.GetReadBufferSize());
+ const auto result = applet_resource->GetController<Controller_NPad>(HidController::NPad)
+ .SetSupportedNpadIdTypes(ctx.ReadBuffer());
LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id);
IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(ResultSuccess);
+ rb.Push(result);
}
void Hid::ActivateNpad(Kernel::HLERequestContext& ctx) {
@@ -1830,7 +1830,7 @@ void Hid::InitializeSevenSixAxisSensor(Kernel::HLERequestContext& ctx) {
ASSERT_MSG(t_mem_1_size == 0x1000, "t_mem_1_size is not 0x1000 bytes");
ASSERT_MSG(t_mem_2_size == 0x7F000, "t_mem_2_size is not 0x7F000 bytes");
- auto t_mem_1 = system.CurrentProcess()->GetHandleTable().GetObject<Kernel::KTransferMemory>(
+ auto t_mem_1 = system.ApplicationProcess()->GetHandleTable().GetObject<Kernel::KTransferMemory>(
t_mem_1_handle);
if (t_mem_1.IsNull()) {
@@ -1840,7 +1840,7 @@ void Hid::InitializeSevenSixAxisSensor(Kernel::HLERequestContext& ctx) {
return;
}
- auto t_mem_2 = system.CurrentProcess()->GetHandleTable().GetObject<Kernel::KTransferMemory>(
+ auto t_mem_2 = system.ApplicationProcess()->GetHandleTable().GetObject<Kernel::KTransferMemory>(
t_mem_2_handle);
if (t_mem_2.IsNull()) {
@@ -2104,7 +2104,7 @@ void Hid::WritePalmaRgbLedPatternEntry(Kernel::HLERequestContext& ctx) {
const auto connection_handle{rp.PopRaw<Controller_Palma::PalmaConnectionHandle>()};
const auto unknown{rp.Pop<u64>()};
- const auto buffer = ctx.ReadBuffer();
+ [[maybe_unused]] const auto buffer = ctx.ReadBuffer();
LOG_WARNING(Service_HID, "(STUBBED) called, connection_handle={}, unknown={}",
connection_handle.npad_id, unknown);
@@ -2127,8 +2127,8 @@ void Hid::WritePalmaWaveEntry(Kernel::HLERequestContext& ctx) {
ASSERT_MSG(t_mem_size == 0x3000, "t_mem_size is not 0x3000 bytes");
- auto t_mem =
- system.CurrentProcess()->GetHandleTable().GetObject<Kernel::KTransferMemory>(t_mem_handle);
+ auto t_mem = system.ApplicationProcess()->GetHandleTable().GetObject<Kernel::KTransferMemory>(
+ t_mem_handle);
if (t_mem.IsNull()) {
LOG_ERROR(Service_HID, "t_mem is a nullptr for handle=0x{:08X}", t_mem_handle);
@@ -2734,25 +2734,11 @@ private:
}
};
-class HidTmp final : public ServiceFramework<HidTmp> {
-public:
- explicit HidTmp(Core::System& system_) : ServiceFramework{system_, "hid:tmp"} {
- // clang-format off
- static const FunctionInfo functions[] = {
- {0, nullptr, "GetConsoleSixAxisSensorCalibrationValues"},
- };
- // clang-format on
-
- RegisterHandlers(functions);
- }
-};
-
void InstallInterfaces(SM::ServiceManager& service_manager, Core::System& system) {
std::make_shared<Hid>(system)->InstallAsService(service_manager);
std::make_shared<HidBus>(system)->InstallAsService(service_manager);
std::make_shared<HidDbg>(system)->InstallAsService(service_manager);
std::make_shared<HidSys>(system)->InstallAsService(service_manager);
- std::make_shared<HidTmp>(system)->InstallAsService(service_manager);
std::make_shared<Service::IRS::IRS>(system)->InstallAsService(service_manager);
std::make_shared<Service::IRS::IRS_SYS>(system)->InstallAsService(service_manager);
diff --git a/src/core/hle/service/hid/hidbus.cpp b/src/core/hle/service/hid/hidbus.cpp
index e5e50845f..bd94e8f3d 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) {
const auto parameters{rp.PopRaw<Parameters>()};
- LOG_INFO(Service_HID,
- "called, enable={}, abstracted_pad_id={}, bus_type={}, internal_index={}, "
- "player_number={}, is_valid={}, inval={}, applet_resource_user_id{}",
- parameters.enable, parameters.bus_handle.abstracted_pad_id,
- parameters.bus_handle.bus_type, parameters.bus_handle.internal_index,
- parameters.bus_handle.player_number, parameters.bus_handle.is_valid, parameters.inval,
- parameters.applet_resource_user_id);
+ LOG_DEBUG(Service_HID,
+ "called, enable={}, abstracted_pad_id={}, bus_type={}, internal_index={}, "
+ "player_number={}, is_valid={}, inval={}, applet_resource_user_id{}",
+ parameters.enable, parameters.bus_handle.abstracted_pad_id,
+ parameters.bus_handle.bus_type, parameters.bus_handle.internal_index,
+ parameters.bus_handle.player_number, parameters.bus_handle.is_valid, parameters.inval,
+ parameters.applet_resource_user_id);
const auto device_index = GetDeviceIndexFromHandle(parameters.bus_handle);
@@ -326,11 +326,11 @@ void HidBus::GetExternalDeviceId(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto bus_handle_{rp.PopRaw<BusHandle>()};
- LOG_INFO(Service_HID,
- "called, abstracted_pad_id={}, bus_type={}, internal_index={}, player_number={}, "
- "is_valid={}",
- bus_handle_.abstracted_pad_id, bus_handle_.bus_type, bus_handle_.internal_index,
- bus_handle_.player_number, bus_handle_.is_valid);
+ LOG_DEBUG(Service_HID,
+ "called, abstracted_pad_id={}, bus_type={}, internal_index={}, player_number={}, "
+ "is_valid={}",
+ bus_handle_.abstracted_pad_id, bus_handle_.bus_type, bus_handle_.internal_index,
+ bus_handle_.player_number, bus_handle_.is_valid);
const auto device_index = GetDeviceIndexFromHandle(bus_handle_);
@@ -449,8 +449,8 @@ void HidBus::EnableJoyPollingReceiveMode(Kernel::HLERequestContext& ctx) {
ASSERT_MSG(t_mem_size == 0x1000, "t_mem_size is not 0x1000 bytes");
- auto t_mem =
- system.CurrentProcess()->GetHandleTable().GetObject<Kernel::KTransferMemory>(t_mem_handle);
+ auto t_mem = system.ApplicationProcess()->GetHandleTable().GetObject<Kernel::KTransferMemory>(
+ t_mem_handle);
if (t_mem.IsNull()) {
LOG_ERROR(Service_HID, "t_mem is a nullptr for handle=0x{:08X}", t_mem_handle);
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 @@
#pragma once
#include <array>
+#include <span>
#include "common/common_types.h"
#include "core/hle/result.h"
@@ -150,7 +151,7 @@ public:
}
// Assigns a command from data
- virtual bool SetCommand(const std::vector<u8>& data) {
+ virtual bool SetCommand(std::span<const u8> data) {
return {};
}
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 @@
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
-#include "core/hid/emulated_devices.h"
+#include "core/hid/emulated_controller.h"
#include "core/hid/hid_core.h"
#include "core/hle/kernel/k_event.h"
#include "core/hle/kernel/k_readable_event.h"
@@ -12,16 +12,20 @@ namespace Service::HID {
RingController::RingController(Core::HID::HIDCore& hid_core_,
KernelHelpers::ServiceContext& service_context_)
: HidbusBase(service_context_) {
- input = hid_core_.GetEmulatedDevices();
+ input = hid_core_.GetEmulatedController(Core::HID::NpadIdType::Player1);
}
RingController::~RingController() = default;
void RingController::OnInit() {
+ input->SetPollingMode(Core::HID::EmulatedDeviceIndex::RightIndex,
+ Common::Input::PollingMode::Ring);
return;
}
void RingController::OnRelease() {
+ input->SetPollingMode(Core::HID::EmulatedDeviceIndex::RightIndex,
+ Common::Input::PollingMode::Active);
return;
};
@@ -112,7 +116,7 @@ std::vector<u8> RingController::GetReply() const {
}
}
-bool RingController::SetCommand(const std::vector<u8>& data) {
+bool RingController::SetCommand(std::span<const u8> data) {
if (data.size() < 4) {
LOG_ERROR(Service_HID, "Command size not supported {}", data.size());
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 @@
#pragma once
#include <array>
+#include <span>
#include "common/common_types.h"
#include "core/hle/service/hid/hidbus/hidbus_base.h"
namespace Core::HID {
-class EmulatedDevices;
+class EmulatedController;
} // namespace Core::HID
namespace Service::HID {
@@ -31,7 +32,7 @@ public:
u8 GetDeviceId() const override;
// Assigns a command from data
- bool SetCommand(const std::vector<u8>& data) override;
+ bool SetCommand(std::span<const u8> data) override;
// Returns a reply from a command
std::vector<u8> GetReply() const override;
@@ -248,6 +249,6 @@ private:
.zero = {.value = idle_value, .crc = 225},
};
- Core::HID::EmulatedDevices* input;
+ Core::HID::EmulatedController* input;
};
} // 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 {
return {};
}
-bool Starlink::SetCommand(const std::vector<u8>& data) {
+bool Starlink::SetCommand(std::span<const u8> data) {
LOG_ERROR(Service_HID, "Command not implemented");
return false;
}
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:
u8 GetDeviceId() const override;
// Assigns a command from data
- bool SetCommand(const std::vector<u8>& data) override;
+ bool SetCommand(std::span<const u8> data) override;
// Returns a reply from a command
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 {
return {};
}
-bool HidbusStubbed::SetCommand(const std::vector<u8>& data) {
+bool HidbusStubbed::SetCommand(std::span<const u8> data) {
LOG_ERROR(Service_HID, "Command not implemented");
return false;
}
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:
u8 GetDeviceId() const override;
// Assigns a command from data
- bool SetCommand(const std::vector<u8>& data) override;
+ bool SetCommand(std::span<const u8> data) override;
// Returns a reply from a command
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..3bd418e92 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) {
auto result = IsIrCameraHandleValid(parameters.camera_handle);
if (result.IsSuccess()) {
// TODO: Stop Image processor
+ npad_device->SetPollingMode(Core::HID::EmulatedDeviceIndex::RightIndex,
+ Common::Input::PollingMode::Active);
result = ResultSuccess;
}
@@ -139,6 +141,8 @@ void IRS::RunMomentProcessor(Kernel::HLERequestContext& ctx) {
MakeProcessor<MomentProcessor>(parameters.camera_handle, device);
auto& image_transfer_processor = GetProcessor<MomentProcessor>(parameters.camera_handle);
image_transfer_processor.SetConfig(parameters.processor_config);
+ npad_device->SetPollingMode(Core::HID::EmulatedDeviceIndex::RightIndex,
+ Common::Input::PollingMode::IR);
}
IPC::ResponseBuilder rb{ctx, 2};
@@ -170,6 +174,8 @@ void IRS::RunClusteringProcessor(Kernel::HLERequestContext& ctx) {
auto& image_transfer_processor =
GetProcessor<ClusteringProcessor>(parameters.camera_handle);
image_transfer_processor.SetConfig(parameters.processor_config);
+ npad_device->SetPollingMode(Core::HID::EmulatedDeviceIndex::RightIndex,
+ Common::Input::PollingMode::IR);
}
IPC::ResponseBuilder rb{ctx, 2};
@@ -190,8 +196,8 @@ void IRS::RunImageTransferProcessor(Kernel::HLERequestContext& ctx) {
const auto parameters{rp.PopRaw<Parameters>()};
const auto t_mem_handle{ctx.GetCopyHandle(0)};
- auto t_mem =
- system.CurrentProcess()->GetHandleTable().GetObject<Kernel::KTransferMemory>(t_mem_handle);
+ auto t_mem = system.ApplicationProcess()->GetHandleTable().GetObject<Kernel::KTransferMemory>(
+ t_mem_handle);
if (t_mem.IsNull()) {
LOG_ERROR(Service_IRS, "t_mem is a nullptr for handle=0x{:08X}", t_mem_handle);
@@ -219,6 +225,8 @@ void IRS::RunImageTransferProcessor(Kernel::HLERequestContext& ctx) {
GetProcessor<ImageTransferProcessor>(parameters.camera_handle);
image_transfer_processor.SetConfig(parameters.processor_config);
image_transfer_processor.SetTransferMemoryPointer(transfer_memory);
+ npad_device->SetPollingMode(Core::HID::EmulatedDeviceIndex::RightIndex,
+ Common::Input::PollingMode::IR);
}
IPC::ResponseBuilder rb{ctx, 2};
@@ -294,6 +302,8 @@ void IRS::RunTeraPluginProcessor(Kernel::HLERequestContext& ctx) {
auto& image_transfer_processor =
GetProcessor<TeraPluginProcessor>(parameters.camera_handle);
image_transfer_processor.SetConfig(parameters.processor_config);
+ npad_device->SetPollingMode(Core::HID::EmulatedDeviceIndex::RightIndex,
+ Common::Input::PollingMode::IR);
}
IPC::ResponseBuilder rb{ctx, 2};
@@ -343,6 +353,8 @@ void IRS::RunPointingProcessor(Kernel::HLERequestContext& ctx) {
MakeProcessor<PointingProcessor>(camera_handle, device);
auto& image_transfer_processor = GetProcessor<PointingProcessor>(camera_handle);
image_transfer_processor.SetConfig(processor_config);
+ npad_device->SetPollingMode(Core::HID::EmulatedDeviceIndex::RightIndex,
+ Common::Input::PollingMode::IR);
}
IPC::ResponseBuilder rb{ctx, 2};
@@ -433,8 +445,8 @@ void IRS::RunImageTransferExProcessor(Kernel::HLERequestContext& ctx) {
const auto parameters{rp.PopRaw<Parameters>()};
const auto t_mem_handle{ctx.GetCopyHandle(0)};
- auto t_mem =
- system.CurrentProcess()->GetHandleTable().GetObject<Kernel::KTransferMemory>(t_mem_handle);
+ auto t_mem = system.ApplicationProcess()->GetHandleTable().GetObject<Kernel::KTransferMemory>(
+ t_mem_handle);
u8* transfer_memory = system.Memory().GetPointer(t_mem->GetSourceAddress());
@@ -453,6 +465,8 @@ void IRS::RunImageTransferExProcessor(Kernel::HLERequestContext& ctx) {
GetProcessor<ImageTransferProcessor>(parameters.camera_handle);
image_transfer_processor.SetConfig(parameters.processor_config);
image_transfer_processor.SetTransferMemoryPointer(transfer_memory);
+ npad_device->SetPollingMode(Core::HID::EmulatedDeviceIndex::RightIndex,
+ Common::Input::PollingMode::IR);
}
IPC::ResponseBuilder rb{ctx, 2};
@@ -479,6 +493,8 @@ void IRS::RunIrLedProcessor(Kernel::HLERequestContext& ctx) {
MakeProcessor<IrLedProcessor>(camera_handle, device);
auto& image_transfer_processor = GetProcessor<IrLedProcessor>(camera_handle);
image_transfer_processor.SetConfig(processor_config);
+ npad_device->SetPollingMode(Core::HID::EmulatedDeviceIndex::RightIndex,
+ Common::Input::PollingMode::IR);
}
IPC::ResponseBuilder rb{ctx, 2};
@@ -504,6 +520,8 @@ void IRS::StopImageProcessorAsync(Kernel::HLERequestContext& ctx) {
auto result = IsIrCameraHandleValid(parameters.camera_handle);
if (result.IsSuccess()) {
// TODO: Stop image processor async
+ npad_device->SetPollingMode(Core::HID::EmulatedDeviceIndex::RightIndex,
+ Common::Input::PollingMode::Active);
result = ResultSuccess;
}
diff --git a/src/core/hle/service/jit/jit.cpp b/src/core/hle/service/jit/jit.cpp
index 8f2920c51..47a1277ea 100644
--- a/src/core/hle/service/jit/jit.cpp
+++ b/src/core/hle/service/jit/jit.cpp
@@ -62,7 +62,7 @@ public:
const auto parameters{rp.PopRaw<InputParameters>()};
// Optional input/output buffers
- std::vector<u8> input_buffer{ctx.CanReadBuffer() ? ctx.ReadBuffer() : std::vector<u8>()};
+ const auto input_buffer{ctx.CanReadBuffer() ? ctx.ReadBuffer() : std::span<const u8>()};
std::vector<u8> output_buffer(ctx.CanWriteBuffer() ? ctx.GetWriteBufferSize() : 0);
// Function call prototype:
@@ -132,7 +132,7 @@ public:
const auto command{rp.PopRaw<u64>()};
// Optional input/output buffers
- std::vector<u8> input_buffer{ctx.CanReadBuffer() ? ctx.ReadBuffer() : std::vector<u8>()};
+ const auto input_buffer{ctx.CanReadBuffer() ? ctx.ReadBuffer() : std::span<const u8>()};
std::vector<u8> output_buffer(ctx.CanWriteBuffer() ? ctx.GetWriteBufferSize() : 0);
// Function call prototype:
@@ -353,9 +353,9 @@ public:
return;
}
- // Fetch using the handle table for the current process here,
+ // Fetch using the handle table for the application process here,
// since we are not multiprocess yet.
- const auto& handle_table{system.CurrentProcess()->GetHandleTable()};
+ const auto& handle_table{system.ApplicationProcess()->GetHandleTable()};
auto process{handle_table.GetObject<Kernel::KProcess>(process_handle)};
if (process.IsNull()) {
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:
}
void SetAdvertiseData(Kernel::HLERequestContext& ctx) {
- std::vector<u8> read_buffer = ctx.ReadBuffer();
+ const auto read_buffer = ctx.ReadBuffer();
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(lan_discovery.SetAdvertiseData(read_buffer));
@@ -464,7 +464,7 @@ public:
parameters.security_config.passphrase_size,
parameters.security_config.security_mode, parameters.local_communication_version);
- const std::vector<u8> read_buffer = ctx.ReadBuffer();
+ const auto read_buffer = ctx.ReadBuffer();
if (read_buffer.size() != sizeof(NetworkInfo)) {
LOG_ERROR(Frontend, "NetworkInfo doesn't match read_buffer size!");
IPC::ResponseBuilder rb{ctx, 2};
diff --git a/src/core/hle/service/ldr/ldr.cpp b/src/core/hle/service/ldr/ldr.cpp
index 652441bc2..2d4d6fe3e 100644
--- a/src/core/hle/service/ldr/ldr.cpp
+++ b/src/core/hle/service/ldr/ldr.cpp
@@ -246,7 +246,7 @@ public:
return;
}
- if (system.GetCurrentProcessProgramID() != header.application_id) {
+ if (system.GetApplicationProcessProgramID() != header.application_id) {
LOG_ERROR(Service_LDR,
"Attempting to load NRR with title ID other than current process. (actual "
"{:016X})!",
@@ -542,15 +542,16 @@ public:
}
// Map memory for the NRO
- const auto map_result{MapNro(system.CurrentProcess(), nro_address, nro_size, bss_address,
- bss_size, nro_size + bss_size)};
+ const auto map_result{MapNro(system.ApplicationProcess(), nro_address, nro_size,
+ bss_address, bss_size, nro_size + bss_size)};
if (map_result.Failed()) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(map_result.Code());
}
// Load the NRO into the mapped memory
- if (const auto result{LoadNro(system.CurrentProcess(), header, nro_address, *map_result)};
+ if (const auto result{
+ LoadNro(system.ApplicationProcess(), header, nro_address, *map_result)};
result.IsError()) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(map_result.Code());
@@ -570,7 +571,7 @@ public:
Result UnmapNro(const NROInfo& info) {
// Each region must be unmapped separately to validate memory state
- auto& page_table{system.CurrentProcess()->PageTable()};
+ auto& page_table{system.ApplicationProcess()->PageTable()};
if (info.bss_size != 0) {
CASCADE_CODE(page_table.UnmapCodeMemory(
@@ -641,7 +642,7 @@ public:
LOG_WARNING(Service_LDR, "(STUBBED) called");
initialized = true;
- current_map_addr = system.CurrentProcess()->PageTable().GetAliasCodeRegionStart();
+ current_map_addr = system.ApplicationProcess()->PageTable().GetAliasCodeRegionStart();
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
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) {
return WrongDeviceState;
}
- if (!npad_device->SetPollingMode(Common::Input::PollingMode::NFC)) {
+ if (npad_device->SetPollingMode(Core::HID::EmulatedDeviceIndex::RightIndex,
+ Common::Input::PollingMode::NFC) !=
+ Common::Input::DriverResult::Success) {
LOG_ERROR(Service_NFC, "Nfc not supported");
return NfcDisabled;
}
@@ -141,7 +143,8 @@ Result NfcDevice::StartDetection(NFP::TagProtocol allowed_protocol) {
}
Result NfcDevice::StopDetection() {
- npad_device->SetPollingMode(Common::Input::PollingMode::Active);
+ npad_device->SetPollingMode(Core::HID::EmulatedDeviceIndex::RightIndex,
+ Common::Input::PollingMode::Active);
if (device_state == NFP::DeviceState::Initialized) {
return ResultSuccess;
diff --git a/src/core/hle/service/nfp/nfp_device.cpp b/src/core/hle/service/nfp/nfp_device.cpp
index c860fd1a1..7a6bbbba7 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) {
return WrongDeviceState;
}
- if (!npad_device->SetPollingMode(Common::Input::PollingMode::NFC)) {
+ if (npad_device->SetPollingMode(Core::HID::EmulatedDeviceIndex::RightIndex,
+ Common::Input::PollingMode::NFC) !=
+ Common::Input::DriverResult::Success) {
LOG_ERROR(Service_NFP, "Nfc not supported");
return NfcDisabled;
}
@@ -163,7 +165,8 @@ Result NfpDevice::StartDetection(TagProtocol allowed_protocol) {
}
Result NfpDevice::StopDetection() {
- npad_device->SetPollingMode(Common::Input::PollingMode::Active);
+ npad_device->SetPollingMode(Core::HID::EmulatedDeviceIndex::RightIndex,
+ Common::Input::PollingMode::Active);
if (device_state == DeviceState::Initialized) {
return ResultSuccess;
@@ -615,7 +618,7 @@ Result NfpDevice::RecreateApplicationArea(u32 access_id, std::span<const u8> dat
sizeof(ApplicationArea) - data.size());
// TODO: Investigate why the title id needs to be moddified
- tag_data.title_id = system.GetCurrentProcessProgramID();
+ tag_data.title_id = system.GetApplicationProcessProgramID();
tag_data.title_id = tag_data.title_id | 0x30000000ULL;
tag_data.settings.settings.appdata_initialized.Assign(1);
tag_data.application_area_id = access_id;
diff --git a/src/core/hle/service/nvdrv/core/syncpoint_manager.h b/src/core/hle/service/nvdrv/core/syncpoint_manager.h
index 4f2cefae5..7728ff596 100644
--- a/src/core/hle/service/nvdrv/core/syncpoint_manager.h
+++ b/src/core/hle/service/nvdrv/core/syncpoint_manager.h
@@ -124,7 +124,7 @@ private:
//!< value
};
- constexpr static std::size_t SyncpointCount{192};
+ static constexpr std::size_t SyncpointCount{192};
std::array<SyncpointInfo, SyncpointCount> syncpoints{};
std::mutex reservation_lock;
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 @@
#pragma once
+#include <span>
#include <vector>
+
#include "common/common_types.h"
#include "core/hle/service/nvdrv/nvdata.h"
@@ -31,7 +33,7 @@ public:
* @param output A buffer where the output data will be written to.
* @returns The result code of the ioctl.
*/
- virtual NvResult Ioctl1(DeviceFD fd, Ioctl command, const std::vector<u8>& input,
+ virtual NvResult Ioctl1(DeviceFD fd, Ioctl command, std::span<const u8> input,
std::vector<u8>& output) = 0;
/**
@@ -42,8 +44,8 @@ public:
* @param output A buffer where the output data will be written to.
* @returns The result code of the ioctl.
*/
- virtual NvResult Ioctl2(DeviceFD fd, Ioctl command, const std::vector<u8>& input,
- const std::vector<u8>& inline_input, std::vector<u8>& output) = 0;
+ virtual NvResult Ioctl2(DeviceFD fd, Ioctl command, std::span<const u8> input,
+ std::span<const u8> inline_input, std::vector<u8>& output) = 0;
/**
* Handles an ioctl3 request.
@@ -53,7 +55,7 @@ public:
* @param inline_output A buffer where the inlined output data will be written to.
* @returns The result code of the ioctl.
*/
- virtual NvResult Ioctl3(DeviceFD fd, Ioctl command, const std::vector<u8>& input,
+ virtual NvResult Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> input,
std::vector<u8>& output, std::vector<u8>& inline_output) = 0;
/**
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)
: nvdevice{system_}, container{core}, nvmap{core.GetNvMapFile()} {}
nvdisp_disp0::~nvdisp_disp0() = default;
-NvResult nvdisp_disp0::Ioctl1(DeviceFD fd, Ioctl command, const std::vector<u8>& input,
+NvResult nvdisp_disp0::Ioctl1(DeviceFD fd, Ioctl command, std::span<const u8> input,
std::vector<u8>& output) {
UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);
return NvResult::NotImplemented;
}
-NvResult nvdisp_disp0::Ioctl2(DeviceFD fd, Ioctl command, const std::vector<u8>& input,
- const std::vector<u8>& inline_input, std::vector<u8>& output) {
+NvResult nvdisp_disp0::Ioctl2(DeviceFD fd, Ioctl command, std::span<const u8> input,
+ std::span<const u8> inline_input, std::vector<u8>& output) {
UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);
return NvResult::NotImplemented;
}
-NvResult nvdisp_disp0::Ioctl3(DeviceFD fd, Ioctl command, const std::vector<u8>& input,
+NvResult nvdisp_disp0::Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> input,
std::vector<u8>& output, std::vector<u8>& inline_output) {
UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);
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:
explicit nvdisp_disp0(Core::System& system_, NvCore::Container& core);
~nvdisp_disp0() override;
- NvResult Ioctl1(DeviceFD fd, Ioctl command, const std::vector<u8>& input,
+ NvResult Ioctl1(DeviceFD fd, Ioctl command, std::span<const u8> input,
std::vector<u8>& output) override;
- NvResult Ioctl2(DeviceFD fd, Ioctl command, const std::vector<u8>& input,
- const std::vector<u8>& inline_input, std::vector<u8>& output) override;
- NvResult Ioctl3(DeviceFD fd, Ioctl command, const std::vector<u8>& input,
- std::vector<u8>& output, std::vector<u8>& inline_output) override;
+ NvResult Ioctl2(DeviceFD fd, Ioctl command, std::span<const u8> input,
+ std::span<const u8> inline_input, std::vector<u8>& output) override;
+ NvResult Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> input, std::vector<u8>& output,
+ std::vector<u8>& inline_output) override;
void OnOpen(DeviceFD fd) override;
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
nvhost_as_gpu::~nvhost_as_gpu() = default;
-NvResult nvhost_as_gpu::Ioctl1(DeviceFD fd, Ioctl command, const std::vector<u8>& input,
+NvResult nvhost_as_gpu::Ioctl1(DeviceFD fd, Ioctl command, std::span<const u8> input,
std::vector<u8>& output) {
switch (command.group) {
case 'A':
@@ -60,13 +60,13 @@ NvResult nvhost_as_gpu::Ioctl1(DeviceFD fd, Ioctl command, const std::vector<u8>
return NvResult::NotImplemented;
}
-NvResult nvhost_as_gpu::Ioctl2(DeviceFD fd, Ioctl command, const std::vector<u8>& input,
- const std::vector<u8>& inline_input, std::vector<u8>& output) {
+NvResult nvhost_as_gpu::Ioctl2(DeviceFD fd, Ioctl command, std::span<const u8> input,
+ std::span<const u8> inline_input, std::vector<u8>& output) {
UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);
return NvResult::NotImplemented;
}
-NvResult nvhost_as_gpu::Ioctl3(DeviceFD fd, Ioctl command, const std::vector<u8>& input,
+NvResult nvhost_as_gpu::Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> input,
std::vector<u8>& output, std::vector<u8>& inline_output) {
switch (command.group) {
case 'A':
@@ -87,7 +87,7 @@ NvResult nvhost_as_gpu::Ioctl3(DeviceFD fd, Ioctl command, const std::vector<u8>
void nvhost_as_gpu::OnOpen(DeviceFD fd) {}
void nvhost_as_gpu::OnClose(DeviceFD fd) {}
-NvResult nvhost_as_gpu::AllocAsEx(const std::vector<u8>& input, std::vector<u8>& output) {
+NvResult nvhost_as_gpu::AllocAsEx(std::span<const u8> input, std::vector<u8>& output) {
IoctlAllocAsEx params{};
std::memcpy(&params, input.data(), input.size());
@@ -141,7 +141,7 @@ NvResult nvhost_as_gpu::AllocAsEx(const std::vector<u8>& input, std::vector<u8>&
return NvResult::Success;
}
-NvResult nvhost_as_gpu::AllocateSpace(const std::vector<u8>& input, std::vector<u8>& output) {
+NvResult nvhost_as_gpu::AllocateSpace(std::span<const u8> input, std::vector<u8>& output) {
IoctlAllocSpace params{};
std::memcpy(&params, input.data(), input.size());
@@ -220,7 +220,7 @@ void nvhost_as_gpu::FreeMappingLocked(u64 offset) {
mapping_map.erase(offset);
}
-NvResult nvhost_as_gpu::FreeSpace(const std::vector<u8>& input, std::vector<u8>& output) {
+NvResult nvhost_as_gpu::FreeSpace(std::span<const u8> input, std::vector<u8>& output) {
IoctlFreeSpace params{};
std::memcpy(&params, input.data(), input.size());
@@ -266,7 +266,7 @@ NvResult nvhost_as_gpu::FreeSpace(const std::vector<u8>& input, std::vector<u8>&
return NvResult::Success;
}
-NvResult nvhost_as_gpu::Remap(const std::vector<u8>& input, std::vector<u8>& output) {
+NvResult nvhost_as_gpu::Remap(std::span<const u8> input, std::vector<u8>& output) {
const auto num_entries = input.size() / sizeof(IoctlRemapEntry);
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
return NvResult::Success;
}
-NvResult nvhost_as_gpu::MapBufferEx(const std::vector<u8>& input, std::vector<u8>& output) {
+NvResult nvhost_as_gpu::MapBufferEx(std::span<const u8> input, std::vector<u8>& output) {
IoctlMapBufferEx params{};
std::memcpy(&params, input.data(), input.size());
@@ -424,7 +424,7 @@ NvResult nvhost_as_gpu::MapBufferEx(const std::vector<u8>& input, std::vector<u8
return NvResult::Success;
}
-NvResult nvhost_as_gpu::UnmapBuffer(const std::vector<u8>& input, std::vector<u8>& output) {
+NvResult nvhost_as_gpu::UnmapBuffer(std::span<const u8> input, std::vector<u8>& output) {
IoctlUnmapBuffer params{};
std::memcpy(&params, input.data(), input.size());
@@ -463,7 +463,7 @@ NvResult nvhost_as_gpu::UnmapBuffer(const std::vector<u8>& input, std::vector<u8
return NvResult::Success;
}
-NvResult nvhost_as_gpu::BindChannel(const std::vector<u8>& input, std::vector<u8>& output) {
+NvResult nvhost_as_gpu::BindChannel(std::span<const u8> input, std::vector<u8>& output) {
IoctlBindChannel params{};
std::memcpy(&params, input.data(), input.size());
LOG_DEBUG(Service_NVDRV, "called, fd={:X}", params.fd);
@@ -492,7 +492,7 @@ void nvhost_as_gpu::GetVARegionsImpl(IoctlGetVaRegions& params) {
};
}
-NvResult nvhost_as_gpu::GetVARegions(const std::vector<u8>& input, std::vector<u8>& output) {
+NvResult nvhost_as_gpu::GetVARegions(std::span<const u8> input, std::vector<u8>& output) {
IoctlGetVaRegions params{};
std::memcpy(&params, input.data(), input.size());
@@ -511,7 +511,7 @@ NvResult nvhost_as_gpu::GetVARegions(const std::vector<u8>& input, std::vector<u
return NvResult::Success;
}
-NvResult nvhost_as_gpu::GetVARegions(const std::vector<u8>& input, std::vector<u8>& output,
+NvResult nvhost_as_gpu::GetVARegions(std::span<const u8> input, std::vector<u8>& output,
std::vector<u8>& inline_output) {
IoctlGetVaRegions params{};
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:
explicit nvhost_as_gpu(Core::System& system_, Module& module, NvCore::Container& core);
~nvhost_as_gpu() override;
- NvResult Ioctl1(DeviceFD fd, Ioctl command, const std::vector<u8>& input,
+ NvResult Ioctl1(DeviceFD fd, Ioctl command, std::span<const u8> input,
std::vector<u8>& output) override;
- NvResult Ioctl2(DeviceFD fd, Ioctl command, const std::vector<u8>& input,
- const std::vector<u8>& inline_input, std::vector<u8>& output) override;
- NvResult Ioctl3(DeviceFD fd, Ioctl command, const std::vector<u8>& input,
- std::vector<u8>& output, std::vector<u8>& inline_output) override;
+ NvResult Ioctl2(DeviceFD fd, Ioctl command, std::span<const u8> input,
+ std::span<const u8> inline_input, std::vector<u8>& output) override;
+ NvResult Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> input, std::vector<u8>& output,
+ std::vector<u8>& inline_output) override;
void OnOpen(DeviceFD fd) override;
void OnClose(DeviceFD fd) override;
@@ -138,17 +138,17 @@ private:
static_assert(sizeof(IoctlGetVaRegions) == 16 + sizeof(VaRegion) * 2,
"IoctlGetVaRegions is incorrect size");
- NvResult AllocAsEx(const std::vector<u8>& input, std::vector<u8>& output);
- NvResult AllocateSpace(const std::vector<u8>& input, std::vector<u8>& output);
- NvResult Remap(const std::vector<u8>& input, std::vector<u8>& output);
- NvResult MapBufferEx(const std::vector<u8>& input, std::vector<u8>& output);
- NvResult UnmapBuffer(const std::vector<u8>& input, std::vector<u8>& output);
- NvResult FreeSpace(const std::vector<u8>& input, std::vector<u8>& output);
- NvResult BindChannel(const std::vector<u8>& input, std::vector<u8>& output);
+ NvResult AllocAsEx(std::span<const u8> input, std::vector<u8>& output);
+ NvResult AllocateSpace(std::span<const u8> input, std::vector<u8>& output);
+ NvResult Remap(std::span<const u8> input, std::vector<u8>& output);
+ NvResult MapBufferEx(std::span<const u8> input, std::vector<u8>& output);
+ NvResult UnmapBuffer(std::span<const u8> input, std::vector<u8>& output);
+ NvResult FreeSpace(std::span<const u8> input, std::vector<u8>& output);
+ NvResult BindChannel(std::span<const u8> input, std::vector<u8>& output);
void GetVARegionsImpl(IoctlGetVaRegions& params);
- NvResult GetVARegions(const std::vector<u8>& input, std::vector<u8>& output);
- NvResult GetVARegions(const std::vector<u8>& input, std::vector<u8>& output,
+ NvResult GetVARegions(std::span<const u8> input, std::vector<u8>& output);
+ NvResult GetVARegions(std::span<const u8> input, std::vector<u8>& output,
std::vector<u8>& inline_output);
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..e12025560 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() {
}
}
-NvResult nvhost_ctrl::Ioctl1(DeviceFD fd, Ioctl command, const std::vector<u8>& input,
+NvResult nvhost_ctrl::Ioctl1(DeviceFD fd, Ioctl command, std::span<const u8> input,
std::vector<u8>& output) {
switch (command.group) {
case 0x0:
@@ -63,13 +63,13 @@ NvResult nvhost_ctrl::Ioctl1(DeviceFD fd, Ioctl command, const std::vector<u8>&
return NvResult::NotImplemented;
}
-NvResult nvhost_ctrl::Ioctl2(DeviceFD fd, Ioctl command, const std::vector<u8>& input,
- const std::vector<u8>& inline_input, std::vector<u8>& output) {
+NvResult nvhost_ctrl::Ioctl2(DeviceFD fd, Ioctl command, std::span<const u8> input,
+ std::span<const u8> inline_input, std::vector<u8>& output) {
UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);
return NvResult::NotImplemented;
}
-NvResult nvhost_ctrl::Ioctl3(DeviceFD fd, Ioctl command, const std::vector<u8>& input,
+NvResult nvhost_ctrl::Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> input,
std::vector<u8>& output, std::vector<u8>& inline_outpu) {
UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);
return NvResult::NotImplemented;
@@ -79,7 +79,7 @@ void nvhost_ctrl::OnOpen(DeviceFD fd) {}
void nvhost_ctrl::OnClose(DeviceFD fd) {}
-NvResult nvhost_ctrl::NvOsGetConfigU32(const std::vector<u8>& input, std::vector<u8>& output) {
+NvResult nvhost_ctrl::NvOsGetConfigU32(std::span<const u8> input, std::vector<u8>& output) {
IocGetConfigParams params{};
std::memcpy(&params, input.data(), sizeof(params));
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
return NvResult::ConfigVarNotFound; // Returns error on production mode
}
-NvResult nvhost_ctrl::IocCtrlEventWait(const std::vector<u8>& input, std::vector<u8>& output,
+NvResult nvhost_ctrl::IocCtrlEventWait(std::span<const u8> input, std::vector<u8>& output,
bool is_allocation) {
IocCtrlEventWaitParams params{};
std::memcpy(&params, input.data(), sizeof(params));
@@ -150,9 +150,9 @@ NvResult nvhost_ctrl::IocCtrlEventWait(const std::vector<u8>& input, std::vector
const auto check_failing = [&]() {
if (events[slot].fails > 2) {
{
- auto lk = system.StallProcesses();
+ auto lk = system.StallApplication();
host1x_syncpoint_manager.WaitHost(fence_id, target_value);
- system.UnstallProcesses();
+ system.UnstallApplication();
}
params.value.raw = target_value;
return true;
@@ -231,7 +231,7 @@ NvResult nvhost_ctrl::FreeEvent(u32 slot) {
return NvResult::Success;
}
-NvResult nvhost_ctrl::IocCtrlEventRegister(const std::vector<u8>& input, std::vector<u8>& output) {
+NvResult nvhost_ctrl::IocCtrlEventRegister(std::span<const u8> input, std::vector<u8>& output) {
IocCtrlEventRegisterParams params{};
std::memcpy(&params, input.data(), sizeof(params));
const u32 event_id = params.user_event_id;
@@ -252,8 +252,7 @@ NvResult nvhost_ctrl::IocCtrlEventRegister(const std::vector<u8>& input, std::ve
return NvResult::Success;
}
-NvResult nvhost_ctrl::IocCtrlEventUnregister(const std::vector<u8>& input,
- std::vector<u8>& output) {
+NvResult nvhost_ctrl::IocCtrlEventUnregister(std::span<const u8> input, std::vector<u8>& output) {
IocCtrlEventUnregisterParams params{};
std::memcpy(&params, input.data(), sizeof(params));
const u32 event_id = params.user_event_id & 0x00FF;
@@ -263,7 +262,7 @@ NvResult nvhost_ctrl::IocCtrlEventUnregister(const std::vector<u8>& input,
return FreeEvent(event_id);
}
-NvResult nvhost_ctrl::IocCtrlEventUnregisterBatch(const std::vector<u8>& input,
+NvResult nvhost_ctrl::IocCtrlEventUnregisterBatch(std::span<const u8> input,
std::vector<u8>& output) {
IocCtrlEventUnregisterBatchParams params{};
std::memcpy(&params, input.data(), sizeof(params));
@@ -282,7 +281,7 @@ NvResult nvhost_ctrl::IocCtrlEventUnregisterBatch(const std::vector<u8>& input,
return NvResult::Success;
}
-NvResult nvhost_ctrl::IocCtrlClearEventWait(const std::vector<u8>& input, std::vector<u8>& output) {
+NvResult nvhost_ctrl::IocCtrlClearEventWait(std::span<const u8> input, std::vector<u8>& output) {
IocCtrlEventClearParams params{};
std::memcpy(&params, input.data(), sizeof(params));
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:
NvCore::Container& core);
~nvhost_ctrl() override;
- NvResult Ioctl1(DeviceFD fd, Ioctl command, const std::vector<u8>& input,
+ NvResult Ioctl1(DeviceFD fd, Ioctl command, std::span<const u8> input,
std::vector<u8>& output) override;
- NvResult Ioctl2(DeviceFD fd, Ioctl command, const std::vector<u8>& input,
- const std::vector<u8>& inline_input, std::vector<u8>& output) override;
- NvResult Ioctl3(DeviceFD fd, Ioctl command, const std::vector<u8>& input,
- std::vector<u8>& output, std::vector<u8>& inline_output) override;
+ NvResult Ioctl2(DeviceFD fd, Ioctl command, std::span<const u8> input,
+ std::span<const u8> inline_input, std::vector<u8>& output) override;
+ NvResult Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> input, std::vector<u8>& output,
+ std::vector<u8>& inline_output) override;
void OnOpen(DeviceFD fd) override;
void OnClose(DeviceFD fd) override;
@@ -186,13 +186,13 @@ private:
static_assert(sizeof(IocCtrlEventUnregisterBatchParams) == 8,
"IocCtrlEventKill is incorrect size");
- NvResult NvOsGetConfigU32(const std::vector<u8>& input, std::vector<u8>& output);
- NvResult IocCtrlEventWait(const std::vector<u8>& input, std::vector<u8>& output,
+ NvResult NvOsGetConfigU32(std::span<const u8> input, std::vector<u8>& output);
+ NvResult IocCtrlEventWait(std::span<const u8> input, std::vector<u8>& output,
bool is_allocation);
- NvResult IocCtrlEventRegister(const std::vector<u8>& input, std::vector<u8>& output);
- NvResult IocCtrlEventUnregister(const std::vector<u8>& input, std::vector<u8>& output);
- NvResult IocCtrlEventUnregisterBatch(const std::vector<u8>& input, std::vector<u8>& output);
- NvResult IocCtrlClearEventWait(const std::vector<u8>& input, std::vector<u8>& output);
+ NvResult IocCtrlEventRegister(std::span<const u8> input, std::vector<u8>& output);
+ NvResult IocCtrlEventUnregister(std::span<const u8> input, std::vector<u8>& output);
+ NvResult IocCtrlEventUnregisterBatch(std::span<const u8> input, std::vector<u8>& output);
+ NvResult IocCtrlClearEventWait(std::span<const u8> input, std::vector<u8>& output);
NvResult FreeEvent(u32 slot);
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() {
events_interface.FreeEvent(unknown_event);
}
-NvResult nvhost_ctrl_gpu::Ioctl1(DeviceFD fd, Ioctl command, const std::vector<u8>& input,
+NvResult nvhost_ctrl_gpu::Ioctl1(DeviceFD fd, Ioctl command, std::span<const u8> input,
std::vector<u8>& output) {
switch (command.group) {
case 'G':
@@ -53,13 +53,13 @@ NvResult nvhost_ctrl_gpu::Ioctl1(DeviceFD fd, Ioctl command, const std::vector<u
return NvResult::NotImplemented;
}
-NvResult nvhost_ctrl_gpu::Ioctl2(DeviceFD fd, Ioctl command, const std::vector<u8>& input,
- const std::vector<u8>& inline_input, std::vector<u8>& output) {
+NvResult nvhost_ctrl_gpu::Ioctl2(DeviceFD fd, Ioctl command, std::span<const u8> input,
+ std::span<const u8> inline_input, std::vector<u8>& output) {
UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);
return NvResult::NotImplemented;
}
-NvResult nvhost_ctrl_gpu::Ioctl3(DeviceFD fd, Ioctl command, const std::vector<u8>& input,
+NvResult nvhost_ctrl_gpu::Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> input,
std::vector<u8>& output, std::vector<u8>& inline_output) {
switch (command.group) {
case 'G':
@@ -82,8 +82,7 @@ NvResult nvhost_ctrl_gpu::Ioctl3(DeviceFD fd, Ioctl command, const std::vector<u
void nvhost_ctrl_gpu::OnOpen(DeviceFD fd) {}
void nvhost_ctrl_gpu::OnClose(DeviceFD fd) {}
-NvResult nvhost_ctrl_gpu::GetCharacteristics(const std::vector<u8>& input,
- std::vector<u8>& output) {
+NvResult nvhost_ctrl_gpu::GetCharacteristics(std::span<const u8> input, std::vector<u8>& output) {
LOG_DEBUG(Service_NVDRV, "called");
IoctlCharacteristics params{};
std::memcpy(&params, input.data(), input.size());
@@ -128,7 +127,7 @@ NvResult nvhost_ctrl_gpu::GetCharacteristics(const std::vector<u8>& input,
return NvResult::Success;
}
-NvResult nvhost_ctrl_gpu::GetCharacteristics(const std::vector<u8>& input, std::vector<u8>& output,
+NvResult nvhost_ctrl_gpu::GetCharacteristics(std::span<const u8> input, std::vector<u8>& output,
std::vector<u8>& inline_output) {
LOG_DEBUG(Service_NVDRV, "called");
IoctlCharacteristics params{};
@@ -176,7 +175,7 @@ NvResult nvhost_ctrl_gpu::GetCharacteristics(const std::vector<u8>& input, std::
return NvResult::Success;
}
-NvResult nvhost_ctrl_gpu::GetTPCMasks(const std::vector<u8>& input, std::vector<u8>& output) {
+NvResult nvhost_ctrl_gpu::GetTPCMasks(std::span<const u8> input, std::vector<u8>& output) {
IoctlGpuGetTpcMasksArgs params{};
std::memcpy(&params, input.data(), input.size());
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<
return NvResult::Success;
}
-NvResult nvhost_ctrl_gpu::GetTPCMasks(const std::vector<u8>& input, std::vector<u8>& output,
+NvResult nvhost_ctrl_gpu::GetTPCMasks(std::span<const u8> input, std::vector<u8>& output,
std::vector<u8>& inline_output) {
IoctlGpuGetTpcMasksArgs params{};
std::memcpy(&params, input.data(), input.size());
@@ -200,7 +199,7 @@ NvResult nvhost_ctrl_gpu::GetTPCMasks(const std::vector<u8>& input, std::vector<
return NvResult::Success;
}
-NvResult nvhost_ctrl_gpu::GetActiveSlotMask(const std::vector<u8>& input, std::vector<u8>& output) {
+NvResult nvhost_ctrl_gpu::GetActiveSlotMask(std::span<const u8> input, std::vector<u8>& output) {
LOG_DEBUG(Service_NVDRV, "called");
IoctlActiveSlotMask params{};
@@ -213,7 +212,7 @@ NvResult nvhost_ctrl_gpu::GetActiveSlotMask(const std::vector<u8>& input, std::v
return NvResult::Success;
}
-NvResult nvhost_ctrl_gpu::ZCullGetCtxSize(const std::vector<u8>& input, std::vector<u8>& output) {
+NvResult nvhost_ctrl_gpu::ZCullGetCtxSize(std::span<const u8> input, std::vector<u8>& output) {
LOG_DEBUG(Service_NVDRV, "called");
IoctlZcullGetCtxSize params{};
@@ -225,7 +224,7 @@ NvResult nvhost_ctrl_gpu::ZCullGetCtxSize(const std::vector<u8>& input, std::vec
return NvResult::Success;
}
-NvResult nvhost_ctrl_gpu::ZCullGetInfo(const std::vector<u8>& input, std::vector<u8>& output) {
+NvResult nvhost_ctrl_gpu::ZCullGetInfo(std::span<const u8> input, std::vector<u8>& output) {
LOG_DEBUG(Service_NVDRV, "called");
IoctlNvgpuGpuZcullGetInfoArgs params{};
@@ -248,7 +247,7 @@ NvResult nvhost_ctrl_gpu::ZCullGetInfo(const std::vector<u8>& input, std::vector
return NvResult::Success;
}
-NvResult nvhost_ctrl_gpu::ZBCSetTable(const std::vector<u8>& input, std::vector<u8>& output) {
+NvResult nvhost_ctrl_gpu::ZBCSetTable(std::span<const u8> input, std::vector<u8>& output) {
LOG_WARNING(Service_NVDRV, "(STUBBED) called");
IoctlZbcSetTable params{};
@@ -264,7 +263,7 @@ NvResult nvhost_ctrl_gpu::ZBCSetTable(const std::vector<u8>& input, std::vector<
return NvResult::Success;
}
-NvResult nvhost_ctrl_gpu::ZBCQueryTable(const std::vector<u8>& input, std::vector<u8>& output) {
+NvResult nvhost_ctrl_gpu::ZBCQueryTable(std::span<const u8> input, std::vector<u8>& output) {
LOG_WARNING(Service_NVDRV, "(STUBBED) called");
IoctlZbcQueryTable params{};
@@ -274,7 +273,7 @@ NvResult nvhost_ctrl_gpu::ZBCQueryTable(const std::vector<u8>& input, std::vecto
return NvResult::Success;
}
-NvResult nvhost_ctrl_gpu::FlushL2(const std::vector<u8>& input, std::vector<u8>& output) {
+NvResult nvhost_ctrl_gpu::FlushL2(std::span<const u8> input, std::vector<u8>& output) {
LOG_WARNING(Service_NVDRV, "(STUBBED) called");
IoctlFlushL2 params{};
@@ -284,7 +283,7 @@ NvResult nvhost_ctrl_gpu::FlushL2(const std::vector<u8>& input, std::vector<u8>&
return NvResult::Success;
}
-NvResult nvhost_ctrl_gpu::GetGpuTime(const std::vector<u8>& input, std::vector<u8>& output) {
+NvResult nvhost_ctrl_gpu::GetGpuTime(std::span<const u8> input, std::vector<u8>& output) {
LOG_DEBUG(Service_NVDRV, "called");
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:
explicit nvhost_ctrl_gpu(Core::System& system_, EventInterface& events_interface_);
~nvhost_ctrl_gpu() override;
- NvResult Ioctl1(DeviceFD fd, Ioctl command, const std::vector<u8>& input,
+ NvResult Ioctl1(DeviceFD fd, Ioctl command, std::span<const u8> input,
std::vector<u8>& output) override;
- NvResult Ioctl2(DeviceFD fd, Ioctl command, const std::vector<u8>& input,
- const std::vector<u8>& inline_input, std::vector<u8>& output) override;
- NvResult Ioctl3(DeviceFD fd, Ioctl command, const std::vector<u8>& input,
- std::vector<u8>& output, std::vector<u8>& inline_output) override;
+ NvResult Ioctl2(DeviceFD fd, Ioctl command, std::span<const u8> input,
+ std::span<const u8> inline_input, std::vector<u8>& output) override;
+ NvResult Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> input, std::vector<u8>& output,
+ std::vector<u8>& inline_output) override;
void OnOpen(DeviceFD fd) override;
void OnClose(DeviceFD fd) override;
@@ -151,21 +151,21 @@ private:
};
static_assert(sizeof(IoctlGetGpuTime) == 0x10, "IoctlGetGpuTime is incorrect size");
- NvResult GetCharacteristics(const std::vector<u8>& input, std::vector<u8>& output);
- NvResult GetCharacteristics(const std::vector<u8>& input, std::vector<u8>& output,
+ NvResult GetCharacteristics(std::span<const u8> input, std::vector<u8>& output);
+ NvResult GetCharacteristics(std::span<const u8> input, std::vector<u8>& output,
std::vector<u8>& inline_output);
- NvResult GetTPCMasks(const std::vector<u8>& input, std::vector<u8>& output);
- NvResult GetTPCMasks(const std::vector<u8>& input, std::vector<u8>& output,
+ NvResult GetTPCMasks(std::span<const u8> input, std::vector<u8>& output);
+ NvResult GetTPCMasks(std::span<const u8> input, std::vector<u8>& output,
std::vector<u8>& inline_output);
- NvResult GetActiveSlotMask(const std::vector<u8>& input, std::vector<u8>& output);
- NvResult ZCullGetCtxSize(const std::vector<u8>& input, std::vector<u8>& output);
- NvResult ZCullGetInfo(const std::vector<u8>& input, std::vector<u8>& output);
- NvResult ZBCSetTable(const std::vector<u8>& input, std::vector<u8>& output);
- NvResult ZBCQueryTable(const std::vector<u8>& input, std::vector<u8>& output);
- NvResult FlushL2(const std::vector<u8>& input, std::vector<u8>& output);
- NvResult GetGpuTime(const std::vector<u8>& input, std::vector<u8>& output);
+ NvResult GetActiveSlotMask(std::span<const u8> input, std::vector<u8>& output);
+ NvResult ZCullGetCtxSize(std::span<const u8> input, std::vector<u8>& output);
+ NvResult ZCullGetInfo(std::span<const u8> input, std::vector<u8>& output);
+ NvResult ZBCSetTable(std::span<const u8> input, std::vector<u8>& output);
+ NvResult ZBCQueryTable(std::span<const u8> input, std::vector<u8>& output);
+ NvResult FlushL2(std::span<const u8> input, std::vector<u8>& output);
+ NvResult GetGpuTime(std::span<const u8> input, std::vector<u8>& output);
EventInterface& events_interface;
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() {
syncpoint_manager.FreeSyncpoint(channel_syncpoint);
}
-NvResult nvhost_gpu::Ioctl1(DeviceFD fd, Ioctl command, const std::vector<u8>& input,
+NvResult nvhost_gpu::Ioctl1(DeviceFD fd, Ioctl command, std::span<const u8> input,
std::vector<u8>& output) {
switch (command.group) {
case 0x0:
@@ -98,8 +98,8 @@ NvResult nvhost_gpu::Ioctl1(DeviceFD fd, Ioctl command, const std::vector<u8>& i
return NvResult::NotImplemented;
};
-NvResult nvhost_gpu::Ioctl2(DeviceFD fd, Ioctl command, const std::vector<u8>& input,
- const std::vector<u8>& inline_input, std::vector<u8>& output) {
+NvResult nvhost_gpu::Ioctl2(DeviceFD fd, Ioctl command, std::span<const u8> input,
+ std::span<const u8> inline_input, std::vector<u8>& output) {
switch (command.group) {
case 'H':
switch (command.cmd) {
@@ -112,7 +112,7 @@ NvResult nvhost_gpu::Ioctl2(DeviceFD fd, Ioctl command, const std::vector<u8>& i
return NvResult::NotImplemented;
}
-NvResult nvhost_gpu::Ioctl3(DeviceFD fd, Ioctl command, const std::vector<u8>& input,
+NvResult nvhost_gpu::Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> input,
std::vector<u8>& output, std::vector<u8>& inline_output) {
UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);
return NvResult::NotImplemented;
@@ -121,7 +121,7 @@ NvResult nvhost_gpu::Ioctl3(DeviceFD fd, Ioctl command, const std::vector<u8>& i
void nvhost_gpu::OnOpen(DeviceFD fd) {}
void nvhost_gpu::OnClose(DeviceFD fd) {}
-NvResult nvhost_gpu::SetNVMAPfd(const std::vector<u8>& input, std::vector<u8>& output) {
+NvResult nvhost_gpu::SetNVMAPfd(std::span<const u8> input, std::vector<u8>& output) {
IoctlSetNvmapFD params{};
std::memcpy(&params, input.data(), input.size());
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
return NvResult::Success;
}
-NvResult nvhost_gpu::SetClientData(const std::vector<u8>& input, std::vector<u8>& output) {
+NvResult nvhost_gpu::SetClientData(std::span<const u8> input, std::vector<u8>& output) {
LOG_DEBUG(Service_NVDRV, "called");
IoctlClientData params{};
@@ -139,7 +139,7 @@ NvResult nvhost_gpu::SetClientData(const std::vector<u8>& input, std::vector<u8>
return NvResult::Success;
}
-NvResult nvhost_gpu::GetClientData(const std::vector<u8>& input, std::vector<u8>& output) {
+NvResult nvhost_gpu::GetClientData(std::span<const u8> input, std::vector<u8>& output) {
LOG_DEBUG(Service_NVDRV, "called");
IoctlClientData params{};
@@ -149,7 +149,7 @@ NvResult nvhost_gpu::GetClientData(const std::vector<u8>& input, std::vector<u8>
return NvResult::Success;
}
-NvResult nvhost_gpu::ZCullBind(const std::vector<u8>& input, std::vector<u8>& output) {
+NvResult nvhost_gpu::ZCullBind(std::span<const u8> input, std::vector<u8>& output) {
std::memcpy(&zcull_params, input.data(), input.size());
LOG_DEBUG(Service_NVDRV, "called, gpu_va={:X}, mode={:X}", zcull_params.gpu_va,
zcull_params.mode);
@@ -158,7 +158,7 @@ NvResult nvhost_gpu::ZCullBind(const std::vector<u8>& input, std::vector<u8>& ou
return NvResult::Success;
}
-NvResult nvhost_gpu::SetErrorNotifier(const std::vector<u8>& input, std::vector<u8>& output) {
+NvResult nvhost_gpu::SetErrorNotifier(std::span<const u8> input, std::vector<u8>& output) {
IoctlSetErrorNotifier params{};
std::memcpy(&params, input.data(), input.size());
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<
return NvResult::Success;
}
-NvResult nvhost_gpu::SetChannelPriority(const std::vector<u8>& input, std::vector<u8>& output) {
+NvResult nvhost_gpu::SetChannelPriority(std::span<const u8> input, std::vector<u8>& output) {
std::memcpy(&channel_priority, input.data(), input.size());
LOG_DEBUG(Service_NVDRV, "(STUBBED) called, priority={:X}", channel_priority);
return NvResult::Success;
}
-NvResult nvhost_gpu::AllocGPFIFOEx2(const std::vector<u8>& input, std::vector<u8>& output) {
+NvResult nvhost_gpu::AllocGPFIFOEx2(std::span<const u8> input, std::vector<u8>& output) {
IoctlAllocGpfifoEx2 params{};
std::memcpy(&params, input.data(), input.size());
LOG_WARNING(Service_NVDRV,
@@ -197,7 +197,7 @@ NvResult nvhost_gpu::AllocGPFIFOEx2(const std::vector<u8>& input, std::vector<u8
return NvResult::Success;
}
-NvResult nvhost_gpu::AllocateObjectContext(const std::vector<u8>& input, std::vector<u8>& output) {
+NvResult nvhost_gpu::AllocateObjectContext(std::span<const u8> input, std::vector<u8>& output) {
IoctlAllocObjCtx params{};
std::memcpy(&params, input.data(), input.size());
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>
return NvResult::Success;
}
-NvResult nvhost_gpu::SubmitGPFIFOBase(const std::vector<u8>& input, std::vector<u8>& output,
+NvResult nvhost_gpu::SubmitGPFIFOBase(std::span<const u8> input, std::vector<u8>& output,
bool kickoff) {
if (input.size() < sizeof(IoctlSubmitGpfifo)) {
UNIMPLEMENTED();
@@ -314,8 +314,7 @@ NvResult nvhost_gpu::SubmitGPFIFOBase(const std::vector<u8>& input, std::vector<
return SubmitGPFIFOImpl(params, output, std::move(entries));
}
-NvResult nvhost_gpu::SubmitGPFIFOBase(const std::vector<u8>& input,
- const std::vector<u8>& input_inline,
+NvResult nvhost_gpu::SubmitGPFIFOBase(std::span<const u8> input, std::span<const u8> input_inline,
std::vector<u8>& output) {
if (input.size() < sizeof(IoctlSubmitGpfifo)) {
UNIMPLEMENTED();
@@ -328,7 +327,7 @@ NvResult nvhost_gpu::SubmitGPFIFOBase(const std::vector<u8>& input,
return SubmitGPFIFOImpl(params, output, std::move(entries));
}
-NvResult nvhost_gpu::GetWaitbase(const std::vector<u8>& input, std::vector<u8>& output) {
+NvResult nvhost_gpu::GetWaitbase(std::span<const u8> input, std::vector<u8>& output) {
IoctlGetWaitbase params{};
std::memcpy(&params, input.data(), sizeof(IoctlGetWaitbase));
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>&
return NvResult::Success;
}
-NvResult nvhost_gpu::ChannelSetTimeout(const std::vector<u8>& input, std::vector<u8>& output) {
+NvResult nvhost_gpu::ChannelSetTimeout(std::span<const u8> input, std::vector<u8>& output) {
IoctlChannelSetTimeout params{};
std::memcpy(&params, input.data(), sizeof(IoctlChannelSetTimeout));
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
return NvResult::Success;
}
-NvResult nvhost_gpu::ChannelSetTimeslice(const std::vector<u8>& input, std::vector<u8>& output) {
+NvResult nvhost_gpu::ChannelSetTimeslice(std::span<const u8> input, std::vector<u8>& output) {
IoctlSetTimeslice params{};
std::memcpy(&params, input.data(), sizeof(IoctlSetTimeslice));
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:
NvCore::Container& core);
~nvhost_gpu() override;
- NvResult Ioctl1(DeviceFD fd, Ioctl command, const std::vector<u8>& input,
+ NvResult Ioctl1(DeviceFD fd, Ioctl command, std::span<const u8> input,
std::vector<u8>& output) override;
- NvResult Ioctl2(DeviceFD fd, Ioctl command, const std::vector<u8>& input,
- const std::vector<u8>& inline_input, std::vector<u8>& output) override;
- NvResult Ioctl3(DeviceFD fd, Ioctl command, const std::vector<u8>& input,
- std::vector<u8>& output, std::vector<u8>& inline_output) override;
+ NvResult Ioctl2(DeviceFD fd, Ioctl command, std::span<const u8> input,
+ std::span<const u8> inline_input, std::vector<u8>& output) override;
+ NvResult Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> input, std::vector<u8>& output,
+ std::vector<u8>& inline_output) override;
void OnOpen(DeviceFD fd) override;
void OnClose(DeviceFD fd) override;
@@ -186,23 +186,23 @@ private:
u32_le channel_priority{};
u32_le channel_timeslice{};
- NvResult SetNVMAPfd(const std::vector<u8>& input, std::vector<u8>& output);
- NvResult SetClientData(const std::vector<u8>& input, std::vector<u8>& output);
- NvResult GetClientData(const std::vector<u8>& input, std::vector<u8>& output);
- NvResult ZCullBind(const std::vector<u8>& input, std::vector<u8>& output);
- NvResult SetErrorNotifier(const std::vector<u8>& input, std::vector<u8>& output);
- NvResult SetChannelPriority(const std::vector<u8>& input, std::vector<u8>& output);
- NvResult AllocGPFIFOEx2(const std::vector<u8>& input, std::vector<u8>& output);
- NvResult AllocateObjectContext(const std::vector<u8>& input, std::vector<u8>& output);
+ NvResult SetNVMAPfd(std::span<const u8> input, std::vector<u8>& output);
+ NvResult SetClientData(std::span<const u8> input, std::vector<u8>& output);
+ NvResult GetClientData(std::span<const u8> input, std::vector<u8>& output);
+ NvResult ZCullBind(std::span<const u8> input, std::vector<u8>& output);
+ NvResult SetErrorNotifier(std::span<const u8> input, std::vector<u8>& output);
+ NvResult SetChannelPriority(std::span<const u8> input, std::vector<u8>& output);
+ NvResult AllocGPFIFOEx2(std::span<const u8> input, std::vector<u8>& output);
+ NvResult AllocateObjectContext(std::span<const u8> input, std::vector<u8>& output);
NvResult SubmitGPFIFOImpl(IoctlSubmitGpfifo& params, std::vector<u8>& output,
Tegra::CommandList&& entries);
- NvResult SubmitGPFIFOBase(const std::vector<u8>& input, std::vector<u8>& output,
+ NvResult SubmitGPFIFOBase(std::span<const u8> input, std::vector<u8>& output,
bool kickoff = false);
- NvResult SubmitGPFIFOBase(const std::vector<u8>& input, const std::vector<u8>& input_inline,
+ NvResult SubmitGPFIFOBase(std::span<const u8> input, std::span<const u8> input_inline,
std::vector<u8>& output);
- NvResult GetWaitbase(const std::vector<u8>& input, std::vector<u8>& output);
- NvResult ChannelSetTimeout(const std::vector<u8>& input, std::vector<u8>& output);
- NvResult ChannelSetTimeslice(const std::vector<u8>& input, std::vector<u8>& output);
+ NvResult GetWaitbase(std::span<const u8> input, std::vector<u8>& output);
+ NvResult ChannelSetTimeout(std::span<const u8> input, std::vector<u8>& output);
+ NvResult ChannelSetTimeslice(std::span<const u8> input, std::vector<u8>& output);
EventInterface& events_interface;
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_)
: nvhost_nvdec_common{system_, core_, NvCore::ChannelType::NvDec} {}
nvhost_nvdec::~nvhost_nvdec() = default;
-NvResult nvhost_nvdec::Ioctl1(DeviceFD fd, Ioctl command, const std::vector<u8>& input,
+NvResult nvhost_nvdec::Ioctl1(DeviceFD fd, Ioctl command, std::span<const u8> input,
std::vector<u8>& output) {
switch (command.group) {
case 0x0:
@@ -55,13 +55,13 @@ NvResult nvhost_nvdec::Ioctl1(DeviceFD fd, Ioctl command, const std::vector<u8>&
return NvResult::NotImplemented;
}
-NvResult nvhost_nvdec::Ioctl2(DeviceFD fd, Ioctl command, const std::vector<u8>& input,
- const std::vector<u8>& inline_input, std::vector<u8>& output) {
+NvResult nvhost_nvdec::Ioctl2(DeviceFD fd, Ioctl command, std::span<const u8> input,
+ std::span<const u8> inline_input, std::vector<u8>& output) {
UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);
return NvResult::NotImplemented;
}
-NvResult nvhost_nvdec::Ioctl3(DeviceFD fd, Ioctl command, const std::vector<u8>& input,
+NvResult nvhost_nvdec::Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> input,
std::vector<u8>& output, std::vector<u8>& inline_output) {
UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);
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:
explicit nvhost_nvdec(Core::System& system_, NvCore::Container& core);
~nvhost_nvdec() override;
- NvResult Ioctl1(DeviceFD fd, Ioctl command, const std::vector<u8>& input,
+ NvResult Ioctl1(DeviceFD fd, Ioctl command, std::span<const u8> input,
std::vector<u8>& output) override;
- NvResult Ioctl2(DeviceFD fd, Ioctl command, const std::vector<u8>& input,
- const std::vector<u8>& inline_input, std::vector<u8>& output) override;
- NvResult Ioctl3(DeviceFD fd, Ioctl command, const std::vector<u8>& input,
- std::vector<u8>& output, std::vector<u8>& inline_output) override;
+ NvResult Ioctl2(DeviceFD fd, Ioctl command, std::span<const u8> input,
+ std::span<const u8> inline_input, std::vector<u8>& output) override;
+ NvResult Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> input, std::vector<u8>& output,
+ std::vector<u8>& inline_output) override;
void OnOpen(DeviceFD fd) override;
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 {
// Copies count amount of type T from the input vector into the dst vector.
// Returns the number of bytes written into dst.
template <typename T>
-std::size_t SliceVectors(const std::vector<u8>& input, std::vector<T>& dst, std::size_t count,
+std::size_t SliceVectors(std::span<const u8> input, std::vector<T>& dst, std::size_t count,
std::size_t offset) {
if (dst.empty()) {
return 0;
@@ -63,7 +63,7 @@ nvhost_nvdec_common::~nvhost_nvdec_common() {
core.Host1xDeviceFile().syncpts_accumulated.push_back(channel_syncpoint);
}
-NvResult nvhost_nvdec_common::SetNVMAPfd(const std::vector<u8>& input) {
+NvResult nvhost_nvdec_common::SetNVMAPfd(std::span<const u8> input) {
IoctlSetNvmapFD params{};
std::memcpy(&params, input.data(), sizeof(IoctlSetNvmapFD));
LOG_DEBUG(Service_NVDRV, "called, fd={}", params.nvmap_fd);
@@ -72,7 +72,7 @@ NvResult nvhost_nvdec_common::SetNVMAPfd(const std::vector<u8>& input) {
return NvResult::Success;
}
-NvResult nvhost_nvdec_common::Submit(DeviceFD fd, const std::vector<u8>& input,
+NvResult nvhost_nvdec_common::Submit(DeviceFD fd, std::span<const u8> input,
std::vector<u8>& output) {
IoctlSubmit params{};
std::memcpy(&params, input.data(), sizeof(IoctlSubmit));
@@ -121,7 +121,7 @@ NvResult nvhost_nvdec_common::Submit(DeviceFD fd, const std::vector<u8>& input,
return NvResult::Success;
}
-NvResult nvhost_nvdec_common::GetSyncpoint(const std::vector<u8>& input, std::vector<u8>& output) {
+NvResult nvhost_nvdec_common::GetSyncpoint(std::span<const u8> input, std::vector<u8>& output) {
IoctlGetSyncpoint params{};
std::memcpy(&params, input.data(), sizeof(IoctlGetSyncpoint));
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
return NvResult::Success;
}
-NvResult nvhost_nvdec_common::GetWaitbase(const std::vector<u8>& input, std::vector<u8>& output) {
+NvResult nvhost_nvdec_common::GetWaitbase(std::span<const u8> input, std::vector<u8>& output) {
IoctlGetWaitbase params{};
LOG_CRITICAL(Service_NVDRV, "called WAITBASE");
std::memcpy(&params, input.data(), sizeof(IoctlGetWaitbase));
@@ -142,7 +142,7 @@ NvResult nvhost_nvdec_common::GetWaitbase(const std::vector<u8>& input, std::vec
return NvResult::Success;
}
-NvResult nvhost_nvdec_common::MapBuffer(const std::vector<u8>& input, std::vector<u8>& output) {
+NvResult nvhost_nvdec_common::MapBuffer(std::span<const u8> input, std::vector<u8>& output) {
IoctlMapBuffer params{};
std::memcpy(&params, input.data(), sizeof(IoctlMapBuffer));
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
return NvResult::Success;
}
-NvResult nvhost_nvdec_common::UnmapBuffer(const std::vector<u8>& input, std::vector<u8>& output) {
+NvResult nvhost_nvdec_common::UnmapBuffer(std::span<const u8> input, std::vector<u8>& output) {
IoctlMapBuffer params{};
std::memcpy(&params, input.data(), sizeof(IoctlMapBuffer));
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
return NvResult::Success;
}
-NvResult nvhost_nvdec_common::SetSubmitTimeout(const std::vector<u8>& input,
- std::vector<u8>& output) {
+NvResult nvhost_nvdec_common::SetSubmitTimeout(std::span<const u8> input, std::vector<u8>& output) {
std::memcpy(&submit_timeout, input.data(), input.size());
LOG_WARNING(Service_NVDRV, "(STUBBED) called");
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:
static_assert(sizeof(IoctlMapBuffer) == 0x0C, "IoctlMapBuffer is incorrect size");
/// Ioctl command implementations
- NvResult SetNVMAPfd(const std::vector<u8>& input);
- NvResult Submit(DeviceFD fd, const std::vector<u8>& input, std::vector<u8>& output);
- NvResult GetSyncpoint(const std::vector<u8>& input, std::vector<u8>& output);
- NvResult GetWaitbase(const std::vector<u8>& input, std::vector<u8>& output);
- NvResult MapBuffer(const std::vector<u8>& input, std::vector<u8>& output);
- NvResult UnmapBuffer(const std::vector<u8>& input, std::vector<u8>& output);
- NvResult SetSubmitTimeout(const std::vector<u8>& input, std::vector<u8>& output);
+ NvResult SetNVMAPfd(std::span<const u8> input);
+ NvResult Submit(DeviceFD fd, std::span<const u8> input, std::vector<u8>& output);
+ NvResult GetSyncpoint(std::span<const u8> input, std::vector<u8>& output);
+ NvResult GetWaitbase(std::span<const u8> input, std::vector<u8>& output);
+ NvResult MapBuffer(std::span<const u8> input, std::vector<u8>& output);
+ NvResult UnmapBuffer(std::span<const u8> input, std::vector<u8>& output);
+ NvResult SetSubmitTimeout(std::span<const u8> input, std::vector<u8>& output);
Kernel::KEvent* QueryEvent(u32 event_id) override;
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 {
nvhost_nvjpg::nvhost_nvjpg(Core::System& system_) : nvdevice{system_} {}
nvhost_nvjpg::~nvhost_nvjpg() = default;
-NvResult nvhost_nvjpg::Ioctl1(DeviceFD fd, Ioctl command, const std::vector<u8>& input,
+NvResult nvhost_nvjpg::Ioctl1(DeviceFD fd, Ioctl command, std::span<const u8> input,
std::vector<u8>& output) {
switch (command.group) {
case 'H':
@@ -31,13 +31,13 @@ NvResult nvhost_nvjpg::Ioctl1(DeviceFD fd, Ioctl command, const std::vector<u8>&
return NvResult::NotImplemented;
}
-NvResult nvhost_nvjpg::Ioctl2(DeviceFD fd, Ioctl command, const std::vector<u8>& input,
- const std::vector<u8>& inline_input, std::vector<u8>& output) {
+NvResult nvhost_nvjpg::Ioctl2(DeviceFD fd, Ioctl command, std::span<const u8> input,
+ std::span<const u8> inline_input, std::vector<u8>& output) {
UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);
return NvResult::NotImplemented;
}
-NvResult nvhost_nvjpg::Ioctl3(DeviceFD fd, Ioctl command, const std::vector<u8>& input,
+NvResult nvhost_nvjpg::Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> input,
std::vector<u8>& output, std::vector<u8>& inline_output) {
UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);
return NvResult::NotImplemented;
@@ -46,7 +46,7 @@ NvResult nvhost_nvjpg::Ioctl3(DeviceFD fd, Ioctl command, const std::vector<u8>&
void nvhost_nvjpg::OnOpen(DeviceFD fd) {}
void nvhost_nvjpg::OnClose(DeviceFD fd) {}
-NvResult nvhost_nvjpg::SetNVMAPfd(const std::vector<u8>& input, std::vector<u8>& output) {
+NvResult nvhost_nvjpg::SetNVMAPfd(std::span<const u8> input, std::vector<u8>& output) {
IoctlSetNvmapFD params{};
std::memcpy(&params, input.data(), input.size());
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:
explicit nvhost_nvjpg(Core::System& system_);
~nvhost_nvjpg() override;
- NvResult Ioctl1(DeviceFD fd, Ioctl command, const std::vector<u8>& input,
+ NvResult Ioctl1(DeviceFD fd, Ioctl command, std::span<const u8> input,
std::vector<u8>& output) override;
- NvResult Ioctl2(DeviceFD fd, Ioctl command, const std::vector<u8>& input,
- const std::vector<u8>& inline_input, std::vector<u8>& output) override;
- NvResult Ioctl3(DeviceFD fd, Ioctl command, const std::vector<u8>& input,
- std::vector<u8>& output, std::vector<u8>& inline_output) override;
+ NvResult Ioctl2(DeviceFD fd, Ioctl command, std::span<const u8> input,
+ std::span<const u8> inline_input, std::vector<u8>& output) override;
+ NvResult Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> input, std::vector<u8>& output,
+ std::vector<u8>& inline_output) override;
void OnOpen(DeviceFD fd) override;
void OnClose(DeviceFD fd) override;
@@ -33,7 +33,7 @@ private:
s32_le nvmap_fd{};
- NvResult SetNVMAPfd(const std::vector<u8>& input, std::vector<u8>& output);
+ NvResult SetNVMAPfd(std::span<const u8> input, std::vector<u8>& output);
};
} // 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_)
nvhost_vic::~nvhost_vic() = default;
-NvResult nvhost_vic::Ioctl1(DeviceFD fd, Ioctl command, const std::vector<u8>& input,
+NvResult nvhost_vic::Ioctl1(DeviceFD fd, Ioctl command, std::span<const u8> input,
std::vector<u8>& output) {
switch (command.group) {
case 0x0:
@@ -55,13 +55,13 @@ NvResult nvhost_vic::Ioctl1(DeviceFD fd, Ioctl command, const std::vector<u8>& i
return NvResult::NotImplemented;
}
-NvResult nvhost_vic::Ioctl2(DeviceFD fd, Ioctl command, const std::vector<u8>& input,
- const std::vector<u8>& inline_input, std::vector<u8>& output) {
+NvResult nvhost_vic::Ioctl2(DeviceFD fd, Ioctl command, std::span<const u8> input,
+ std::span<const u8> inline_input, std::vector<u8>& output) {
UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);
return NvResult::NotImplemented;
}
-NvResult nvhost_vic::Ioctl3(DeviceFD fd, Ioctl command, const std::vector<u8>& input,
+NvResult nvhost_vic::Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> input,
std::vector<u8>& output, std::vector<u8>& inline_output) {
UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);
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:
explicit nvhost_vic(Core::System& system_, NvCore::Container& core);
~nvhost_vic();
- NvResult Ioctl1(DeviceFD fd, Ioctl command, const std::vector<u8>& input,
+ NvResult Ioctl1(DeviceFD fd, Ioctl command, std::span<const u8> input,
std::vector<u8>& output) override;
- NvResult Ioctl2(DeviceFD fd, Ioctl command, const std::vector<u8>& input,
- const std::vector<u8>& inline_input, std::vector<u8>& output) override;
- NvResult Ioctl3(DeviceFD fd, Ioctl command, const std::vector<u8>& input,
- std::vector<u8>& output, std::vector<u8>& inline_output) override;
+ NvResult Ioctl2(DeviceFD fd, Ioctl command, std::span<const u8> input,
+ std::span<const u8> inline_input, std::vector<u8>& output) override;
+ NvResult Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> input, std::vector<u8>& output,
+ std::vector<u8>& inline_output) override;
void OnOpen(DeviceFD fd) override;
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..277afe0b4 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_)
nvmap::~nvmap() = default;
-NvResult nvmap::Ioctl1(DeviceFD fd, Ioctl command, const std::vector<u8>& input,
+NvResult nvmap::Ioctl1(DeviceFD fd, Ioctl command, std::span<const u8> input,
std::vector<u8>& output) {
switch (command.group) {
case 0x1:
@@ -54,13 +54,13 @@ NvResult nvmap::Ioctl1(DeviceFD fd, Ioctl command, const std::vector<u8>& input,
return NvResult::NotImplemented;
}
-NvResult nvmap::Ioctl2(DeviceFD fd, Ioctl command, const std::vector<u8>& input,
- const std::vector<u8>& inline_input, std::vector<u8>& output) {
+NvResult nvmap::Ioctl2(DeviceFD fd, Ioctl command, std::span<const u8> input,
+ std::span<const u8> inline_input, std::vector<u8>& output) {
UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);
return NvResult::NotImplemented;
}
-NvResult nvmap::Ioctl3(DeviceFD fd, Ioctl command, const std::vector<u8>& input,
+NvResult nvmap::Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> input,
std::vector<u8>& output, std::vector<u8>& inline_output) {
UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);
return NvResult::NotImplemented;
@@ -69,7 +69,7 @@ NvResult nvmap::Ioctl3(DeviceFD fd, Ioctl command, const std::vector<u8>& input,
void nvmap::OnOpen(DeviceFD fd) {}
void nvmap::OnClose(DeviceFD fd) {}
-NvResult nvmap::IocCreate(const std::vector<u8>& input, std::vector<u8>& output) {
+NvResult nvmap::IocCreate(std::span<const u8> input, std::vector<u8>& output) {
IocCreateParams params;
std::memcpy(&params, input.data(), sizeof(params));
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)
return NvResult::Success;
}
-NvResult nvmap::IocAlloc(const std::vector<u8>& input, std::vector<u8>& output) {
+NvResult nvmap::IocAlloc(std::span<const u8> input, std::vector<u8>& output) {
IocAllocParams params;
std::memcpy(&params, input.data(), sizeof(params));
LOG_DEBUG(Service_NVDRV, "called, addr={:X}", params.address);
@@ -127,7 +127,7 @@ NvResult nvmap::IocAlloc(const std::vector<u8>& input, std::vector<u8>& output)
return result;
}
bool is_out_io{};
- ASSERT(system.CurrentProcess()
+ ASSERT(system.ApplicationProcess()
->PageTable()
.LockForMapDeviceAddressSpace(&is_out_io, handle_description->address,
handle_description->size,
@@ -137,7 +137,7 @@ NvResult nvmap::IocAlloc(const std::vector<u8>& input, std::vector<u8>& output)
return result;
}
-NvResult nvmap::IocGetId(const std::vector<u8>& input, std::vector<u8>& output) {
+NvResult nvmap::IocGetId(std::span<const u8> input, std::vector<u8>& output) {
IocGetIdParams params;
std::memcpy(&params, input.data(), sizeof(params));
@@ -161,7 +161,7 @@ NvResult nvmap::IocGetId(const std::vector<u8>& input, std::vector<u8>& output)
return NvResult::Success;
}
-NvResult nvmap::IocFromId(const std::vector<u8>& input, std::vector<u8>& output) {
+NvResult nvmap::IocFromId(std::span<const u8> input, std::vector<u8>& output) {
IocFromIdParams params;
std::memcpy(&params, input.data(), sizeof(params));
@@ -192,7 +192,7 @@ NvResult nvmap::IocFromId(const std::vector<u8>& input, std::vector<u8>& output)
return NvResult::Success;
}
-NvResult nvmap::IocParam(const std::vector<u8>& input, std::vector<u8>& output) {
+NvResult nvmap::IocParam(std::span<const u8> input, std::vector<u8>& output) {
enum class ParamTypes { Size = 1, Alignment = 2, Base = 3, Heap = 4, Kind = 5, Compr = 6 };
IocParamParams params;
@@ -241,7 +241,7 @@ NvResult nvmap::IocParam(const std::vector<u8>& input, std::vector<u8>& output)
return NvResult::Success;
}
-NvResult nvmap::IocFree(const std::vector<u8>& input, std::vector<u8>& output) {
+NvResult nvmap::IocFree(std::span<const u8> input, std::vector<u8>& output) {
IocFreeParams params;
std::memcpy(&params, input.data(), sizeof(params));
@@ -254,7 +254,7 @@ NvResult nvmap::IocFree(const std::vector<u8>& input, std::vector<u8>& output) {
if (auto freeInfo{file.FreeHandle(params.handle, false)}) {
if (freeInfo->can_unlock) {
- ASSERT(system.CurrentProcess()
+ ASSERT(system.ApplicationProcess()
->PageTable()
.UnlockForDeviceAddressSpace(freeInfo->address, freeInfo->size)
.IsSuccess());
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:
nvmap(const nvmap&) = delete;
nvmap& operator=(const nvmap&) = delete;
- NvResult Ioctl1(DeviceFD fd, Ioctl command, const std::vector<u8>& input,
+ NvResult Ioctl1(DeviceFD fd, Ioctl command, std::span<const u8> input,
std::vector<u8>& output) override;
- NvResult Ioctl2(DeviceFD fd, Ioctl command, const std::vector<u8>& input,
- const std::vector<u8>& inline_input, std::vector<u8>& output) override;
- NvResult Ioctl3(DeviceFD fd, Ioctl command, const std::vector<u8>& input,
- std::vector<u8>& output, std::vector<u8>& inline_output) override;
+ NvResult Ioctl2(DeviceFD fd, Ioctl command, std::span<const u8> input,
+ std::span<const u8> inline_input, std::vector<u8>& output) override;
+ NvResult Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> input, std::vector<u8>& output,
+ std::vector<u8>& inline_output) override;
void OnOpen(DeviceFD fd) override;
void OnClose(DeviceFD fd) override;
@@ -106,12 +106,12 @@ private:
};
static_assert(sizeof(IocGetIdParams) == 8, "IocGetIdParams has wrong size");
- NvResult IocCreate(const std::vector<u8>& input, std::vector<u8>& output);
- NvResult IocAlloc(const std::vector<u8>& input, std::vector<u8>& output);
- NvResult IocGetId(const std::vector<u8>& input, std::vector<u8>& output);
- NvResult IocFromId(const std::vector<u8>& input, std::vector<u8>& output);
- NvResult IocParam(const std::vector<u8>& input, std::vector<u8>& output);
- NvResult IocFree(const std::vector<u8>& input, std::vector<u8>& output);
+ NvResult IocCreate(std::span<const u8> input, std::vector<u8>& output);
+ NvResult IocAlloc(std::span<const u8> input, std::vector<u8>& output);
+ NvResult IocGetId(std::span<const u8> input, std::vector<u8>& output);
+ NvResult IocFromId(std::span<const u8> input, std::vector<u8>& output);
+ NvResult IocParam(std::span<const u8> input, std::vector<u8>& output);
+ NvResult IocFree(std::span<const u8> input, std::vector<u8>& output);
NvCore::Container& container;
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) {
return fd;
}
-NvResult Module::Ioctl1(DeviceFD fd, Ioctl command, const std::vector<u8>& input,
+NvResult Module::Ioctl1(DeviceFD fd, Ioctl command, std::span<const u8> input,
std::vector<u8>& output) {
if (fd < 0) {
LOG_ERROR(Service_NVDRV, "Invalid DeviceFD={}!", fd);
@@ -141,8 +141,8 @@ NvResult Module::Ioctl1(DeviceFD fd, Ioctl command, const std::vector<u8>& input
return itr->second->Ioctl1(fd, command, input, output);
}
-NvResult Module::Ioctl2(DeviceFD fd, Ioctl command, const std::vector<u8>& input,
- const std::vector<u8>& inline_input, std::vector<u8>& output) {
+NvResult Module::Ioctl2(DeviceFD fd, Ioctl command, std::span<const u8> input,
+ std::span<const u8> inline_input, std::vector<u8>& output) {
if (fd < 0) {
LOG_ERROR(Service_NVDRV, "Invalid DeviceFD={}!", fd);
return NvResult::InvalidState;
@@ -158,7 +158,7 @@ NvResult Module::Ioctl2(DeviceFD fd, Ioctl command, const std::vector<u8>& input
return itr->second->Ioctl2(fd, command, input, inline_input, output);
}
-NvResult Module::Ioctl3(DeviceFD fd, Ioctl command, const std::vector<u8>& input,
+NvResult Module::Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> input,
std::vector<u8>& output, std::vector<u8>& inline_output) {
if (fd < 0) {
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 @@
#include <functional>
#include <list>
#include <memory>
+#include <span>
#include <string>
#include <unordered_map>
#include <vector>
@@ -79,14 +80,13 @@ public:
DeviceFD Open(const std::string& device_name);
/// Sends an ioctl command to the specified file descriptor.
- NvResult Ioctl1(DeviceFD fd, Ioctl command, const std::vector<u8>& input,
- std::vector<u8>& output);
+ NvResult Ioctl1(DeviceFD fd, Ioctl command, std::span<const u8> input, std::vector<u8>& output);
- NvResult Ioctl2(DeviceFD fd, Ioctl command, const std::vector<u8>& input,
- const std::vector<u8>& inline_input, std::vector<u8>& output);
+ NvResult Ioctl2(DeviceFD fd, Ioctl command, std::span<const u8> input,
+ std::span<const u8> inline_input, std::vector<u8>& output);
- NvResult Ioctl3(DeviceFD fd, Ioctl command, const std::vector<u8>& input,
- std::vector<u8>& output, std::vector<u8>& inline_output);
+ NvResult Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> input, std::vector<u8>& output,
+ std::vector<u8>& inline_output);
/// Closes a device file descriptor and returns operation success.
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,
void BufferQueueProducer::Transact(Kernel::HLERequestContext& ctx, TransactionId code, u32 flags) {
Status status{Status::NoError};
- Parcel parcel_in{ctx.ReadBuffer()};
- Parcel parcel_out{};
+ InputParcel parcel_in{ctx.ReadBuffer()};
+ OutputParcel parcel_out{};
switch (code) {
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 @@
namespace Service::android {
-QueueBufferInput::QueueBufferInput(Parcel& parcel) {
+QueueBufferInput::QueueBufferInput(InputParcel& parcel) {
parcel.ReadFlattened(*this);
}
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 @@
namespace Service::android {
-class Parcel;
+class InputParcel;
#pragma pack(push, 1)
struct QueueBufferInput final {
- explicit QueueBufferInput(Parcel& parcel);
+ explicit QueueBufferInput(InputParcel& parcel);
void Deflate(s64* timestamp_, bool* is_auto_timestamp_, Common::Rectangle<s32>* crop_,
NativeWindowScalingMode* scaling_mode_, NativeWindowTransform* transform_,
diff --git a/src/core/hle/service/nvflinger/nvflinger.cpp b/src/core/hle/service/nvflinger/nvflinger.cpp
index d1cbadde4..f4416f5b2 100644
--- a/src/core/hle/service/nvflinger/nvflinger.cpp
+++ b/src/core/hle/service/nvflinger/nvflinger.cpp
@@ -312,8 +312,6 @@ void NVFlinger::Compose() {
}
s64 NVFlinger::GetNextTicks() const {
- static constexpr s64 max_hertz = 120LL;
-
const auto& settings = Settings::values;
auto speed_scale = 1.f;
if (settings.use_multi_core.GetValue()) {
@@ -327,9 +325,11 @@ s64 NVFlinger::GetNextTicks() const {
}
}
- const auto next_ticks = ((1000000000 * (1LL << swap_interval)) / max_hertz);
+ // As an extension, treat nonpositive swap interval as framerate multiplier.
+ const f32 effective_fps = swap_interval <= 0 ? 120.f * static_cast<f32>(1 - swap_interval)
+ : 60.f / static_cast<f32>(swap_interval);
- return static_cast<s64>(speed_scale * static_cast<float>(next_ticks));
+ return static_cast<s64>(speed_scale * (1000000000.f / effective_fps));
}
} // namespace Service::NVFlinger
diff --git a/src/core/hle/service/nvflinger/nvflinger.h b/src/core/hle/service/nvflinger/nvflinger.h
index 9b22397db..3828cf272 100644
--- a/src/core/hle/service/nvflinger/nvflinger.h
+++ b/src/core/hle/service/nvflinger/nvflinger.h
@@ -133,7 +133,7 @@ private:
/// layers.
u32 next_buffer_queue_id = 1;
- u32 swap_interval = 1;
+ s32 swap_interval = 1;
/// Event that handles screen composition.
std::shared_ptr<Core::Timing::EventType> multi_composition_event;
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 @@
#pragma once
#include <memory>
+#include <span>
#include <vector>
#include "common/alignment.h"
@@ -12,18 +13,17 @@
namespace Service::android {
-class Parcel final {
-public:
- static constexpr std::size_t DefaultBufferSize = 0x40;
-
- Parcel() : buffer(DefaultBufferSize) {}
-
- template <typename T>
- explicit Parcel(const T& out_data) : buffer(DefaultBufferSize) {
- Write(out_data);
- }
+struct ParcelHeader {
+ u32 data_size;
+ u32 data_offset;
+ u32 objects_size;
+ u32 objects_offset;
+};
+static_assert(sizeof(ParcelHeader) == 16, "ParcelHeader has wrong size");
- explicit Parcel(std::vector<u8> in_data) : buffer(std::move(in_data)) {
+class InputParcel final {
+public:
+ explicit InputParcel(std::span<const u8> in_data) : read_buffer(std::move(in_data)) {
DeserializeHeader();
[[maybe_unused]] const std::u16string token = ReadInterfaceToken();
}
@@ -31,9 +31,9 @@ public:
template <typename T>
void Read(T& val) {
static_assert(std::is_trivially_copyable_v<T>, "T must be trivially copyable.");
- ASSERT(read_index + sizeof(T) <= buffer.size());
+ ASSERT(read_index + sizeof(T) <= read_buffer.size());
- std::memcpy(&val, buffer.data() + read_index, sizeof(T));
+ std::memcpy(&val, read_buffer.data() + read_index, sizeof(T));
read_index += sizeof(T);
read_index = Common::AlignUp(read_index, 4);
}
@@ -62,10 +62,10 @@ public:
template <typename T>
T ReadUnaligned() {
static_assert(std::is_trivially_copyable_v<T>, "T must be trivially copyable.");
- ASSERT(read_index + sizeof(T) <= buffer.size());
+ ASSERT(read_index + sizeof(T) <= read_buffer.size());
T val;
- std::memcpy(&val, buffer.data() + read_index, sizeof(T));
+ std::memcpy(&val, read_buffer.data() + read_index, sizeof(T));
read_index += sizeof(T);
return val;
}
@@ -101,6 +101,31 @@ public:
return token;
}
+ void DeserializeHeader() {
+ ASSERT(read_buffer.size() > sizeof(ParcelHeader));
+
+ ParcelHeader header{};
+ std::memcpy(&header, read_buffer.data(), sizeof(ParcelHeader));
+
+ read_index = header.data_offset;
+ }
+
+private:
+ std::span<const u8> read_buffer;
+ std::size_t read_index = 0;
+};
+
+class OutputParcel final {
+public:
+ static constexpr std::size_t DefaultBufferSize = 0x40;
+
+ OutputParcel() : buffer(DefaultBufferSize) {}
+
+ template <typename T>
+ explicit OutputParcel(const T& out_data) : buffer(DefaultBufferSize) {
+ Write(out_data);
+ }
+
template <typename T>
void Write(const T& val) {
static_assert(std::is_trivially_copyable_v<T>, "T must be trivially copyable.");
@@ -133,40 +158,20 @@ public:
WriteObject(ptr.get());
}
- void DeserializeHeader() {
- ASSERT(buffer.size() > sizeof(Header));
-
- Header header{};
- std::memcpy(&header, buffer.data(), sizeof(Header));
-
- read_index = header.data_offset;
- }
-
std::vector<u8> Serialize() const {
- ASSERT(read_index == 0);
-
- Header header{};
- header.data_size = static_cast<u32>(write_index - sizeof(Header));
- header.data_offset = sizeof(Header);
+ ParcelHeader header{};
+ header.data_size = static_cast<u32>(write_index - sizeof(ParcelHeader));
+ header.data_offset = sizeof(ParcelHeader);
header.objects_size = 4;
- header.objects_offset = static_cast<u32>(sizeof(Header) + header.data_size);
- std::memcpy(buffer.data(), &header, sizeof(Header));
+ header.objects_offset = static_cast<u32>(sizeof(ParcelHeader) + header.data_size);
+ std::memcpy(buffer.data(), &header, sizeof(ParcelHeader));
return buffer;
}
private:
- struct Header {
- u32 data_size;
- u32 data_offset;
- u32 objects_size;
- u32 objects_offset;
- };
- static_assert(sizeof(Header) == 16, "ParcelHeader has wrong size");
-
mutable std::vector<u8> buffer;
- std::size_t read_index = 0;
- std::size_t write_index = sizeof(Header);
+ std::size_t write_index = sizeof(ParcelHeader);
};
} // namespace Service::android
diff --git a/src/core/hle/service/pctl/pctl_module.cpp b/src/core/hle/service/pctl/pctl_module.cpp
index 2a123b42d..083609b34 100644
--- a/src/core/hle/service/pctl/pctl_module.cpp
+++ b/src/core/hle/service/pctl/pctl_module.cpp
@@ -187,7 +187,7 @@ private:
// TODO(ogniK): Recovery flag initialization for pctl:r
- const auto tid = system.GetCurrentProcessProgramID();
+ const auto tid = system.GetApplicationProcessProgramID();
if (tid != 0) {
const FileSys::PatchManager pm{tid, system.GetFileSystemController(),
system.GetContentProvider()};
diff --git a/src/core/hle/service/pcv/pcv.cpp b/src/core/hle/service/pcv/pcv.cpp
index f7a497a14..98037a8d4 100644
--- a/src/core/hle/service/pcv/pcv.cpp
+++ b/src/core/hle/service/pcv/pcv.cpp
@@ -52,32 +52,6 @@ public:
}
};
-class PCV_ARB final : public ServiceFramework<PCV_ARB> {
-public:
- explicit PCV_ARB(Core::System& system_) : ServiceFramework{system_, "pcv:arb"} {
- // clang-format off
- static const FunctionInfo functions[] = {
- {0, nullptr, "ReleaseControl"},
- };
- // clang-format on
-
- RegisterHandlers(functions);
- }
-};
-
-class PCV_IMM final : public ServiceFramework<PCV_IMM> {
-public:
- explicit PCV_IMM(Core::System& system_) : ServiceFramework{system_, "pcv:imm"} {
- // clang-format off
- static const FunctionInfo functions[] = {
- {0, nullptr, "SetClockRate"},
- };
- // clang-format on
-
- RegisterHandlers(functions);
- }
-};
-
class IClkrstSession final : public ServiceFramework<IClkrstSession> {
public:
explicit IClkrstSession(Core::System& system_, DeviceCode deivce_code_)
@@ -169,8 +143,6 @@ public:
void InstallInterfaces(SM::ServiceManager& sm, Core::System& system) {
std::make_shared<PCV>(system)->InstallAsService(sm);
- std::make_shared<PCV_ARB>(system)->InstallAsService(sm);
- std::make_shared<PCV_IMM>(system)->InstallAsService(sm);
std::make_shared<CLKRST>(system, "clkrst")->InstallAsService(sm);
std::make_shared<CLKRST>(system, "clkrst:i")->InstallAsService(sm);
std::make_shared<CLKRST_A>(system)->InstallAsService(sm);
diff --git a/src/core/hle/service/prepo/prepo.cpp b/src/core/hle/service/prepo/prepo.cpp
index 78f897d3e..90c5f8756 100644
--- a/src/core/hle/service/prepo/prepo.cpp
+++ b/src/core/hle/service/prepo/prepo.cpp
@@ -63,7 +63,7 @@ private:
return ctx.ReadBuffer(1);
}
- return std::vector<u8>{};
+ return std::span<const u8>{};
}();
LOG_DEBUG(Service_PREPO,
@@ -71,7 +71,7 @@ private:
Type, process_id, data1.size(), data2.size());
const auto& reporter{system.GetReporter()};
- reporter.SavePlayReport(Type, system.GetCurrentProcessProgramID(), {data1, data2},
+ reporter.SavePlayReport(Type, system.GetApplicationProcessProgramID(), {data1, data2},
process_id);
IPC::ResponseBuilder rb{ctx, 2};
@@ -90,7 +90,7 @@ private:
return ctx.ReadBuffer(1);
}
- return std::vector<u8>{};
+ return std::span<const u8>{};
}();
LOG_DEBUG(Service_PREPO,
@@ -99,7 +99,7 @@ private:
Type, user_id[1], user_id[0], process_id, data1.size(), data2.size());
const auto& reporter{system.GetReporter()};
- reporter.SavePlayReport(Type, system.GetCurrentProcessProgramID(), {data1, data2},
+ reporter.SavePlayReport(Type, system.GetApplicationProcessProgramID(), {data1, data2},
process_id, user_id);
IPC::ResponseBuilder rb{ctx, 2};
@@ -142,7 +142,7 @@ private:
return ctx.ReadBuffer(1);
}
- return std::vector<u8>{};
+ return std::span<const u8>{};
}();
LOG_DEBUG(Service_PREPO, "called, title_id={:016X}, data1_size={:016X}, data2_size={:016X}",
@@ -166,7 +166,7 @@ private:
return ctx.ReadBuffer(1);
}
- return std::vector<u8>{};
+ return std::span<const u8>{};
}();
LOG_DEBUG(Service_PREPO,
diff --git a/src/core/hle/service/service.cpp b/src/core/hle/service/service.cpp
index 0de67f1e1..1ffc1c694 100644
--- a/src/core/hle/service/service.cpp
+++ b/src/core/hle/service/service.cpp
@@ -68,7 +68,6 @@
#include "core/hle/service/time/time.h"
#include "core/hle/service/usb/usb.h"
#include "core/hle/service/vi/vi.h"
-#include "core/hle/service/wlan/wlan.h"
#include "core/reporter.h"
namespace Service {
@@ -306,7 +305,6 @@ Services::Services(std::shared_ptr<SM::ServiceManager>& sm, Core::System& system
Time::InstallInterfaces(system);
USB::InstallInterfaces(*sm, system);
VI::InstallInterfaces(*sm, system, *nv_flinger, *hos_binder_driver_server);
- WLAN::InstallInterfaces(*sm, system);
}
Services::~Services() = default;
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) {
const s32 fd = rp.Pop<s32>();
LOG_DEBUG(Service, "called. fd={} addrlen={}", fd, ctx.GetReadBufferSize());
-
BuildErrnoResponse(ctx, BindImpl(fd, ctx.ReadBuffer()));
}
@@ -312,7 +311,7 @@ void BSD::SetSockOpt(Kernel::HLERequestContext& ctx) {
const u32 level = rp.Pop<u32>();
const OptName optname = static_cast<OptName>(rp.Pop<u32>());
- const std::vector<u8> buffer = ctx.ReadBuffer();
+ const auto buffer = ctx.ReadBuffer();
const u8* optval = buffer.empty() ? nullptr : buffer.data();
size_t optlen = buffer.size();
@@ -489,7 +488,7 @@ std::pair<s32, Errno> BSD::SocketImpl(Domain domain, Type type, Protocol protoco
return {fd, Errno::SUCCESS};
}
-std::pair<s32, Errno> BSD::PollImpl(std::vector<u8>& write_buffer, std::vector<u8> read_buffer,
+std::pair<s32, Errno> BSD::PollImpl(std::vector<u8>& write_buffer, std::span<const u8> read_buffer,
s32 nfds, s32 timeout) {
if (write_buffer.size() < nfds * sizeof(PollFD)) {
return {-1, Errno::INVAL};
@@ -584,7 +583,7 @@ std::pair<s32, Errno> BSD::AcceptImpl(s32 fd, std::vector<u8>& write_buffer) {
return {new_fd, Errno::SUCCESS};
}
-Errno BSD::BindImpl(s32 fd, const std::vector<u8>& addr) {
+Errno BSD::BindImpl(s32 fd, std::span<const u8> addr) {
if (!IsFileDescriptorValid(fd)) {
return Errno::BADF;
}
@@ -595,7 +594,7 @@ Errno BSD::BindImpl(s32 fd, const std::vector<u8>& addr) {
return Translate(file_descriptors[fd]->socket->Bind(Translate(addr_in)));
}
-Errno BSD::ConnectImpl(s32 fd, const std::vector<u8>& addr) {
+Errno BSD::ConnectImpl(s32 fd, std::span<const u8> addr) {
if (!IsFileDescriptorValid(fd)) {
return Errno::BADF;
}
@@ -800,15 +799,15 @@ std::pair<s32, Errno> BSD::RecvFromImpl(s32 fd, u32 flags, std::vector<u8>& mess
return {ret, bsd_errno};
}
-std::pair<s32, Errno> BSD::SendImpl(s32 fd, u32 flags, const std::vector<u8>& message) {
+std::pair<s32, Errno> BSD::SendImpl(s32 fd, u32 flags, std::span<const u8> message) {
if (!IsFileDescriptorValid(fd)) {
return {-1, Errno::BADF};
}
return Translate(file_descriptors[fd]->socket->Send(message, flags));
}
-std::pair<s32, Errno> BSD::SendToImpl(s32 fd, u32 flags, const std::vector<u8>& message,
- const std::vector<u8>& addr) {
+std::pair<s32, Errno> BSD::SendToImpl(s32 fd, u32 flags, std::span<const u8> message,
+ std::span<const u8> addr) {
if (!IsFileDescriptorValid(fd)) {
return {-1, Errno::BADF};
}
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 @@
#pragma once
#include <memory>
+#include <span>
#include <vector>
#include "common/common_types.h"
@@ -44,7 +45,7 @@ private:
s32 nfds;
s32 timeout;
- std::vector<u8> read_buffer;
+ std::span<const u8> read_buffer;
std::vector<u8> write_buffer;
s32 ret{};
Errno bsd_errno{};
@@ -65,7 +66,7 @@ private:
void Response(Kernel::HLERequestContext& ctx);
s32 fd;
- std::vector<u8> addr;
+ std::span<const u8> addr;
Errno bsd_errno{};
};
@@ -98,7 +99,7 @@ private:
s32 fd;
u32 flags;
- std::vector<u8> message;
+ std::span<const u8> message;
s32 ret{};
Errno bsd_errno{};
};
@@ -109,8 +110,8 @@ private:
s32 fd;
u32 flags;
- std::vector<u8> message;
- std::vector<u8> addr;
+ std::span<const u8> message;
+ std::span<const u8> addr;
s32 ret{};
Errno bsd_errno{};
};
@@ -143,11 +144,11 @@ private:
void ExecuteWork(Kernel::HLERequestContext& ctx, Work work);
std::pair<s32, Errno> SocketImpl(Domain domain, Type type, Protocol protocol);
- std::pair<s32, Errno> PollImpl(std::vector<u8>& write_buffer, std::vector<u8> read_buffer,
+ std::pair<s32, Errno> PollImpl(std::vector<u8>& write_buffer, std::span<const u8> read_buffer,
s32 nfds, s32 timeout);
std::pair<s32, Errno> AcceptImpl(s32 fd, std::vector<u8>& write_buffer);
- Errno BindImpl(s32 fd, const std::vector<u8>& addr);
- Errno ConnectImpl(s32 fd, const std::vector<u8>& addr);
+ Errno BindImpl(s32 fd, std::span<const u8> addr);
+ Errno ConnectImpl(s32 fd, std::span<const u8> addr);
Errno GetPeerNameImpl(s32 fd, std::vector<u8>& write_buffer);
Errno GetSockNameImpl(s32 fd, std::vector<u8>& write_buffer);
Errno ListenImpl(s32 fd, s32 backlog);
@@ -157,9 +158,9 @@ private:
std::pair<s32, Errno> RecvImpl(s32 fd, u32 flags, std::vector<u8>& message);
std::pair<s32, Errno> RecvFromImpl(s32 fd, u32 flags, std::vector<u8>& message,
std::vector<u8>& addr);
- std::pair<s32, Errno> SendImpl(s32 fd, u32 flags, const std::vector<u8>& message);
- std::pair<s32, Errno> SendToImpl(s32 fd, u32 flags, const std::vector<u8>& message,
- const std::vector<u8>& addr);
+ std::pair<s32, Errno> SendImpl(s32 fd, u32 flags, std::span<const u8> message);
+ std::pair<s32, Errno> SendToImpl(s32 fd, u32 flags, std::span<const u8> message,
+ std::span<const u8> addr);
Errno CloseImpl(s32 fd);
s32 FindFreeFileDescriptorHandle() noexcept;
diff --git a/src/core/hle/service/sockets/ethc.cpp b/src/core/hle/service/sockets/ethc.cpp
deleted file mode 100644
index c12ea999b..000000000
--- a/src/core/hle/service/sockets/ethc.cpp
+++ /dev/null
@@ -1,42 +0,0 @@
-// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
-// SPDX-License-Identifier: GPL-2.0-or-later
-
-#include "core/hle/service/sockets/ethc.h"
-
-namespace Service::Sockets {
-
-ETHC_C::ETHC_C(Core::System& system_) : ServiceFramework{system_, "ethc:c"} {
- // clang-format off
- static const FunctionInfo functions[] = {
- {0, nullptr, "Initialize"},
- {1, nullptr, "Cancel"},
- {2, nullptr, "GetResult"},
- {3, nullptr, "GetMediaList"},
- {4, nullptr, "SetMediaType"},
- {5, nullptr, "GetMediaType"},
- {6, nullptr, "Unknown6"},
- };
- // clang-format on
-
- RegisterHandlers(functions);
-}
-
-ETHC_C::~ETHC_C() = default;
-
-ETHC_I::ETHC_I(Core::System& system_) : ServiceFramework{system_, "ethc:i"} {
- // clang-format off
- static const FunctionInfo functions[] = {
- {0, nullptr, "GetReadableHandle"},
- {1, nullptr, "Cancel"},
- {2, nullptr, "GetResult"},
- {3, nullptr, "GetInterfaceList"},
- {4, nullptr, "GetInterfaceCount"},
- };
- // clang-format on
-
- RegisterHandlers(functions);
-}
-
-ETHC_I::~ETHC_I() = default;
-
-} // namespace Service::Sockets
diff --git a/src/core/hle/service/sockets/ethc.h b/src/core/hle/service/sockets/ethc.h
deleted file mode 100644
index 7c5759a96..000000000
--- a/src/core/hle/service/sockets/ethc.h
+++ /dev/null
@@ -1,26 +0,0 @@
-// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
-// SPDX-License-Identifier: GPL-2.0-or-later
-
-#pragma once
-
-#include "core/hle/service/service.h"
-
-namespace Core {
-class System;
-}
-
-namespace Service::Sockets {
-
-class ETHC_C final : public ServiceFramework<ETHC_C> {
-public:
- explicit ETHC_C(Core::System& system_);
- ~ETHC_C() override;
-};
-
-class ETHC_I final : public ServiceFramework<ETHC_I> {
-public:
- explicit ETHC_I(Core::System& system_);
- ~ETHC_I() override;
-};
-
-} // namespace Service::Sockets
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) {
rb.Push(0);
}
-} // namespace Service::Sockets \ No newline at end of file
+} // namespace Service::Sockets
diff --git a/src/core/hle/service/sockets/sockets.cpp b/src/core/hle/service/sockets/sockets.cpp
index 8d3ba6f96..b191b5cf5 100644
--- a/src/core/hle/service/sockets/sockets.cpp
+++ b/src/core/hle/service/sockets/sockets.cpp
@@ -2,7 +2,6 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#include "core/hle/service/sockets/bsd.h"
-#include "core/hle/service/sockets/ethc.h"
#include "core/hle/service/sockets/nsd.h"
#include "core/hle/service/sockets/sfdnsres.h"
#include "core/hle/service/sockets/sockets.h"
@@ -14,9 +13,6 @@ void InstallInterfaces(SM::ServiceManager& service_manager, Core::System& system
std::make_shared<BSD>(system, "bsd:u")->InstallAsService(service_manager);
std::make_shared<BSDCFG>(system)->InstallAsService(service_manager);
- std::make_shared<ETHC_C>(system)->InstallAsService(service_manager);
- std::make_shared<ETHC_I>(system)->InstallAsService(service_manager);
-
std::make_shared<NSD>(system, "nsd:a")->InstallAsService(service_manager);
std::make_shared<NSD>(system, "nsd:u")->InstallAsService(service_manager);
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:
void ImportServerPki(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto certificate_format = rp.PopEnum<CertificateFormat>();
- const auto pkcs_12_certificates = ctx.ReadBuffer(0);
+ [[maybe_unused]] const auto pkcs_12_certificates = ctx.ReadBuffer(0);
constexpr u64 server_id = 0;
@@ -113,13 +113,13 @@ private:
}
void ImportClientPki(Kernel::HLERequestContext& ctx) {
- const auto pkcs_12_certificate = ctx.ReadBuffer(0);
- const auto ascii_password = [&ctx] {
+ [[maybe_unused]] const auto pkcs_12_certificate = ctx.ReadBuffer(0);
+ [[maybe_unused]] const auto ascii_password = [&ctx] {
if (ctx.CanReadBuffer(1)) {
return ctx.ReadBuffer(1);
}
- return std::vector<u8>{};
+ return std::span<const u8>{};
}();
constexpr u64 client_id = 0;
diff --git a/src/core/hle/service/time/time_zone_manager.cpp b/src/core/hle/service/time/time_zone_manager.cpp
index f9ada7c93..973f7837a 100644
--- a/src/core/hle/service/time/time_zone_manager.cpp
+++ b/src/core/hle/service/time/time_zone_manager.cpp
@@ -286,7 +286,7 @@ static constexpr int TransitionTime(int year, Rule rule, int offset) {
}
static bool ParsePosixName(const char* name, TimeZoneRule& rule) {
- constexpr char default_rule[]{",M4.1.0,M10.5.0"};
+ static constexpr char default_rule[]{",M4.1.0,M10.5.0"};
const char* std_name{name};
int std_len{};
int offset{};
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:
return;
}
- const auto parcel = android::Parcel{NativeWindow{*buffer_queue_id}};
+ const auto parcel = android::OutputParcel{NativeWindow{*buffer_queue_id}};
const auto buffer_size = ctx.WriteBuffer(parcel.Serialize());
IPC::ResponseBuilder rb{ctx, 4};
@@ -649,7 +649,7 @@ private:
return;
}
- const auto parcel = android::Parcel{NativeWindow{*buffer_queue_id}};
+ const auto parcel = android::OutputParcel{NativeWindow{*buffer_queue_id}};
const auto buffer_size = ctx.WriteBuffer(parcel.Serialize());
IPC::ResponseBuilder rb{ctx, 6};
diff --git a/src/core/hle/service/wlan/wlan.cpp b/src/core/hle/service/wlan/wlan.cpp
deleted file mode 100644
index 226e3034c..000000000
--- a/src/core/hle/service/wlan/wlan.cpp
+++ /dev/null
@@ -1,186 +0,0 @@
-// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
-// SPDX-License-Identifier: GPL-2.0-or-later
-
-#include <memory>
-
-#include "core/hle/service/service.h"
-#include "core/hle/service/sm/sm.h"
-#include "core/hle/service/wlan/wlan.h"
-
-namespace Service::WLAN {
-
-class WLANInfra final : public ServiceFramework<WLANInfra> {
-public:
- explicit WLANInfra(Core::System& system_) : ServiceFramework{system_, "wlan:inf"} {
- // clang-format off
- static const FunctionInfo functions[] = {
- {0, nullptr, "OpenMode"},
- {1, nullptr, "CloseMode"},
- {2, nullptr, "GetMacAddress"},
- {3, nullptr, "StartScan"},
- {4, nullptr, "StopScan"},
- {5, nullptr, "Connect"},
- {6, nullptr, "CancelConnect"},
- {7, nullptr, "Disconnect"},
- {8, nullptr, "GetConnectionEvent"},
- {9, nullptr, "GetConnectionStatus"},
- {10, nullptr, "GetState"},
- {11, nullptr, "GetScanResult"},
- {12, nullptr, "GetRssi"},
- {13, nullptr, "ChangeRxAntenna"},
- {14, nullptr, "GetFwVersion"},
- {15, nullptr, "RequestSleep"},
- {16, nullptr, "RequestWakeUp"},
- {17, nullptr, "RequestIfUpDown"},
- {18, nullptr, "Unknown18"},
- {19, nullptr, "Unknown19"},
- {20, nullptr, "Unknown20"},
- {21, nullptr, "Unknown21"},
- {22, nullptr, "Unknown22"},
- {23, nullptr, "Unknown23"},
- {24, nullptr, "Unknown24"},
- {25, nullptr, "Unknown25"},
- {26, nullptr, "Unknown26"},
- {27, nullptr, "Unknown27"},
- {28, nullptr, "Unknown28"},
- {29, nullptr, "Unknown29"},
- {30, nullptr, "Unknown30"},
- {31, nullptr, "Unknown31"},
- {32, nullptr, "Unknown32"},
- {33, nullptr, "Unknown33"},
- {34, nullptr, "Unknown34"},
- {35, nullptr, "Unknown35"},
- {36, nullptr, "Unknown36"},
- {37, nullptr, "Unknown37"},
- {38, nullptr, "Unknown38"},
- };
- // clang-format on
-
- RegisterHandlers(functions);
- }
-};
-
-class WLANLocal final : public ServiceFramework<WLANLocal> {
-public:
- explicit WLANLocal(Core::System& system_) : ServiceFramework{system_, "wlan:lcl"} {
- // clang-format off
- static const FunctionInfo functions[] = {
- {0, nullptr, "Unknown0"},
- {1, nullptr, "Unknown1"},
- {2, nullptr, "Unknown2"},
- {3, nullptr, "Unknown3"},
- {4, nullptr, "Unknown4"},
- {5, nullptr, "Unknown5"},
- {6, nullptr, "GetMacAddress"},
- {7, nullptr, "CreateBss"},
- {8, nullptr, "DestroyBss"},
- {9, nullptr, "StartScan"},
- {10, nullptr, "StopScan"},
- {11, nullptr, "Connect"},
- {12, nullptr, "CancelConnect"},
- {13, nullptr, "Join"},
- {14, nullptr, "CancelJoin"},
- {15, nullptr, "Disconnect"},
- {16, nullptr, "SetBeaconLostCount"},
- {17, nullptr, "Unknown17"},
- {18, nullptr, "Unknown18"},
- {19, nullptr, "Unknown19"},
- {20, nullptr, "GetBssIndicationEvent"},
- {21, nullptr, "GetBssIndicationInfo"},
- {22, nullptr, "GetState"},
- {23, nullptr, "GetAllowedChannels"},
- {24, nullptr, "AddIe"},
- {25, nullptr, "DeleteIe"},
- {26, nullptr, "Unknown26"},
- {27, nullptr, "Unknown27"},
- {28, nullptr, "CreateRxEntry"},
- {29, nullptr, "DeleteRxEntry"},
- {30, nullptr, "Unknown30"},
- {31, nullptr, "Unknown31"},
- {32, nullptr, "AddMatchingDataToRxEntry"},
- {33, nullptr, "RemoveMatchingDataFromRxEntry"},
- {34, nullptr, "GetScanResult"},
- {35, nullptr, "Unknown35"},
- {36, nullptr, "SetActionFrameWithBeacon"},
- {37, nullptr, "CancelActionFrameWithBeacon"},
- {38, nullptr, "CreateRxEntryForActionFrame"},
- {39, nullptr, "DeleteRxEntryForActionFrame"},
- {40, nullptr, "Unknown40"},
- {41, nullptr, "Unknown41"},
- {42, nullptr, "CancelGetActionFrame"},
- {43, nullptr, "GetRssi"},
- {44, nullptr, "Unknown44"},
- {45, nullptr, "Unknown45"},
- {46, nullptr, "Unknown46"},
- {47, nullptr, "Unknown47"},
- {48, nullptr, "Unknown48"},
- {49, nullptr, "Unknown49"},
- {50, nullptr, "Unknown50"},
- {51, nullptr, "Unknown51"},
- };
- // clang-format on
-
- RegisterHandlers(functions);
- }
-};
-
-class WLANLocalGetFrame final : public ServiceFramework<WLANLocalGetFrame> {
-public:
- explicit WLANLocalGetFrame(Core::System& system_) : ServiceFramework{system_, "wlan:lg"} {
- // clang-format off
- static const FunctionInfo functions[] = {
- {0, nullptr, "Unknown"},
- };
- // clang-format on
-
- RegisterHandlers(functions);
- }
-};
-
-class WLANSocketGetFrame final : public ServiceFramework<WLANSocketGetFrame> {
-public:
- explicit WLANSocketGetFrame(Core::System& system_) : ServiceFramework{system_, "wlan:sg"} {
- // clang-format off
- static const FunctionInfo functions[] = {
- {0, nullptr, "Unknown"},
- };
- // clang-format on
-
- RegisterHandlers(functions);
- }
-};
-
-class WLANSocketManager final : public ServiceFramework<WLANSocketManager> {
-public:
- explicit WLANSocketManager(Core::System& system_) : ServiceFramework{system_, "wlan:soc"} {
- // clang-format off
- static const FunctionInfo functions[] = {
- {0, nullptr, "Unknown0"},
- {1, nullptr, "Unknown1"},
- {2, nullptr, "Unknown2"},
- {3, nullptr, "Unknown3"},
- {4, nullptr, "Unknown4"},
- {5, nullptr, "Unknown5"},
- {6, nullptr, "GetMacAddress"},
- {7, nullptr, "SwitchTsfTimerFunction"},
- {8, nullptr, "Unknown8"},
- {9, nullptr, "Unknown9"},
- {10, nullptr, "Unknown10"},
- {11, nullptr, "Unknown11"},
- {12, nullptr, "Unknown12"},
- };
- // clang-format on
-
- RegisterHandlers(functions);
- }
-};
-
-void InstallInterfaces(SM::ServiceManager& sm, Core::System& system) {
- std::make_shared<WLANInfra>(system)->InstallAsService(sm);
- std::make_shared<WLANLocal>(system)->InstallAsService(sm);
- std::make_shared<WLANLocalGetFrame>(system)->InstallAsService(sm);
- std::make_shared<WLANSocketGetFrame>(system)->InstallAsService(sm);
- std::make_shared<WLANSocketManager>(system)->InstallAsService(sm);
-}
-
-} // namespace Service::WLAN
diff --git a/src/core/hle/service/wlan/wlan.h b/src/core/hle/service/wlan/wlan.h
deleted file mode 100644
index 535c3bf0d..000000000
--- a/src/core/hle/service/wlan/wlan.h
+++ /dev/null
@@ -1,18 +0,0 @@
-// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
-// SPDX-License-Identifier: GPL-2.0-or-later
-
-#pragma once
-
-namespace Core {
-class System;
-}
-
-namespace Service::SM {
-class ServiceManager;
-}
-
-namespace Service::WLAN {
-
-void InstallInterfaces(SM::ServiceManager& sm, Core::System& system);
-
-} // namespace Service::WLAN
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
return {-1, GetAndLogLastError()};
}
-std::pair<s32, Errno> Socket::Send(const std::vector<u8>& message, int flags) {
+std::pair<s32, Errno> Socket::Send(std::span<const u8> message, int flags) {
ASSERT(message.size() < static_cast<size_t>(std::numeric_limits<int>::max()));
ASSERT(flags == 0);
@@ -563,7 +563,7 @@ std::pair<s32, Errno> Socket::Send(const std::vector<u8>& message, int flags) {
return {-1, GetAndLogLastError()};
}
-std::pair<s32, Errno> Socket::SendTo(u32 flags, const std::vector<u8>& message,
+std::pair<s32, Errno> Socket::SendTo(u32 flags, std::span<const u8> message,
const SockAddrIn* addr) {
ASSERT(flags == 0);
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
return {static_cast<u32>(read_bytes), Errno::SUCCESS};
}
-std::pair<s32, Errno> ProxySocket::Send(const std::vector<u8>& message, int flags) {
+std::pair<s32, Errno> ProxySocket::Send(std::span<const u8> message, int flags) {
LOG_WARNING(Network, "(STUBBED) called");
ASSERT(message.size() < static_cast<size_t>(std::numeric_limits<int>::max()));
ASSERT(flags == 0);
@@ -200,7 +200,7 @@ void ProxySocket::SendPacket(ProxyPacket& packet) {
}
}
-std::pair<s32, Errno> ProxySocket::SendTo(u32 flags, const std::vector<u8>& message,
+std::pair<s32, Errno> ProxySocket::SendTo(u32 flags, std::span<const u8> message,
const SockAddrIn* addr) {
ASSERT(flags == 0);
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 @@
#pragma once
#include <mutex>
+#include <span>
#include <vector>
#include <queue>
@@ -48,11 +49,11 @@ public:
std::pair<s32, Errno> ReceivePacket(int flags, std::vector<u8>& message, SockAddrIn* addr,
std::size_t max_length);
- std::pair<s32, Errno> Send(const std::vector<u8>& message, int flags) override;
+ std::pair<s32, Errno> Send(std::span<const u8> message, int flags) override;
void SendPacket(ProxyPacket& packet);
- std::pair<s32, Errno> SendTo(u32 flags, const std::vector<u8>& message,
+ std::pair<s32, Errno> SendTo(u32 flags, std::span<const u8> message,
const SockAddrIn* addr) override;
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 @@
#include <map>
#include <memory>
+#include <span>
#include <utility>
#if defined(_WIN32)
@@ -66,9 +67,9 @@ public:
virtual std::pair<s32, Errno> RecvFrom(int flags, std::vector<u8>& message,
SockAddrIn* addr) = 0;
- virtual std::pair<s32, Errno> Send(const std::vector<u8>& message, int flags) = 0;
+ virtual std::pair<s32, Errno> Send(std::span<const u8> message, int flags) = 0;
- virtual std::pair<s32, Errno> SendTo(u32 flags, const std::vector<u8>& message,
+ virtual std::pair<s32, Errno> SendTo(u32 flags, std::span<const u8> message,
const SockAddrIn* addr) = 0;
virtual Errno SetLinger(bool enable, u32 linger) = 0;
@@ -138,9 +139,9 @@ public:
std::pair<s32, Errno> RecvFrom(int flags, std::vector<u8>& message, SockAddrIn* addr) override;
- std::pair<s32, Errno> Send(const std::vector<u8>& message, int flags) override;
+ std::pair<s32, Errno> Send(std::span<const u8> message, int flags) override;
- std::pair<s32, Errno> SendTo(u32 flags, const std::vector<u8>& message,
+ std::pair<s32, Errno> SendTo(u32 flags, std::span<const u8> message,
const SockAddrIn* addr) override;
Errno SetLinger(bool enable, u32 linger) override;
diff --git a/src/core/loader/nso.cpp b/src/core/loader/nso.cpp
index 4c3b3c655..a5c384fb5 100644
--- a/src/core/loader/nso.cpp
+++ b/src/core/loader/nso.cpp
@@ -145,7 +145,7 @@ std::optional<VAddr> AppLoader_NSO::LoadModule(Kernel::KProcess& process, Core::
// Apply cheats if they exist and the program has a valid title ID
if (pm) {
- system.SetCurrentProcessBuildID(nso_header.build_id);
+ system.SetApplicationProcessBuildID(nso_header.build_id);
const auto cheats = pm->CreateCheatList(nso_header.build_id);
if (!cheats.empty()) {
system.RegisterCheatList(cheats, nso_header.build_id, load_base, image_size);
diff --git a/src/core/memory.cpp b/src/core/memory.cpp
index a1e41faff..4397fcfb1 100644
--- a/src/core/memory.cpp
+++ b/src/core/memory.cpp
@@ -247,11 +247,11 @@ struct Memory::Impl {
}
void ReadBlock(const VAddr src_addr, void* dest_buffer, const std::size_t size) {
- ReadBlockImpl<false>(*system.CurrentProcess(), src_addr, dest_buffer, size);
+ ReadBlockImpl<false>(*system.ApplicationProcess(), src_addr, dest_buffer, size);
}
void ReadBlockUnsafe(const VAddr src_addr, void* dest_buffer, const std::size_t size) {
- ReadBlockImpl<true>(*system.CurrentProcess(), src_addr, dest_buffer, size);
+ ReadBlockImpl<true>(*system.ApplicationProcess(), src_addr, dest_buffer, size);
}
template <bool UNSAFE>
@@ -279,11 +279,11 @@ struct Memory::Impl {
}
void WriteBlock(const VAddr dest_addr, const void* src_buffer, const std::size_t size) {
- WriteBlockImpl<false>(*system.CurrentProcess(), dest_addr, src_buffer, size);
+ WriteBlockImpl<false>(*system.ApplicationProcess(), dest_addr, src_buffer, size);
}
void WriteBlockUnsafe(const VAddr dest_addr, const void* src_buffer, const std::size_t size) {
- WriteBlockImpl<true>(*system.CurrentProcess(), dest_addr, src_buffer, size);
+ WriteBlockImpl<true>(*system.ApplicationProcess(), dest_addr, src_buffer, size);
}
void ZeroBlock(const Kernel::KProcess& process, const VAddr dest_addr, const std::size_t size) {
@@ -383,6 +383,10 @@ struct Memory::Impl {
return;
}
+ if (Settings::IsFastmemEnabled()) {
+ system.DeviceMemory().buffer.Protect(vaddr, size, !debug, !debug);
+ }
+
// Iterate over a contiguous CPU address space, marking/unmarking the region.
// The region is at a granularity of CPU pages.
@@ -436,7 +440,7 @@ struct Memory::Impl {
}
if (Settings::IsFastmemEnabled()) {
- const bool is_read_enable = !Settings::IsGPULevelExtreme() || !cached;
+ const bool is_read_enable = Settings::IsGPULevelHigh() || !cached;
system.DeviceMemory().buffer.Protect(vaddr, size, is_read_enable, !cached);
}
@@ -707,7 +711,7 @@ void Memory::UnmapRegion(Common::PageTable& page_table, VAddr base, u64 size) {
}
bool Memory::IsValidVirtualAddress(const VAddr vaddr) const {
- const Kernel::KProcess& process = *system.CurrentProcess();
+ const Kernel::KProcess& process = *system.ApplicationProcess();
const auto& page_table = process.PageTable().PageTableImpl();
const size_t page = vaddr >> YUZU_PAGEBITS;
if (page >= page_table.pointers.size()) {
diff --git a/src/core/memory/cheat_engine.cpp b/src/core/memory/cheat_engine.cpp
index ffdbacc18..44ee39648 100644
--- a/src/core/memory/cheat_engine.cpp
+++ b/src/core/memory/cheat_engine.cpp
@@ -191,10 +191,10 @@ void CheatEngine::Initialize() {
});
core_timing.ScheduleLoopingEvent(CHEAT_ENGINE_NS, CHEAT_ENGINE_NS, event);
- metadata.process_id = system.CurrentProcess()->GetProcessID();
- metadata.title_id = system.GetCurrentProcessProgramID();
+ metadata.process_id = system.ApplicationProcess()->GetProcessID();
+ metadata.title_id = system.GetApplicationProcessProgramID();
- const auto& page_table = system.CurrentProcess()->PageTable();
+ const auto& page_table = system.ApplicationProcess()->PageTable();
metadata.heap_extents = {
.base = page_table.GetHeapRegionStart(),
.size = page_table.GetHeapRegionSize(),
diff --git a/src/core/reporter.cpp b/src/core/reporter.cpp
index 77821e047..708ae17aa 100644
--- a/src/core/reporter.cpp
+++ b/src/core/reporter.cpp
@@ -110,7 +110,7 @@ json GetProcessorStateData(const std::string& architecture, u64 entry_point, u64
}
json GetProcessorStateDataAuto(Core::System& system) {
- const auto* process{system.CurrentProcess()};
+ const auto* process{system.ApplicationProcess()};
auto& arm{system.CurrentArmInterface()};
Core::ARM_Interface::ThreadContext64 context{};
@@ -234,7 +234,7 @@ void Reporter::SaveSvcBreakReport(u32 type, bool signal_debugger, u64 info1, u64
}
const auto timestamp = GetTimestamp();
- const auto title_id = system.GetCurrentProcessProgramID();
+ const auto title_id = system.GetApplicationProcessProgramID();
auto out = GetFullDataAuto(timestamp, title_id, system);
auto break_out = json{
@@ -261,7 +261,7 @@ void Reporter::SaveUnimplementedFunctionReport(Kernel::HLERequestContext& ctx, u
}
const auto timestamp = GetTimestamp();
- const auto title_id = system.GetCurrentProcessProgramID();
+ const auto title_id = system.GetApplicationProcessProgramID();
auto out = GetFullDataAuto(timestamp, title_id, system);
auto function_out = GetHLERequestContextData(ctx, system.Memory());
@@ -283,7 +283,7 @@ void Reporter::SaveUnimplementedAppletReport(
}
const auto timestamp = GetTimestamp();
- const auto title_id = system.GetCurrentProcessProgramID();
+ const auto title_id = system.GetApplicationProcessProgramID();
auto out = GetFullDataAuto(timestamp, title_id, system);
out["applet_common_args"] = {
@@ -312,7 +312,7 @@ void Reporter::SaveUnimplementedAppletReport(
}
void Reporter::SavePlayReport(PlayReportType type, u64 title_id,
- const std::vector<std::vector<u8>>& data,
+ const std::vector<std::span<const u8>>& data,
std::optional<u64> process_id, std::optional<u128> user_id) const {
if (!IsReportingEnabled()) {
return;
@@ -376,7 +376,7 @@ void Reporter::SaveUserReport() const {
}
const auto timestamp = GetTimestamp();
- const auto title_id = system.GetCurrentProcessProgramID();
+ const auto title_id = system.GetApplicationProcessProgramID();
SaveToFile(GetFullDataAuto(timestamp, title_id, system),
GetPath("user_report", title_id, timestamp));
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 @@
#include <array>
#include <optional>
+#include <span>
#include <string>
#include <vector>
#include "common/common_types.h"
@@ -56,7 +57,8 @@ public:
System,
};
- void SavePlayReport(PlayReportType type, u64 title_id, const std::vector<std::vector<u8>>& data,
+ void SavePlayReport(PlayReportType type, u64 title_id,
+ const std::vector<std::span<const u8>>& data,
std::optional<u64> process_id = {}, std::optional<u128> user_id = {}) const;
// Used by error applet
diff --git a/src/core/telemetry_session.cpp b/src/core/telemetry_session.cpp
index 8d5f2be2f..9178b00ca 100644
--- a/src/core/telemetry_session.cpp
+++ b/src/core/telemetry_session.cpp
@@ -34,7 +34,7 @@ static u64 GenerateTelemetryId() {
mbedtls_entropy_context entropy;
mbedtls_entropy_init(&entropy);
mbedtls_ctr_drbg_context ctr_drbg;
- constexpr std::array<char, 18> personalization{{"yuzu Telemetry ID"}};
+ static constexpr std::array<char, 18> personalization{{"yuzu Telemetry ID"}};
mbedtls_ctr_drbg_init(&ctr_drbg);
ASSERT(mbedtls_ctr_drbg_seed(&ctr_drbg, mbedtls_entropy_func, &entropy,
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()
if (ENABLE_SDL2)
target_sources(input_common PRIVATE
+ drivers/joycon.cpp
+ drivers/joycon.h
drivers/sdl_driver.cpp
drivers/sdl_driver.h
+ helpers/joycon_driver.cpp
+ helpers/joycon_driver.h
+ helpers/joycon_protocol/calibration.cpp
+ helpers/joycon_protocol/calibration.h
+ helpers/joycon_protocol/common_protocol.cpp
+ helpers/joycon_protocol/common_protocol.h
+ helpers/joycon_protocol/generic_functions.cpp
+ helpers/joycon_protocol/generic_functions.h
+ helpers/joycon_protocol/joycon_types.h
+ helpers/joycon_protocol/irs.cpp
+ helpers/joycon_protocol/irs.h
+ helpers/joycon_protocol/nfc.cpp
+ helpers/joycon_protocol/nfc.h
+ helpers/joycon_protocol/poller.cpp
+ helpers/joycon_protocol/poller.h
+ helpers/joycon_protocol/ringcon.cpp
+ helpers/joycon_protocol/ringcon.h
+ helpers/joycon_protocol/rumble.cpp
+ helpers/joycon_protocol/rumble.h
)
target_link_libraries(input_common PRIVATE SDL2::SDL2)
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 {
}
}
-Common::Input::CameraError Camera::SetCameraFormat(
+Common::Input::DriverResult Camera::SetCameraFormat(
[[maybe_unused]] const PadIdentifier& identifier_,
const Common::Input::CameraFormat camera_format) {
status.format = camera_format;
- return Common::Input::CameraError::None;
+ return Common::Input::DriverResult::Success;
}
} // 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:
std::size_t getImageWidth() const;
std::size_t getImageHeight() const;
- Common::Input::CameraError SetCameraFormat(const PadIdentifier& identifier_,
- Common::Input::CameraFormat camera_format) override;
+ Common::Input::DriverResult SetCameraFormat(const PadIdentifier& identifier_,
+ Common::Input::CameraFormat camera_format) override;
private:
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 @@
#include "common/logging/log.h"
#include "common/param_package.h"
+#include "common/polyfill_thread.h"
#include "common/settings_input.h"
#include "common/thread.h"
#include "input_common/drivers/gc_adapter.h"
@@ -217,8 +218,7 @@ void GCAdapter::AdapterScanThread(std::stop_token stop_token) {
Common::SetCurrentThreadName("ScanGCAdapter");
usb_adapter_handle = nullptr;
pads = {};
- while (!stop_token.stop_requested() && !Setup()) {
- std::this_thread::sleep_for(std::chrono::seconds(2));
+ while (!Setup() && Common::StoppableTimedWait(stop_token, std::chrono::seconds{2})) {
}
}
@@ -324,7 +324,7 @@ bool GCAdapter::GetGCEndpoint(libusb_device* device) {
return true;
}
-Common::Input::VibrationError GCAdapter::SetVibration(
+Common::Input::DriverResult GCAdapter::SetVibration(
const PadIdentifier& identifier, const Common::Input::VibrationStatus& vibration) {
const auto mean_amplitude = (vibration.low_amplitude + vibration.high_amplitude) * 0.5f;
const auto processed_amplitude =
@@ -333,9 +333,9 @@ Common::Input::VibrationError GCAdapter::SetVibration(
pads[identifier.port].rumble_amplitude = processed_amplitude;
if (!rumble_enabled) {
- return Common::Input::VibrationError::Disabled;
+ return Common::Input::DriverResult::Disabled;
}
- return Common::Input::VibrationError::None;
+ return Common::Input::DriverResult::Success;
}
bool 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:
explicit GCAdapter(std::string input_engine_);
~GCAdapter() override;
- Common::Input::VibrationError SetVibration(
+ Common::Input::DriverResult SetVibration(
const PadIdentifier& identifier, const Common::Input::VibrationStatus& vibration) override;
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..b4cd39a20
--- /dev/null
+++ b/src/input_common/drivers/joycon.cpp
@@ -0,0 +1,724 @@
+// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include <fmt/format.h>
+
+#include "common/param_package.h"
+#include "common/polyfill_ranges.h"
+#include "common/polyfill_thread.h"
+#include "common/settings.h"
+#include "common/thread.h"
+#include "input_common/drivers/joycon.h"
+#include "input_common/helpers/joycon_driver.h"
+#include "input_common/helpers/joycon_protocol/joycon_types.h"
+
+namespace InputCommon {
+
+Joycons::Joycons(const std::string& input_engine_) : InputEngine(input_engine_) {
+ // Avoid conflicting with SDL driver
+ if (!Settings::values.enable_joycon_driver && !Settings::values.enable_procon_driver) {
+ return;
+ }
+ LOG_INFO(Input, "Joycon driver Initialization started");
+ const int init_res = SDL_hid_init();
+ if (init_res == 0) {
+ Setup();
+ } else {
+ LOG_ERROR(Input, "Hidapi could not be initialized. failed with error = {}", init_res);
+ }
+}
+
+Joycons::~Joycons() {
+ Reset();
+}
+
+void Joycons::Reset() {
+ scan_thread = {};
+ for (const auto& device : left_joycons) {
+ if (!device) {
+ continue;
+ }
+ device->Stop();
+ }
+ for (const auto& device : right_joycons) {
+ if (!device) {
+ continue;
+ }
+ device->Stop();
+ }
+ for (const auto& device : pro_controller) {
+ if (!device) {
+ continue;
+ }
+ device->Stop();
+ }
+ SDL_hid_exit();
+}
+
+void Joycons::Setup() {
+ u32 port = 0;
+ PreSetController(GetIdentifier(0, Joycon::ControllerType::None));
+ for (auto& device : left_joycons) {
+ PreSetController(GetIdentifier(port, Joycon::ControllerType::Left));
+ device = std::make_shared<Joycon::JoyconDriver>(port++);
+ }
+ port = 0;
+ for (auto& device : right_joycons) {
+ PreSetController(GetIdentifier(port, Joycon::ControllerType::Right));
+ device = std::make_shared<Joycon::JoyconDriver>(port++);
+ }
+ port = 0;
+ for (auto& device : pro_controller) {
+ PreSetController(GetIdentifier(port, Joycon::ControllerType::Pro));
+ device = std::make_shared<Joycon::JoyconDriver>(port++);
+ }
+
+ scan_thread = std::jthread([this](std::stop_token stop_token) { ScanThread(stop_token); });
+}
+
+void Joycons::ScanThread(std::stop_token stop_token) {
+ constexpr u16 nintendo_vendor_id = 0x057e;
+ Common::SetCurrentThreadName("JoyconScanThread");
+
+ do {
+ SDL_hid_device_info* devs = SDL_hid_enumerate(nintendo_vendor_id, 0x0);
+ SDL_hid_device_info* cur_dev = devs;
+
+ while (cur_dev) {
+ if (IsDeviceNew(cur_dev)) {
+ LOG_DEBUG(Input, "Device Found,type : {:04X} {:04X}", cur_dev->vendor_id,
+ cur_dev->product_id);
+ RegisterNewDevice(cur_dev);
+ }
+ cur_dev = cur_dev->next;
+ }
+
+ SDL_hid_free_enumeration(devs);
+ } while (Common::StoppableTimedWait(stop_token, std::chrono::seconds{5}));
+}
+
+bool Joycons::IsDeviceNew(SDL_hid_device_info* device_info) const {
+ Joycon::ControllerType type{};
+ Joycon::SerialNumber serial_number{};
+
+ const auto result = Joycon::JoyconDriver::GetDeviceType(device_info, type);
+ if (result != Joycon::DriverResult::Success) {
+ return false;
+ }
+
+ const auto result2 = Joycon::JoyconDriver::GetSerialNumber(device_info, serial_number);
+ if (result2 != Joycon::DriverResult::Success) {
+ return false;
+ }
+
+ auto is_handle_identical = [serial_number](std::shared_ptr<Joycon::JoyconDriver> device) {
+ if (!device) {
+ return false;
+ }
+ if (!device->IsConnected()) {
+ return false;
+ }
+ if (device->GetHandleSerialNumber() != serial_number) {
+ return false;
+ }
+ return true;
+ };
+
+ // Check if device already exist
+ switch (type) {
+ case Joycon::ControllerType::Left:
+ if (!Settings::values.enable_joycon_driver) {
+ return false;
+ }
+ for (const auto& device : left_joycons) {
+ if (is_handle_identical(device)) {
+ return false;
+ }
+ }
+ break;
+ case Joycon::ControllerType::Right:
+ if (!Settings::values.enable_joycon_driver) {
+ return false;
+ }
+ for (const auto& device : right_joycons) {
+ if (is_handle_identical(device)) {
+ return false;
+ }
+ }
+ break;
+ case Joycon::ControllerType::Pro:
+ if (!Settings::values.enable_procon_driver) {
+ return false;
+ }
+ for (const auto& device : pro_controller) {
+ if (is_handle_identical(device)) {
+ return false;
+ }
+ }
+ break;
+ default:
+ return false;
+ }
+
+ return true;
+}
+
+void Joycons::RegisterNewDevice(SDL_hid_device_info* device_info) {
+ Joycon::ControllerType type{};
+ auto result = Joycon::JoyconDriver::GetDeviceType(device_info, type);
+ auto handle = GetNextFreeHandle(type);
+ if (handle == nullptr) {
+ LOG_WARNING(Input, "No free handles available");
+ return;
+ }
+ if (result == Joycon::DriverResult::Success) {
+ result = handle->RequestDeviceAccess(device_info);
+ }
+ if (result == Joycon::DriverResult::Success) {
+ LOG_WARNING(Input, "Initialize device");
+
+ const std::size_t port = handle->GetDevicePort();
+ const Joycon::JoyconCallbacks callbacks{
+ .on_battery_data = {[this, port, type](Joycon::Battery value) {
+ OnBatteryUpdate(port, type, value);
+ }},
+ .on_color_data = {[this, port, type](Joycon::Color value) {
+ OnColorUpdate(port, type, value);
+ }},
+ .on_button_data = {[this, port, type](int id, bool value) {
+ OnButtonUpdate(port, type, id, value);
+ }},
+ .on_stick_data = {[this, port, type](int id, f32 value) {
+ OnStickUpdate(port, type, id, value);
+ }},
+ .on_motion_data = {[this, port, type](int id, const Joycon::MotionData& value) {
+ OnMotionUpdate(port, type, id, value);
+ }},
+ .on_ring_data = {[this](f32 ring_data) { OnRingConUpdate(ring_data); }},
+ .on_amiibo_data = {[this, port](const std::vector<u8>& amiibo_data) {
+ OnAmiiboUpdate(port, amiibo_data);
+ }},
+ .on_camera_data = {[this, port](const std::vector<u8>& camera_data,
+ Joycon::IrsResolution format) {
+ OnCameraUpdate(port, camera_data, format);
+ }},
+ };
+
+ handle->InitializeDevice();
+ handle->SetCallbacks(callbacks);
+ }
+}
+
+std::shared_ptr<Joycon::JoyconDriver> Joycons::GetNextFreeHandle(
+ Joycon::ControllerType type) const {
+ if (type == Joycon::ControllerType::Left) {
+ const auto unconnected_device =
+ std::ranges::find_if(left_joycons, [](auto& device) { return !device->IsConnected(); });
+ if (unconnected_device != left_joycons.end()) {
+ return *unconnected_device;
+ }
+ }
+ if (type == Joycon::ControllerType::Right) {
+ const auto unconnected_device = std::ranges::find_if(
+ right_joycons, [](auto& device) { return !device->IsConnected(); });
+
+ if (unconnected_device != right_joycons.end()) {
+ return *unconnected_device;
+ }
+ }
+ if (type == Joycon::ControllerType::Pro) {
+ const auto unconnected_device = std::ranges::find_if(
+ pro_controller, [](auto& device) { return !device->IsConnected(); });
+
+ if (unconnected_device != pro_controller.end()) {
+ return *unconnected_device;
+ }
+ }
+ return nullptr;
+}
+
+bool Joycons::IsVibrationEnabled(const PadIdentifier& identifier) {
+ const auto handle = GetHandle(identifier);
+ if (handle == nullptr) {
+ return false;
+ }
+ return handle->IsVibrationEnabled();
+}
+
+Common::Input::DriverResult Joycons::SetVibration(const PadIdentifier& identifier,
+ const Common::Input::VibrationStatus& vibration) {
+ const Joycon::VibrationValue native_vibration{
+ .low_amplitude = vibration.low_amplitude,
+ .low_frequency = vibration.low_frequency,
+ .high_amplitude = vibration.high_amplitude,
+ .high_frequency = vibration.high_frequency,
+ };
+ auto handle = GetHandle(identifier);
+ if (handle == nullptr) {
+ return Common::Input::DriverResult::InvalidHandle;
+ }
+
+ handle->SetVibration(native_vibration);
+ return Common::Input::DriverResult::Success;
+}
+
+Common::Input::DriverResult Joycons::SetLeds(const PadIdentifier& identifier,
+ const Common::Input::LedStatus& led_status) {
+ auto handle = GetHandle(identifier);
+ if (handle == nullptr) {
+ return Common::Input::DriverResult::InvalidHandle;
+ }
+ int led_config = led_status.led_1 ? 1 : 0;
+ led_config += led_status.led_2 ? 2 : 0;
+ led_config += led_status.led_3 ? 4 : 0;
+ led_config += led_status.led_4 ? 8 : 0;
+
+ return static_cast<Common::Input::DriverResult>(
+ handle->SetLedConfig(static_cast<u8>(led_config)));
+}
+
+Common::Input::DriverResult Joycons::SetCameraFormat(const PadIdentifier& identifier,
+ Common::Input::CameraFormat camera_format) {
+ auto handle = GetHandle(identifier);
+ if (handle == nullptr) {
+ return Common::Input::DriverResult::InvalidHandle;
+ }
+ return static_cast<Common::Input::DriverResult>(handle->SetIrsConfig(
+ Joycon::IrsMode::ImageTransfer, static_cast<Joycon::IrsResolution>(camera_format)));
+};
+
+Common::Input::NfcState Joycons::SupportsNfc(const PadIdentifier& identifier_) const {
+ return Common::Input::NfcState::Success;
+};
+
+Common::Input::NfcState Joycons::WriteNfcData(const PadIdentifier& identifier_,
+ const std::vector<u8>& data) {
+ return Common::Input::NfcState::NotSupported;
+};
+
+Common::Input::DriverResult Joycons::SetPollingMode(const PadIdentifier& identifier,
+ const Common::Input::PollingMode polling_mode) {
+ auto handle = GetHandle(identifier);
+ if (handle == nullptr) {
+ LOG_ERROR(Input, "Invalid handle {}", identifier.port);
+ return Common::Input::DriverResult::InvalidHandle;
+ }
+
+ switch (polling_mode) {
+ case Common::Input::PollingMode::Active:
+ return static_cast<Common::Input::DriverResult>(handle->SetActiveMode());
+ case Common::Input::PollingMode::Pasive:
+ return static_cast<Common::Input::DriverResult>(handle->SetPasiveMode());
+ case Common::Input::PollingMode::IR:
+ return static_cast<Common::Input::DriverResult>(handle->SetIrMode());
+ case Common::Input::PollingMode::NFC:
+ return static_cast<Common::Input::DriverResult>(handle->SetNfcMode());
+ case Common::Input::PollingMode::Ring:
+ return static_cast<Common::Input::DriverResult>(handle->SetRingConMode());
+ default:
+ return Common::Input::DriverResult::NotSupported;
+ }
+}
+
+void Joycons::OnBatteryUpdate(std::size_t port, Joycon::ControllerType type,
+ Joycon::Battery value) {
+ const auto identifier = GetIdentifier(port, type);
+ if (value.charging != 0) {
+ SetBattery(identifier, Common::Input::BatteryLevel::Charging);
+ return;
+ }
+
+ Common::Input::BatteryLevel battery{};
+ switch (value.status) {
+ case 0:
+ battery = Common::Input::BatteryLevel::Empty;
+ break;
+ case 1:
+ battery = Common::Input::BatteryLevel::Critical;
+ break;
+ case 2:
+ battery = Common::Input::BatteryLevel::Low;
+ break;
+ case 3:
+ battery = Common::Input::BatteryLevel::Medium;
+ break;
+ case 4:
+ default:
+ battery = Common::Input::BatteryLevel::Full;
+ break;
+ }
+ SetBattery(identifier, battery);
+}
+
+void Joycons::OnColorUpdate(std::size_t port, Joycon::ControllerType type,
+ const Joycon::Color& value) {
+ const auto identifier = GetIdentifier(port, type);
+ Common::Input::BodyColorStatus color{
+ .body = value.body,
+ .buttons = value.buttons,
+ .left_grip = value.left_grip,
+ .right_grip = value.right_grip,
+ };
+ SetColor(identifier, color);
+}
+
+void Joycons::OnButtonUpdate(std::size_t port, Joycon::ControllerType type, int id, bool value) {
+ const auto identifier = GetIdentifier(port, type);
+ SetButton(identifier, id, value);
+}
+
+void Joycons::OnStickUpdate(std::size_t port, Joycon::ControllerType type, int id, f32 value) {
+ const auto identifier = GetIdentifier(port, type);
+ SetAxis(identifier, id, value);
+}
+
+void Joycons::OnMotionUpdate(std::size_t port, Joycon::ControllerType type, int id,
+ const Joycon::MotionData& value) {
+ const auto identifier = GetIdentifier(port, type);
+ BasicMotion motion_data{
+ .gyro_x = value.gyro_x,
+ .gyro_y = value.gyro_y,
+ .gyro_z = value.gyro_z,
+ .accel_x = value.accel_x,
+ .accel_y = value.accel_y,
+ .accel_z = value.accel_z,
+ .delta_timestamp = 15000,
+ };
+ SetMotion(identifier, id, motion_data);
+}
+
+void Joycons::OnRingConUpdate(f32 ring_data) {
+ // To simplify ring detection it will always be mapped to an empty identifier for all
+ // controllers
+ static constexpr PadIdentifier identifier = {
+ .guid = Common::UUID{},
+ .port = 0,
+ .pad = 0,
+ };
+ SetAxis(identifier, 100, ring_data);
+}
+
+void Joycons::OnAmiiboUpdate(std::size_t port, const std::vector<u8>& amiibo_data) {
+ const auto identifier = GetIdentifier(port, Joycon::ControllerType::Right);
+ const auto nfc_state = amiibo_data.empty() ? Common::Input::NfcState::AmiiboRemoved
+ : Common::Input::NfcState::NewAmiibo;
+ SetNfc(identifier, {nfc_state, amiibo_data});
+}
+
+void Joycons::OnCameraUpdate(std::size_t port, const std::vector<u8>& camera_data,
+ Joycon::IrsResolution format) {
+ const auto identifier = GetIdentifier(port, Joycon::ControllerType::Right);
+ SetCamera(identifier, {static_cast<Common::Input::CameraFormat>(format), camera_data});
+}
+
+std::shared_ptr<Joycon::JoyconDriver> Joycons::GetHandle(PadIdentifier identifier) const {
+ auto is_handle_active = [&](std::shared_ptr<Joycon::JoyconDriver> device) {
+ if (!device) {
+ return false;
+ }
+ if (!device->IsConnected()) {
+ return false;
+ }
+ if (device->GetDevicePort() == identifier.port) {
+ return true;
+ }
+ return false;
+ };
+ const auto type = static_cast<Joycon::ControllerType>(identifier.pad);
+
+ if (type == Joycon::ControllerType::Left) {
+ const auto matching_device = std::ranges::find_if(
+ left_joycons, [is_handle_active](auto& device) { return is_handle_active(device); });
+
+ if (matching_device != left_joycons.end()) {
+ return *matching_device;
+ }
+ }
+
+ if (type == Joycon::ControllerType::Right) {
+ const auto matching_device = std::ranges::find_if(
+ right_joycons, [is_handle_active](auto& device) { return is_handle_active(device); });
+
+ if (matching_device != right_joycons.end()) {
+ return *matching_device;
+ }
+ }
+
+ if (type == Joycon::ControllerType::Pro) {
+ const auto matching_device = std::ranges::find_if(
+ pro_controller, [is_handle_active](auto& device) { return is_handle_active(device); });
+
+ if (matching_device != pro_controller.end()) {
+ return *matching_device;
+ }
+ }
+
+ return nullptr;
+}
+
+PadIdentifier Joycons::GetIdentifier(std::size_t port, Joycon::ControllerType type) const {
+ const std::array<u8, 16> guid{0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, static_cast<u8>(type)};
+ return {
+ .guid = Common::UUID{guid},
+ .port = port,
+ .pad = static_cast<std::size_t>(type),
+ };
+}
+
+Common::ParamPackage Joycons::GetParamPackage(std::size_t port, Joycon::ControllerType type) const {
+ const auto identifier = GetIdentifier(port, type);
+ return {
+ {"engine", GetEngineName()},
+ {"guid", identifier.guid.RawString()},
+ {"port", std::to_string(identifier.port)},
+ {"pad", std::to_string(identifier.pad)},
+ };
+}
+
+std::vector<Common::ParamPackage> Joycons::GetInputDevices() const {
+ std::vector<Common::ParamPackage> devices{};
+
+ auto add_entry = [&](std::shared_ptr<Joycon::JoyconDriver> device) {
+ if (!device) {
+ return;
+ }
+ if (!device->IsConnected()) {
+ return;
+ }
+ auto param = GetParamPackage(device->GetDevicePort(), device->GetHandleDeviceType());
+ std::string name = fmt::format("{} {}", JoyconName(device->GetHandleDeviceType()),
+ device->GetDevicePort() + 1);
+ param.Set("display", std::move(name));
+ devices.emplace_back(param);
+ };
+
+ for (const auto& controller : left_joycons) {
+ add_entry(controller);
+ }
+ for (const auto& controller : right_joycons) {
+ add_entry(controller);
+ }
+ for (const auto& controller : pro_controller) {
+ add_entry(controller);
+ }
+
+ // List dual joycon pairs
+ for (std::size_t i = 0; i < MaxSupportedControllers; i++) {
+ if (!left_joycons[i] || !right_joycons[i]) {
+ continue;
+ }
+ if (!left_joycons[i]->IsConnected() || !right_joycons[i]->IsConnected()) {
+ continue;
+ }
+ auto main_param = GetParamPackage(i, left_joycons[i]->GetHandleDeviceType());
+ const auto second_param = GetParamPackage(i, right_joycons[i]->GetHandleDeviceType());
+ const auto type = Joycon::ControllerType::Dual;
+ std::string name = fmt::format("{} {}", JoyconName(type), i + 1);
+
+ main_param.Set("display", std::move(name));
+ main_param.Set("guid2", second_param.Get("guid", ""));
+ main_param.Set("pad", std::to_string(static_cast<size_t>(type)));
+ devices.emplace_back(main_param);
+ }
+
+ return devices;
+}
+
+ButtonMapping Joycons::GetButtonMappingForDevice(const Common::ParamPackage& params) {
+ static constexpr std::array<std::tuple<Settings::NativeButton::Values, Joycon::PadButton, bool>,
+ 18>
+ switch_to_joycon_button = {
+ std::tuple{Settings::NativeButton::A, Joycon::PadButton::A, true},
+ {Settings::NativeButton::B, Joycon::PadButton::B, true},
+ {Settings::NativeButton::X, Joycon::PadButton::X, true},
+ {Settings::NativeButton::Y, Joycon::PadButton::Y, true},
+ {Settings::NativeButton::DLeft, Joycon::PadButton::Left, false},
+ {Settings::NativeButton::DUp, Joycon::PadButton::Up, false},
+ {Settings::NativeButton::DRight, Joycon::PadButton::Right, false},
+ {Settings::NativeButton::DDown, Joycon::PadButton::Down, false},
+ {Settings::NativeButton::L, Joycon::PadButton::L, false},
+ {Settings::NativeButton::R, Joycon::PadButton::R, true},
+ {Settings::NativeButton::ZL, Joycon::PadButton::ZL, false},
+ {Settings::NativeButton::ZR, Joycon::PadButton::ZR, true},
+ {Settings::NativeButton::Plus, Joycon::PadButton::Plus, true},
+ {Settings::NativeButton::Minus, Joycon::PadButton::Minus, false},
+ {Settings::NativeButton::Home, Joycon::PadButton::Home, true},
+ {Settings::NativeButton::Screenshot, Joycon::PadButton::Capture, false},
+ {Settings::NativeButton::LStick, Joycon::PadButton::StickL, false},
+ {Settings::NativeButton::RStick, Joycon::PadButton::StickR, true},
+ };
+
+ if (!params.Has("port")) {
+ return {};
+ }
+
+ ButtonMapping mapping{};
+ for (const auto& [switch_button, joycon_button, side] : switch_to_joycon_button) {
+ const std::size_t port = static_cast<std::size_t>(params.Get("port", 0));
+ auto pad = static_cast<Joycon::ControllerType>(params.Get("pad", 0));
+ if (pad == Joycon::ControllerType::Dual) {
+ pad = side ? Joycon::ControllerType::Right : Joycon::ControllerType::Left;
+ }
+
+ Common::ParamPackage button_params = GetParamPackage(port, pad);
+ button_params.Set("button", static_cast<int>(joycon_button));
+ mapping.insert_or_assign(switch_button, std::move(button_params));
+ }
+
+ // Map SL and SR buttons for left joycons
+ if (params.Get("pad", 0) == static_cast<int>(Joycon::ControllerType::Left)) {
+ const std::size_t port = static_cast<std::size_t>(params.Get("port", 0));
+ Common::ParamPackage button_params = GetParamPackage(port, Joycon::ControllerType::Left);
+
+ Common::ParamPackage sl_button_params = button_params;
+ Common::ParamPackage sr_button_params = button_params;
+ sl_button_params.Set("button", static_cast<int>(Joycon::PadButton::LeftSL));
+ sr_button_params.Set("button", static_cast<int>(Joycon::PadButton::LeftSR));
+ mapping.insert_or_assign(Settings::NativeButton::SL, std::move(sl_button_params));
+ mapping.insert_or_assign(Settings::NativeButton::SR, std::move(sr_button_params));
+ }
+
+ // Map SL and SR buttons for right joycons
+ if (params.Get("pad", 0) == static_cast<int>(Joycon::ControllerType::Right)) {
+ const std::size_t port = static_cast<std::size_t>(params.Get("port", 0));
+ Common::ParamPackage button_params = GetParamPackage(port, Joycon::ControllerType::Right);
+
+ Common::ParamPackage sl_button_params = button_params;
+ Common::ParamPackage sr_button_params = button_params;
+ sl_button_params.Set("button", static_cast<int>(Joycon::PadButton::RightSL));
+ sr_button_params.Set("button", static_cast<int>(Joycon::PadButton::RightSR));
+ mapping.insert_or_assign(Settings::NativeButton::SL, std::move(sl_button_params));
+ mapping.insert_or_assign(Settings::NativeButton::SR, std::move(sr_button_params));
+ }
+
+ return mapping;
+}
+
+AnalogMapping Joycons::GetAnalogMappingForDevice(const Common::ParamPackage& params) {
+ if (!params.Has("port")) {
+ return {};
+ }
+
+ const std::size_t port = static_cast<std::size_t>(params.Get("port", 0));
+ auto pad_left = static_cast<Joycon::ControllerType>(params.Get("pad", 0));
+ auto pad_right = pad_left;
+ if (pad_left == Joycon::ControllerType::Dual) {
+ pad_left = Joycon::ControllerType::Left;
+ pad_right = Joycon::ControllerType::Right;
+ }
+
+ AnalogMapping mapping = {};
+ Common::ParamPackage left_analog_params = GetParamPackage(port, pad_left);
+ left_analog_params.Set("axis_x", static_cast<int>(Joycon::PadAxes::LeftStickX));
+ left_analog_params.Set("axis_y", static_cast<int>(Joycon::PadAxes::LeftStickY));
+ mapping.insert_or_assign(Settings::NativeAnalog::LStick, std::move(left_analog_params));
+ Common::ParamPackage right_analog_params = GetParamPackage(port, pad_right);
+ right_analog_params.Set("axis_x", static_cast<int>(Joycon::PadAxes::RightStickX));
+ right_analog_params.Set("axis_y", static_cast<int>(Joycon::PadAxes::RightStickY));
+ mapping.insert_or_assign(Settings::NativeAnalog::RStick, std::move(right_analog_params));
+ return mapping;
+}
+
+MotionMapping Joycons::GetMotionMappingForDevice(const Common::ParamPackage& params) {
+ if (!params.Has("port")) {
+ return {};
+ }
+
+ const std::size_t port = static_cast<std::size_t>(params.Get("port", 0));
+ auto pad_left = static_cast<Joycon::ControllerType>(params.Get("pad", 0));
+ auto pad_right = pad_left;
+ if (pad_left == Joycon::ControllerType::Dual) {
+ pad_left = Joycon::ControllerType::Left;
+ pad_right = Joycon::ControllerType::Right;
+ }
+
+ MotionMapping mapping = {};
+ Common::ParamPackage left_motion_params = GetParamPackage(port, pad_left);
+ left_motion_params.Set("motion", 0);
+ mapping.insert_or_assign(Settings::NativeMotion::MotionLeft, std::move(left_motion_params));
+ Common::ParamPackage right_Motion_params = GetParamPackage(port, pad_right);
+ right_Motion_params.Set("motion", 1);
+ mapping.insert_or_assign(Settings::NativeMotion::MotionRight, std::move(right_Motion_params));
+ return mapping;
+}
+
+Common::Input::ButtonNames Joycons::GetUIButtonName(const Common::ParamPackage& params) const {
+ const auto button = static_cast<Joycon::PadButton>(params.Get("button", 0));
+ switch (button) {
+ case Joycon::PadButton::Left:
+ return Common::Input::ButtonNames::ButtonLeft;
+ case Joycon::PadButton::Right:
+ return Common::Input::ButtonNames::ButtonRight;
+ case Joycon::PadButton::Down:
+ return Common::Input::ButtonNames::ButtonDown;
+ case Joycon::PadButton::Up:
+ return Common::Input::ButtonNames::ButtonUp;
+ case Joycon::PadButton::LeftSL:
+ case Joycon::PadButton::RightSL:
+ return Common::Input::ButtonNames::TriggerSL;
+ case Joycon::PadButton::LeftSR:
+ case Joycon::PadButton::RightSR:
+ return Common::Input::ButtonNames::TriggerSR;
+ case Joycon::PadButton::L:
+ return Common::Input::ButtonNames::TriggerL;
+ case Joycon::PadButton::R:
+ return Common::Input::ButtonNames::TriggerR;
+ case Joycon::PadButton::ZL:
+ return Common::Input::ButtonNames::TriggerZL;
+ case Joycon::PadButton::ZR:
+ return Common::Input::ButtonNames::TriggerZR;
+ case Joycon::PadButton::A:
+ return Common::Input::ButtonNames::ButtonA;
+ case Joycon::PadButton::B:
+ return Common::Input::ButtonNames::ButtonB;
+ case Joycon::PadButton::X:
+ return Common::Input::ButtonNames::ButtonX;
+ case Joycon::PadButton::Y:
+ return Common::Input::ButtonNames::ButtonY;
+ case Joycon::PadButton::Plus:
+ return Common::Input::ButtonNames::ButtonPlus;
+ case Joycon::PadButton::Minus:
+ return Common::Input::ButtonNames::ButtonMinus;
+ case Joycon::PadButton::Home:
+ return Common::Input::ButtonNames::ButtonHome;
+ case Joycon::PadButton::Capture:
+ return Common::Input::ButtonNames::ButtonCapture;
+ case Joycon::PadButton::StickL:
+ return Common::Input::ButtonNames::ButtonStickL;
+ case Joycon::PadButton::StickR:
+ return Common::Input::ButtonNames::ButtonStickR;
+ default:
+ return Common::Input::ButtonNames::Undefined;
+ }
+}
+
+Common::Input::ButtonNames Joycons::GetUIName(const Common::ParamPackage& params) const {
+ if (params.Has("button")) {
+ return GetUIButtonName(params);
+ }
+ if (params.Has("axis")) {
+ return Common::Input::ButtonNames::Value;
+ }
+ if (params.Has("motion")) {
+ return Common::Input::ButtonNames::Engine;
+ }
+
+ return Common::Input::ButtonNames::Invalid;
+}
+
+std::string Joycons::JoyconName(Joycon::ControllerType type) const {
+ switch (type) {
+ case Joycon::ControllerType::Left:
+ return "Left Joycon";
+ case Joycon::ControllerType::Right:
+ return "Right Joycon";
+ case Joycon::ControllerType::Pro:
+ return "Pro Controller";
+ case Joycon::ControllerType::Dual:
+ return "Dual Joycon";
+ default:
+ return "Unknown Switch Controller";
+ }
+}
+} // namespace InputCommon
diff --git a/src/input_common/drivers/joycon.h b/src/input_common/drivers/joycon.h
new file mode 100644
index 000000000..473ba1b9e
--- /dev/null
+++ b/src/input_common/drivers/joycon.h
@@ -0,0 +1,112 @@
+// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#pragma once
+
+#include <array>
+#include <span>
+#include <thread>
+#include <SDL_hidapi.h>
+
+#include "input_common/input_engine.h"
+
+namespace InputCommon::Joycon {
+using SerialNumber = std::array<u8, 15>;
+struct Battery;
+struct Color;
+struct MotionData;
+enum class ControllerType : u8;
+enum class DriverResult;
+enum class IrsResolution;
+class JoyconDriver;
+} // namespace InputCommon::Joycon
+
+namespace InputCommon {
+
+class Joycons final : public InputCommon::InputEngine {
+public:
+ explicit Joycons(const std::string& input_engine_);
+
+ ~Joycons();
+
+ bool IsVibrationEnabled(const PadIdentifier& identifier) override;
+ Common::Input::DriverResult SetVibration(
+ const PadIdentifier& identifier, const Common::Input::VibrationStatus& vibration) override;
+
+ Common::Input::DriverResult SetLeds(const PadIdentifier& identifier,
+ const Common::Input::LedStatus& led_status) override;
+
+ Common::Input::DriverResult SetCameraFormat(const PadIdentifier& identifier,
+ Common::Input::CameraFormat camera_format) override;
+
+ Common::Input::NfcState SupportsNfc(const PadIdentifier& identifier_) const override;
+ Common::Input::NfcState WriteNfcData(const PadIdentifier& identifier_,
+ const std::vector<u8>& data) override;
+
+ Common::Input::DriverResult SetPollingMode(
+ const PadIdentifier& identifier, const Common::Input::PollingMode polling_mode) override;
+
+ /// Used for automapping features
+ std::vector<Common::ParamPackage> GetInputDevices() const override;
+ ButtonMapping GetButtonMappingForDevice(const Common::ParamPackage& params) override;
+ AnalogMapping GetAnalogMappingForDevice(const Common::ParamPackage& params) override;
+ MotionMapping GetMotionMappingForDevice(const Common::ParamPackage& params) override;
+ Common::Input::ButtonNames GetUIName(const Common::ParamPackage& params) const override;
+
+private:
+ static constexpr std::size_t MaxSupportedControllers = 8;
+
+ /// For shutting down, clear all data, join all threads, release usb devices
+ void Reset();
+
+ /// Registers controllers, clears all data and starts the scan thread
+ void Setup();
+
+ /// Actively searchs for new devices
+ void ScanThread(std::stop_token stop_token);
+
+ /// Returns true if device is valid and not registered
+ bool IsDeviceNew(SDL_hid_device_info* device_info) const;
+
+ /// Tries to connect to the new device
+ void RegisterNewDevice(SDL_hid_device_info* device_info);
+
+ /// Returns the next free handle
+ std::shared_ptr<Joycon::JoyconDriver> GetNextFreeHandle(Joycon::ControllerType type) const;
+
+ void OnBatteryUpdate(std::size_t port, Joycon::ControllerType type, Joycon::Battery value);
+ void OnColorUpdate(std::size_t port, Joycon::ControllerType type, const Joycon::Color& value);
+ void OnButtonUpdate(std::size_t port, Joycon::ControllerType type, int id, bool value);
+ void OnStickUpdate(std::size_t port, Joycon::ControllerType type, int id, f32 value);
+ void OnMotionUpdate(std::size_t port, Joycon::ControllerType type, int id,
+ const Joycon::MotionData& value);
+ void OnRingConUpdate(f32 ring_data);
+ void OnAmiiboUpdate(std::size_t port, const std::vector<u8>& amiibo_data);
+ void OnCameraUpdate(std::size_t port, const std::vector<u8>& camera_data,
+ Joycon::IrsResolution format);
+
+ /// Returns a JoyconHandle corresponding to a PadIdentifier
+ std::shared_ptr<Joycon::JoyconDriver> GetHandle(PadIdentifier identifier) const;
+
+ /// Returns a PadIdentifier corresponding to the port number and joycon type
+ PadIdentifier GetIdentifier(std::size_t port, Joycon::ControllerType type) const;
+
+ /// Returns a ParamPackage corresponding to the port number and joycon type
+ Common::ParamPackage GetParamPackage(std::size_t port, Joycon::ControllerType type) const;
+
+ std::string JoyconName(std::size_t port) const;
+
+ Common::Input::ButtonNames GetUIButtonName(const Common::ParamPackage& params) const;
+
+ /// Returns the name of the device in text format
+ std::string JoyconName(Joycon::ControllerType type) const;
+
+ std::jthread scan_thread;
+
+ // Joycon types are split by type to ease supporting dualjoycon configurations
+ std::array<std::shared_ptr<Joycon::JoyconDriver>, MaxSupportedControllers> left_joycons{};
+ std::array<std::shared_ptr<Joycon::JoyconDriver>, MaxSupportedControllers> right_joycons{};
+ std::array<std::shared_ptr<Joycon::JoyconDriver>, MaxSupportedControllers> pro_controller{};
+};
+
+} // namespace InputCommon
diff --git a/src/input_common/drivers/mouse.cpp b/src/input_common/drivers/mouse.cpp
index faf9cbdc3..da50e0a24 100644
--- a/src/input_common/drivers/mouse.cpp
+++ b/src/input_common/drivers/mouse.cpp
@@ -15,23 +15,39 @@ constexpr int mouse_axis_y = 1;
constexpr int wheel_axis_x = 2;
constexpr int wheel_axis_y = 3;
constexpr int motion_wheel_y = 4;
-constexpr int touch_axis_x = 10;
-constexpr int touch_axis_y = 11;
constexpr PadIdentifier identifier = {
.guid = Common::UUID{},
.port = 0,
.pad = 0,
};
+constexpr PadIdentifier real_mouse_identifier = {
+ .guid = Common::UUID{},
+ .port = 1,
+ .pad = 0,
+};
+
+constexpr PadIdentifier touch_identifier = {
+ .guid = Common::UUID{},
+ .port = 2,
+ .pad = 0,
+};
+
Mouse::Mouse(std::string input_engine_) : InputEngine(std::move(input_engine_)) {
PreSetController(identifier);
+ PreSetController(real_mouse_identifier);
+ PreSetController(touch_identifier);
+
+ // Initialize all mouse axis
PreSetAxis(identifier, mouse_axis_x);
PreSetAxis(identifier, mouse_axis_y);
PreSetAxis(identifier, wheel_axis_x);
PreSetAxis(identifier, wheel_axis_y);
PreSetAxis(identifier, motion_wheel_y);
- PreSetAxis(identifier, touch_axis_x);
- PreSetAxis(identifier, touch_axis_y);
+ PreSetAxis(real_mouse_identifier, mouse_axis_x);
+ PreSetAxis(real_mouse_identifier, mouse_axis_y);
+ PreSetAxis(touch_identifier, mouse_axis_x);
+ PreSetAxis(touch_identifier, mouse_axis_y);
update_thread = std::jthread([this](std::stop_token stop_token) { UpdateThread(stop_token); });
}
@@ -39,7 +55,7 @@ void Mouse::UpdateThread(std::stop_token stop_token) {
Common::SetCurrentThreadName("Mouse");
constexpr int update_time = 10;
while (!stop_token.stop_requested()) {
- if (Settings::values.mouse_panning && !Settings::values.mouse_enabled) {
+ if (Settings::values.mouse_panning) {
// Slow movement by 4%
last_mouse_change *= 0.96f;
const float sensitivity =
@@ -57,17 +73,7 @@ void Mouse::UpdateThread(std::stop_token stop_token) {
}
}
-void Mouse::MouseMove(int x, int y, f32 touch_x, f32 touch_y, int center_x, int center_y) {
- // If native mouse is enabled just set the screen coordinates
- if (Settings::values.mouse_enabled) {
- SetAxis(identifier, mouse_axis_x, touch_x);
- SetAxis(identifier, mouse_axis_y, touch_y);
- return;
- }
-
- SetAxis(identifier, touch_axis_x, touch_x);
- SetAxis(identifier, touch_axis_y, touch_y);
-
+void Mouse::Move(int x, int y, int center_x, int center_y) {
if (Settings::values.mouse_panning) {
auto mouse_change =
(Common::MakeVec(x, y) - Common::MakeVec(center_x, center_y)).Cast<float>();
@@ -113,20 +119,41 @@ void Mouse::MouseMove(int x, int y, f32 touch_x, f32 touch_y, int center_x, int
}
}
-void Mouse::PressButton(int x, int y, f32 touch_x, f32 touch_y, MouseButton button) {
- SetAxis(identifier, touch_axis_x, touch_x);
- SetAxis(identifier, touch_axis_y, touch_y);
+void Mouse::MouseMove(f32 touch_x, f32 touch_y) {
+ SetAxis(real_mouse_identifier, mouse_axis_x, touch_x);
+ SetAxis(real_mouse_identifier, mouse_axis_y, touch_y);
+}
+
+void Mouse::TouchMove(f32 touch_x, f32 touch_y) {
+ SetAxis(touch_identifier, mouse_axis_x, touch_x);
+ SetAxis(touch_identifier, mouse_axis_y, touch_y);
+}
+
+void Mouse::PressButton(int x, int y, MouseButton button) {
SetButton(identifier, static_cast<int>(button), true);
+
// Set initial analog parameters
mouse_origin = {x, y};
last_mouse_position = {x, y};
button_pressed = true;
}
+void Mouse::PressMouseButton(MouseButton button) {
+ SetButton(real_mouse_identifier, static_cast<int>(button), true);
+}
+
+void Mouse::PressTouchButton(f32 touch_x, f32 touch_y, MouseButton button) {
+ SetAxis(touch_identifier, mouse_axis_x, touch_x);
+ SetAxis(touch_identifier, mouse_axis_y, touch_y);
+ SetButton(touch_identifier, static_cast<int>(button), true);
+}
+
void Mouse::ReleaseButton(MouseButton button) {
SetButton(identifier, static_cast<int>(button), false);
+ SetButton(real_mouse_identifier, static_cast<int>(button), false);
+ SetButton(touch_identifier, static_cast<int>(button), false);
- if (!Settings::values.mouse_panning && !Settings::values.mouse_enabled) {
+ if (!Settings::values.mouse_panning) {
SetAxis(identifier, mouse_axis_x, 0);
SetAxis(identifier, mouse_axis_y, 0);
}
diff --git a/src/input_common/drivers/mouse.h b/src/input_common/drivers/mouse.h
index 72073cc23..f3b65bdd1 100644
--- a/src/input_common/drivers/mouse.h
+++ b/src/input_common/drivers/mouse.h
@@ -37,13 +37,43 @@ public:
* @param center_x the x-coordinate of the middle of the screen
* @param center_y the y-coordinate of the middle of the screen
*/
- void MouseMove(int x, int y, f32 touch_x, f32 touch_y, int center_x, int center_y);
+ void Move(int x, int y, int center_x, int center_y);
/**
- * Sets the status of all buttons bound with the key to pressed
- * @param key_code the code of the key to press
+ * Signals that real mouse has moved.
+ * @param x the absolute position on the touchscreen of the cursor
+ * @param y the absolute position on the touchscreen of the cursor
*/
- void PressButton(int x, int y, f32 touch_x, f32 touch_y, MouseButton button);
+ void MouseMove(f32 touch_x, f32 touch_y);
+
+ /**
+ * Signals that touch finger has moved.
+ * @param x the absolute position on the touchscreen of the cursor
+ * @param y the absolute position on the touchscreen of the cursor
+ */
+ void TouchMove(f32 touch_x, f32 touch_y);
+
+ /**
+ * Sets the status of a button to pressed
+ * @param x the x-coordinate of the cursor
+ * @param y the y-coordinate of the cursor
+ * @param button the id of the button to press
+ */
+ void PressButton(int x, int y, MouseButton button);
+
+ /**
+ * Sets the status of a mouse button to pressed
+ * @param button the id of the button to press
+ */
+ void PressMouseButton(MouseButton button);
+
+ /**
+ * Sets the status of touch finger to pressed
+ * @param x the absolute position on the touchscreen of the cursor
+ * @param y the absolute position on the touchscreen of the cursor
+ * @param button the id of the button to press
+ */
+ void PressTouchButton(f32 touch_x, f32 touch_y, MouseButton button);
/**
* Sets the status of all buttons bound with the key to released
diff --git a/src/input_common/drivers/sdl_driver.cpp b/src/input_common/drivers/sdl_driver.cpp
index 4818bb744..5c20b3426 100644
--- a/src/input_common/drivers/sdl_driver.cpp
+++ b/src/input_common/drivers/sdl_driver.cpp
@@ -40,25 +40,26 @@ public:
}
void EnableMotion() {
- if (sdl_controller) {
- SDL_GameController* controller = sdl_controller.get();
- has_accel = SDL_GameControllerHasSensor(controller, SDL_SENSOR_ACCEL) == SDL_TRUE;
- has_gyro = SDL_GameControllerHasSensor(controller, SDL_SENSOR_GYRO) == SDL_TRUE;
- if (has_accel) {
- SDL_GameControllerSetSensorEnabled(controller, SDL_SENSOR_ACCEL, SDL_TRUE);
- }
- if (has_gyro) {
- SDL_GameControllerSetSensorEnabled(controller, SDL_SENSOR_GYRO, SDL_TRUE);
- }
+ if (!sdl_controller) {
+ return;
+ }
+ SDL_GameController* controller = sdl_controller.get();
+ if (HasMotion()) {
+ SDL_GameControllerSetSensorEnabled(controller, SDL_SENSOR_ACCEL, SDL_FALSE);
+ SDL_GameControllerSetSensorEnabled(controller, SDL_SENSOR_GYRO, SDL_FALSE);
+ }
+ has_accel = SDL_GameControllerHasSensor(controller, SDL_SENSOR_ACCEL) == SDL_TRUE;
+ has_gyro = SDL_GameControllerHasSensor(controller, SDL_SENSOR_GYRO) == SDL_TRUE;
+ if (has_accel) {
+ SDL_GameControllerSetSensorEnabled(controller, SDL_SENSOR_ACCEL, SDL_TRUE);
+ }
+ if (has_gyro) {
+ SDL_GameControllerSetSensorEnabled(controller, SDL_SENSOR_GYRO, SDL_TRUE);
}
}
- bool HasGyro() const {
- return has_gyro;
- }
-
- bool HasAccel() const {
- return has_accel;
+ bool HasMotion() const {
+ return has_gyro || has_accel;
}
bool UpdateMotion(SDL_ControllerSensorEvent event) {
@@ -85,6 +86,20 @@ public:
if (time_difference == 0) {
return false;
}
+
+ // Motion data is invalid
+ if (motion.accel_x == 0 && motion.gyro_x == 0 && motion.accel_y == 0 &&
+ motion.gyro_y == 0 && motion.accel_z == 0 && motion.gyro_z == 0) {
+ if (motion_error_count++ < 200) {
+ return false;
+ }
+ // Try restarting the sensor
+ motion_error_count = 0;
+ EnableMotion();
+ return false;
+ }
+
+ motion_error_count = 0;
motion.delta_timestamp = time_difference * 1000;
return true;
}
@@ -250,6 +265,7 @@ private:
mutable std::mutex mutex;
u64 last_motion_update{};
+ std::size_t motion_error_count{};
bool has_gyro{false};
bool has_accel{false};
bool has_vibration{false};
@@ -318,6 +334,23 @@ void SDLDriver::InitJoystick(int joystick_index) {
const auto guid = GetGUID(sdl_joystick);
+ if (Settings::values.enable_joycon_driver) {
+ if (guid.uuid[5] == 0x05 && guid.uuid[4] == 0x7e &&
+ (guid.uuid[8] == 0x06 || guid.uuid[8] == 0x07)) {
+ LOG_WARNING(Input, "Preferring joycon driver for device index {}", joystick_index);
+ SDL_JoystickClose(sdl_joystick);
+ return;
+ }
+ }
+
+ if (Settings::values.enable_procon_driver) {
+ if (guid.uuid[5] == 0x05 && guid.uuid[4] == 0x7e && guid.uuid[8] == 0x09) {
+ LOG_WARNING(Input, "Preferring joycon driver for device index {}", joystick_index);
+ SDL_JoystickClose(sdl_joystick);
+ return;
+ }
+ }
+
std::scoped_lock lock{joystick_map_mutex};
if (joystick_map.find(guid) == joystick_map.end()) {
auto joystick = std::make_shared<SDLJoystick>(guid, 0, sdl_joystick, sdl_gamecontroller);
@@ -440,9 +473,19 @@ SDLDriver::SDLDriver(std::string input_engine_) : InputEngine(std::move(input_en
SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_PS5_RUMBLE, "1");
SDL_SetHint(SDL_HINT_JOYSTICK_ALLOW_BACKGROUND_EVENTS, "1");
- // Use hidapi driver for joycons. This will allow joycons to be detected as a GameController and
- // not a generic one
- SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_JOY_CONS, "1");
+ // Disable hidapi drivers for joycon controllers when the custom joycon driver is enabled
+ if (Settings::values.enable_joycon_driver) {
+ SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_JOY_CONS, "0");
+ } else {
+ SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_JOY_CONS, "1");
+ }
+
+ // Disable hidapi drivers for pro controllers when the custom joycon driver is enabled
+ if (Settings::values.enable_procon_driver) {
+ SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_SWITCH, "0");
+ } else {
+ SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_SWITCH, "1");
+ }
// Disable hidapi driver for xbox. Already default on Windows, this causes conflict with native
// driver on Linux.
@@ -532,7 +575,7 @@ std::vector<Common::ParamPackage> SDLDriver::GetInputDevices() const {
return devices;
}
-Common::Input::VibrationError SDLDriver::SetVibration(
+Common::Input::DriverResult SDLDriver::SetVibration(
const PadIdentifier& identifier, const Common::Input::VibrationStatus& vibration) {
const auto joystick =
GetSDLJoystickByGUID(identifier.guid.RawString(), static_cast<int>(identifier.port));
@@ -566,14 +609,14 @@ Common::Input::VibrationError SDLDriver::SetVibration(
.vibration = new_vibration,
});
- return Common::Input::VibrationError::None;
+ return Common::Input::DriverResult::Success;
}
bool SDLDriver::IsVibrationEnabled(const PadIdentifier& identifier) {
const auto joystick =
GetSDLJoystickByGUID(identifier.guid.RawString(), static_cast<int>(identifier.port));
- constexpr Common::Input::VibrationStatus test_vibration{
+ static constexpr Common::Input::VibrationStatus test_vibration{
.low_amplitude = 1,
.low_frequency = 160.0f,
.high_amplitude = 1,
@@ -581,7 +624,7 @@ bool SDLDriver::IsVibrationEnabled(const PadIdentifier& identifier) {
.type = Common::Input::VibrationAmplificationType::Exponential,
};
- constexpr Common::Input::VibrationStatus zero_vibration{
+ static constexpr Common::Input::VibrationStatus zero_vibration{
.low_amplitude = 0,
.low_frequency = 160.0f,
.high_amplitude = 0,
@@ -942,18 +985,18 @@ MotionMapping SDLDriver::GetMotionMappingForDevice(const Common::ParamPackage& p
MotionMapping mapping = {};
joystick->EnableMotion();
- if (joystick->HasGyro() || joystick->HasAccel()) {
+ if (joystick->HasMotion()) {
mapping.insert_or_assign(Settings::NativeMotion::MotionRight,
BuildMotionParam(joystick->GetPort(), joystick->GetGUID()));
}
if (params.Has("guid2")) {
joystick2->EnableMotion();
- if (joystick2->HasGyro() || joystick2->HasAccel()) {
+ if (joystick2->HasMotion()) {
mapping.insert_or_assign(Settings::NativeMotion::MotionLeft,
BuildMotionParam(joystick2->GetPort(), joystick2->GetGUID()));
}
} else {
- if (joystick->HasGyro() || joystick->HasAccel()) {
+ if (joystick->HasMotion()) {
mapping.insert_or_assign(Settings::NativeMotion::MotionLeft,
BuildMotionParam(joystick->GetPort(), joystick->GetGUID()));
}
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:
bool IsStickInverted(const Common::ParamPackage& params) override;
- Common::Input::VibrationError SetVibration(
+ Common::Input::DriverResult SetVibration(
const PadIdentifier& identifier, const Common::Input::VibrationStatus& vibration) override;
bool IsVibrationEnabled(const PadIdentifier& identifier) override;
diff --git a/src/input_common/drivers/tas_input.cpp b/src/input_common/drivers/tas_input.cpp
index f3ade90da..f3cb14c56 100644
--- a/src/input_common/drivers/tas_input.cpp
+++ b/src/input_common/drivers/tas_input.cpp
@@ -156,10 +156,12 @@ void Tas::RecordInput(u64 buttons, TasAnalog left_axis, TasAnalog right_axis) {
};
}
-std::tuple<TasState, size_t, size_t> Tas::GetStatus() const {
+std::tuple<TasState, size_t, std::array<size_t, PLAYER_NUMBER>> Tas::GetStatus() const {
TasState state;
+ std::array<size_t, PLAYER_NUMBER> lengths{0};
if (is_recording) {
- return {TasState::Recording, 0, record_commands.size()};
+ lengths[0] = record_commands.size();
+ return {TasState::Recording, record_commands.size(), lengths};
}
if (is_running) {
@@ -168,7 +170,11 @@ std::tuple<TasState, size_t, size_t> Tas::GetStatus() const {
state = TasState::Stopped;
}
- return {state, current_command, script_length};
+ for (size_t i = 0; i < PLAYER_NUMBER; i++) {
+ lengths[i] = commands[i].size();
+ }
+
+ return {state, current_command, lengths};
}
void Tas::UpdateThread() {
diff --git a/src/input_common/drivers/tas_input.h b/src/input_common/drivers/tas_input.h
index 38a27a230..5be66d142 100644
--- a/src/input_common/drivers/tas_input.h
+++ b/src/input_common/drivers/tas_input.h
@@ -124,7 +124,7 @@ public:
* Current playback progress ;
* Total length of script file currently loaded or being recorded
*/
- std::tuple<TasState, size_t, size_t> GetStatus() const;
+ std::tuple<TasState, size_t, std::array<size_t, PLAYER_NUMBER>> GetStatus() const;
private:
enum class TasAxis : u8;
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(
VirtualAmiibo::~VirtualAmiibo() = default;
-Common::Input::PollingError VirtualAmiibo::SetPollingMode(
+Common::Input::DriverResult VirtualAmiibo::SetPollingMode(
[[maybe_unused]] const PadIdentifier& identifier_,
const Common::Input::PollingMode polling_mode_) {
polling_mode = polling_mode_;
- if (polling_mode == Common::Input::PollingMode::NFC) {
+ switch (polling_mode) {
+ case Common::Input::PollingMode::NFC:
if (state == State::Initialized) {
state = State::WaitingForAmiibo;
}
- } else {
+ return Common::Input::DriverResult::Success;
+ default:
if (state == State::AmiiboIsOpen) {
CloseAmiibo();
}
+ return Common::Input::DriverResult::NotSupported;
}
-
- return Common::Input::PollingError::None;
}
Common::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:
~VirtualAmiibo() override;
// Sets polling mode to a controller
- Common::Input::PollingError SetPollingMode(
+ Common::Input::DriverResult SetPollingMode(
const PadIdentifier& identifier_, const Common::Input::PollingMode polling_mode_) override;
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..e65b6b845
--- /dev/null
+++ b/src/input_common/helpers/joycon_driver.cpp
@@ -0,0 +1,576 @@
+// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include "common/logging/log.h"
+#include "common/swap.h"
+#include "common/thread.h"
+#include "input_common/helpers/joycon_driver.h"
+#include "input_common/helpers/joycon_protocol/calibration.h"
+#include "input_common/helpers/joycon_protocol/generic_functions.h"
+#include "input_common/helpers/joycon_protocol/irs.h"
+#include "input_common/helpers/joycon_protocol/nfc.h"
+#include "input_common/helpers/joycon_protocol/poller.h"
+#include "input_common/helpers/joycon_protocol/ringcon.h"
+#include "input_common/helpers/joycon_protocol/rumble.h"
+
+namespace InputCommon::Joycon {
+JoyconDriver::JoyconDriver(std::size_t port_) : port{port_} {
+ hidapi_handle = std::make_shared<JoyconHandle>();
+}
+
+JoyconDriver::~JoyconDriver() {
+ Stop();
+}
+
+void JoyconDriver::Stop() {
+ is_connected = false;
+ input_thread = {};
+}
+
+DriverResult JoyconDriver::RequestDeviceAccess(SDL_hid_device_info* device_info) {
+ std::scoped_lock lock{mutex};
+
+ handle_device_type = ControllerType::None;
+ GetDeviceType(device_info, handle_device_type);
+ if (handle_device_type == ControllerType::None) {
+ return DriverResult::UnsupportedControllerType;
+ }
+
+ hidapi_handle->handle =
+ SDL_hid_open(device_info->vendor_id, device_info->product_id, device_info->serial_number);
+ std::memcpy(&handle_serial_number, device_info->serial_number, 15);
+ if (!hidapi_handle->handle) {
+ LOG_ERROR(Input, "Yuzu can't gain access to this device: ID {:04X}:{:04X}.",
+ device_info->vendor_id, device_info->product_id);
+ return DriverResult::HandleInUse;
+ }
+ SDL_hid_set_nonblocking(hidapi_handle->handle, 1);
+ return DriverResult::Success;
+}
+
+DriverResult JoyconDriver::InitializeDevice() {
+ if (!hidapi_handle->handle) {
+ return DriverResult::InvalidHandle;
+ }
+ std::scoped_lock lock{mutex};
+ disable_input_thread = true;
+
+ // Reset Counters
+ error_counter = 0;
+ hidapi_handle->packet_counter = 0;
+
+ // Reset external device status
+ starlink_connected = false;
+ ring_connected = false;
+ amiibo_detected = false;
+
+ // Set HW default configuration
+ vibration_enabled = true;
+ motion_enabled = true;
+ hidbus_enabled = false;
+ nfc_enabled = false;
+ passive_enabled = false;
+ irs_enabled = false;
+ gyro_sensitivity = Joycon::GyroSensitivity::DPS2000;
+ gyro_performance = Joycon::GyroPerformance::HZ833;
+ accelerometer_sensitivity = Joycon::AccelerometerSensitivity::G8;
+ accelerometer_performance = Joycon::AccelerometerPerformance::HZ100;
+
+ // Initialize HW Protocols
+ calibration_protocol = std::make_unique<CalibrationProtocol>(hidapi_handle);
+ generic_protocol = std::make_unique<GenericProtocol>(hidapi_handle);
+ irs_protocol = std::make_unique<IrsProtocol>(hidapi_handle);
+ nfc_protocol = std::make_unique<NfcProtocol>(hidapi_handle);
+ ring_protocol = std::make_unique<RingConProtocol>(hidapi_handle);
+ rumble_protocol = std::make_unique<RumbleProtocol>(hidapi_handle);
+
+ // Get fixed joycon info
+ generic_protocol->GetVersionNumber(version);
+ generic_protocol->SetLowPowerMode(false);
+ generic_protocol->GetColor(color);
+ if (handle_device_type == ControllerType::Pro) {
+ // Some 3rd party controllers aren't pro controllers
+ generic_protocol->GetControllerType(device_type);
+ } else {
+ device_type = handle_device_type;
+ }
+ generic_protocol->GetSerialNumber(serial_number);
+ supported_features = GetSupportedFeatures();
+
+ // Get Calibration data
+ calibration_protocol->GetLeftJoyStickCalibration(left_stick_calibration);
+ calibration_protocol->GetRightJoyStickCalibration(right_stick_calibration);
+ calibration_protocol->GetImuCalibration(motion_calibration);
+
+ // Set led status
+ generic_protocol->SetLedBlinkPattern(static_cast<u8>(1 + port));
+
+ // Apply HW configuration
+ SetPollingMode();
+
+ // Initialize joycon poller
+ joycon_poller = std::make_unique<JoyconPoller>(device_type, left_stick_calibration,
+ right_stick_calibration, motion_calibration);
+
+ // Start pooling for data
+ is_connected = true;
+ if (!input_thread_running) {
+ input_thread =
+ std::jthread([this](std::stop_token stop_token) { InputThread(stop_token); });
+ }
+
+ disable_input_thread = false;
+ return DriverResult::Success;
+}
+
+void JoyconDriver::InputThread(std::stop_token stop_token) {
+ LOG_INFO(Input, "Joycon Adapter input thread started");
+ Common::SetCurrentThreadName("JoyconInput");
+ input_thread_running = true;
+
+ // Max update rate is 5ms, ensure we are always able to read a bit faster
+ constexpr int ThreadDelay = 2;
+ std::vector<u8> buffer(MaxBufferSize);
+
+ while (!stop_token.stop_requested()) {
+ int status = 0;
+
+ if (!IsInputThreadValid()) {
+ input_thread.request_stop();
+ continue;
+ }
+
+ // By disabling the input thread we can ensure custom commands will succeed as no package is
+ // skipped
+ if (!disable_input_thread) {
+ status = SDL_hid_read_timeout(hidapi_handle->handle, buffer.data(), buffer.size(),
+ ThreadDelay);
+ } else {
+ std::this_thread::sleep_for(std::chrono::milliseconds(ThreadDelay));
+ }
+
+ if (IsPayloadCorrect(status, buffer)) {
+ OnNewData(buffer);
+ }
+
+ std::this_thread::yield();
+ }
+
+ is_connected = false;
+ input_thread_running = false;
+ LOG_INFO(Input, "Joycon Adapter input thread stopped");
+}
+
+void JoyconDriver::OnNewData(std::span<u8> buffer) {
+ const auto report_mode = static_cast<ReportMode>(buffer[0]);
+
+ // Packages can be a litte bit inconsistent. Average the delta time to provide a smoother motion
+ // experience
+ switch (report_mode) {
+ case ReportMode::STANDARD_FULL_60HZ:
+ case ReportMode::NFC_IR_MODE_60HZ:
+ case ReportMode::SIMPLE_HID_MODE: {
+ const auto now = std::chrono::steady_clock::now();
+ const auto new_delta_time = static_cast<u64>(
+ std::chrono::duration_cast<std::chrono::microseconds>(now - last_update).count());
+ delta_time = ((delta_time * 8) + (new_delta_time * 2)) / 10;
+ last_update = now;
+ joycon_poller->UpdateColor(color);
+ break;
+ }
+ default:
+ break;
+ }
+
+ const MotionStatus motion_status{
+ .is_enabled = motion_enabled,
+ .delta_time = delta_time,
+ .gyro_sensitivity = gyro_sensitivity,
+ .accelerometer_sensitivity = accelerometer_sensitivity,
+ };
+
+ // TODO: Remove this when calibration is properly loaded and not calculated
+ if (ring_connected && report_mode == ReportMode::STANDARD_FULL_60HZ) {
+ InputReportActive data{};
+ memcpy(&data, buffer.data(), sizeof(InputReportActive));
+ calibration_protocol->GetRingCalibration(ring_calibration, data.ring_input);
+ }
+
+ const RingStatus ring_status{
+ .is_enabled = ring_connected,
+ .default_value = ring_calibration.default_value,
+ .max_value = ring_calibration.max_value,
+ .min_value = ring_calibration.min_value,
+ };
+
+ if (irs_protocol->IsEnabled()) {
+ irs_protocol->RequestImage(buffer);
+ joycon_poller->UpdateCamera(irs_protocol->GetImage(), irs_protocol->GetIrsFormat());
+ }
+
+ if (nfc_protocol->IsEnabled()) {
+ if (amiibo_detected) {
+ if (!nfc_protocol->HasAmiibo()) {
+ joycon_poller->UpdateAmiibo({});
+ amiibo_detected = false;
+ return;
+ }
+ }
+
+ if (!amiibo_detected) {
+ std::vector<u8> data(0x21C);
+ const auto result = nfc_protocol->ScanAmiibo(data);
+ if (result == DriverResult::Success) {
+ joycon_poller->UpdateAmiibo(data);
+ amiibo_detected = true;
+ }
+ }
+ }
+
+ switch (report_mode) {
+ case ReportMode::STANDARD_FULL_60HZ:
+ joycon_poller->ReadActiveMode(buffer, motion_status, ring_status);
+ break;
+ case ReportMode::NFC_IR_MODE_60HZ:
+ joycon_poller->ReadNfcIRMode(buffer, motion_status);
+ break;
+ case ReportMode::SIMPLE_HID_MODE:
+ joycon_poller->ReadPassiveMode(buffer);
+ break;
+ case ReportMode::SUBCMD_REPLY:
+ LOG_DEBUG(Input, "Unhandled command reply");
+ break;
+ default:
+ LOG_ERROR(Input, "Report mode not Implemented {}", report_mode);
+ break;
+ }
+}
+
+DriverResult JoyconDriver::SetPollingMode() {
+ disable_input_thread = true;
+
+ rumble_protocol->EnableRumble(vibration_enabled && supported_features.vibration);
+
+ if (motion_enabled && supported_features.motion) {
+ generic_protocol->EnableImu(true);
+ generic_protocol->SetImuConfig(gyro_sensitivity, gyro_performance,
+ accelerometer_sensitivity, accelerometer_performance);
+ } else {
+ generic_protocol->EnableImu(false);
+ }
+
+ if (irs_protocol->IsEnabled()) {
+ irs_protocol->DisableIrs();
+ }
+
+ if (nfc_protocol->IsEnabled()) {
+ amiibo_detected = false;
+ nfc_protocol->DisableNfc();
+ }
+
+ if (ring_protocol->IsEnabled()) {
+ ring_connected = false;
+ ring_protocol->DisableRingCon();
+ }
+
+ if (irs_enabled && supported_features.irs) {
+ auto result = irs_protocol->EnableIrs();
+ if (result == DriverResult::Success) {
+ disable_input_thread = false;
+ return result;
+ }
+ irs_protocol->DisableIrs();
+ LOG_ERROR(Input, "Error enabling IRS");
+ }
+
+ if (nfc_enabled && supported_features.nfc) {
+ auto result = nfc_protocol->EnableNfc();
+ if (result == DriverResult::Success) {
+ result = nfc_protocol->StartNFCPollingMode();
+ }
+ if (result == DriverResult::Success) {
+ disable_input_thread = false;
+ return result;
+ }
+ nfc_protocol->DisableNfc();
+ LOG_ERROR(Input, "Error enabling NFC");
+ }
+
+ if (hidbus_enabled && supported_features.hidbus) {
+ auto result = ring_protocol->EnableRingCon();
+ if (result == DriverResult::Success) {
+ result = ring_protocol->StartRingconPolling();
+ }
+ if (result == DriverResult::Success) {
+ ring_connected = true;
+ disable_input_thread = false;
+ return result;
+ }
+ ring_connected = false;
+ ring_protocol->DisableRingCon();
+ LOG_ERROR(Input, "Error enabling Ringcon");
+ }
+
+ if (passive_enabled && supported_features.passive) {
+ const auto result = generic_protocol->EnablePassiveMode();
+ if (result == DriverResult::Success) {
+ disable_input_thread = false;
+ return result;
+ }
+ LOG_ERROR(Input, "Error enabling passive mode");
+ }
+
+ // Default Mode
+ const auto result = generic_protocol->EnableActiveMode();
+ if (result != DriverResult::Success) {
+ LOG_ERROR(Input, "Error enabling active mode");
+ }
+ // Switch calls this function after enabling active mode
+ generic_protocol->TriggersElapsed();
+
+ disable_input_thread = false;
+ return result;
+}
+
+JoyconDriver::SupportedFeatures JoyconDriver::GetSupportedFeatures() {
+ SupportedFeatures features{
+ .passive = true,
+ .motion = true,
+ .vibration = true,
+ };
+
+ if (device_type == ControllerType::Right) {
+ features.nfc = true;
+ features.irs = true;
+ features.hidbus = true;
+ }
+
+ if (device_type == ControllerType::Pro) {
+ features.nfc = true;
+ }
+ return features;
+}
+
+bool JoyconDriver::IsInputThreadValid() const {
+ if (!is_connected.load()) {
+ return false;
+ }
+ if (hidapi_handle->handle == nullptr) {
+ return false;
+ }
+ // Controller is not responding. Terminate connection
+ if (error_counter > MaxErrorCount) {
+ return false;
+ }
+ return true;
+}
+
+bool JoyconDriver::IsPayloadCorrect(int status, std::span<const u8> buffer) {
+ if (status <= -1) {
+ error_counter++;
+ return false;
+ }
+ // There's no new data
+ if (status == 0) {
+ return false;
+ }
+ // No reply ever starts with zero
+ if (buffer[0] == 0x00) {
+ error_counter++;
+ return false;
+ }
+ error_counter = 0;
+ return true;
+}
+
+DriverResult JoyconDriver::SetVibration(const VibrationValue& vibration) {
+ std::scoped_lock lock{mutex};
+ if (disable_input_thread) {
+ return DriverResult::HandleInUse;
+ }
+ return rumble_protocol->SendVibration(vibration);
+}
+
+DriverResult JoyconDriver::SetLedConfig(u8 led_pattern) {
+ std::scoped_lock lock{mutex};
+ if (disable_input_thread) {
+ return DriverResult::HandleInUse;
+ }
+ return generic_protocol->SetLedPattern(led_pattern);
+}
+
+DriverResult JoyconDriver::SetIrsConfig(IrsMode mode_, IrsResolution format_) {
+ std::scoped_lock lock{mutex};
+ if (disable_input_thread) {
+ return DriverResult::HandleInUse;
+ }
+ disable_input_thread = true;
+ const auto result = irs_protocol->SetIrsConfig(mode_, format_);
+ disable_input_thread = false;
+ return result;
+}
+
+DriverResult JoyconDriver::SetPasiveMode() {
+ std::scoped_lock lock{mutex};
+ motion_enabled = false;
+ hidbus_enabled = false;
+ nfc_enabled = false;
+ passive_enabled = true;
+ irs_enabled = false;
+ return SetPollingMode();
+}
+
+DriverResult JoyconDriver::SetActiveMode() {
+ if (is_ring_disabled_by_irs) {
+ is_ring_disabled_by_irs = false;
+ SetActiveMode();
+ return SetRingConMode();
+ }
+
+ std::scoped_lock lock{mutex};
+ motion_enabled = true;
+ hidbus_enabled = false;
+ nfc_enabled = false;
+ passive_enabled = false;
+ irs_enabled = false;
+ return SetPollingMode();
+}
+
+DriverResult JoyconDriver::SetIrMode() {
+ std::scoped_lock lock{mutex};
+
+ if (!supported_features.irs) {
+ return DriverResult::NotSupported;
+ }
+
+ if (ring_connected) {
+ is_ring_disabled_by_irs = true;
+ }
+
+ motion_enabled = false;
+ hidbus_enabled = false;
+ nfc_enabled = false;
+ passive_enabled = false;
+ irs_enabled = true;
+ return SetPollingMode();
+}
+
+DriverResult JoyconDriver::SetNfcMode() {
+ std::scoped_lock lock{mutex};
+
+ if (!supported_features.nfc) {
+ return DriverResult::NotSupported;
+ }
+
+ motion_enabled = true;
+ hidbus_enabled = false;
+ nfc_enabled = true;
+ passive_enabled = false;
+ irs_enabled = false;
+ return SetPollingMode();
+}
+
+DriverResult JoyconDriver::SetRingConMode() {
+ std::scoped_lock lock{mutex};
+
+ if (!supported_features.hidbus) {
+ return DriverResult::NotSupported;
+ }
+
+ motion_enabled = true;
+ hidbus_enabled = true;
+ nfc_enabled = false;
+ passive_enabled = false;
+ irs_enabled = false;
+
+ const auto result = SetPollingMode();
+
+ if (!ring_connected) {
+ return DriverResult::NoDeviceDetected;
+ }
+
+ return result;
+}
+
+bool JoyconDriver::IsConnected() const {
+ std::scoped_lock lock{mutex};
+ return is_connected.load();
+}
+
+bool JoyconDriver::IsVibrationEnabled() const {
+ std::scoped_lock lock{mutex};
+ return vibration_enabled;
+}
+
+FirmwareVersion JoyconDriver::GetDeviceVersion() const {
+ std::scoped_lock lock{mutex};
+ return version;
+}
+
+Color JoyconDriver::GetDeviceColor() const {
+ std::scoped_lock lock{mutex};
+ return color;
+}
+
+std::size_t JoyconDriver::GetDevicePort() const {
+ std::scoped_lock lock{mutex};
+ return port;
+}
+
+ControllerType JoyconDriver::GetDeviceType() const {
+ std::scoped_lock lock{mutex};
+ return device_type;
+}
+
+ControllerType JoyconDriver::GetHandleDeviceType() const {
+ std::scoped_lock lock{mutex};
+ return handle_device_type;
+}
+
+SerialNumber JoyconDriver::GetSerialNumber() const {
+ std::scoped_lock lock{mutex};
+ return serial_number;
+}
+
+SerialNumber JoyconDriver::GetHandleSerialNumber() const {
+ std::scoped_lock lock{mutex};
+ return handle_serial_number;
+}
+
+void JoyconDriver::SetCallbacks(const JoyconCallbacks& callbacks) {
+ joycon_poller->SetCallbacks(callbacks);
+}
+
+DriverResult JoyconDriver::GetDeviceType(SDL_hid_device_info* device_info,
+ ControllerType& controller_type) {
+ static constexpr std::array<std::pair<u32, ControllerType>, 6> supported_devices{
+ std::pair<u32, ControllerType>{0x2006, ControllerType::Left},
+ {0x2007, ControllerType::Right},
+ {0x2009, ControllerType::Pro},
+ };
+ constexpr u16 nintendo_vendor_id = 0x057e;
+
+ controller_type = ControllerType::None;
+ if (device_info->vendor_id != nintendo_vendor_id) {
+ return DriverResult::UnsupportedControllerType;
+ }
+
+ for (const auto& [product_id, type] : supported_devices) {
+ if (device_info->product_id == static_cast<u16>(product_id)) {
+ controller_type = type;
+ return Joycon::DriverResult::Success;
+ }
+ }
+ return Joycon::DriverResult::UnsupportedControllerType;
+}
+
+DriverResult JoyconDriver::GetSerialNumber(SDL_hid_device_info* device_info,
+ SerialNumber& serial_number) {
+ if (device_info->serial_number == nullptr) {
+ return DriverResult::Unknown;
+ }
+ std::memcpy(&serial_number, device_info->serial_number, 15);
+ return Joycon::DriverResult::Success;
+}
+
+} // 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 @@
+// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#pragma once
+
+#include <atomic>
+#include <functional>
+#include <mutex>
+#include <span>
+#include <thread>
+
+#include "input_common/helpers/joycon_protocol/joycon_types.h"
+
+namespace InputCommon::Joycon {
+class CalibrationProtocol;
+class GenericProtocol;
+class IrsProtocol;
+class NfcProtocol;
+class JoyconPoller;
+class RingConProtocol;
+class RumbleProtocol;
+
+class JoyconDriver final {
+public:
+ explicit JoyconDriver(std::size_t port_);
+
+ ~JoyconDriver();
+
+ DriverResult RequestDeviceAccess(SDL_hid_device_info* device_info);
+ DriverResult InitializeDevice();
+ void Stop();
+
+ bool IsConnected() const;
+ bool IsVibrationEnabled() const;
+
+ FirmwareVersion GetDeviceVersion() const;
+ Color GetDeviceColor() const;
+ std::size_t GetDevicePort() const;
+ ControllerType GetDeviceType() const;
+ ControllerType GetHandleDeviceType() const;
+ SerialNumber GetSerialNumber() const;
+ SerialNumber GetHandleSerialNumber() const;
+
+ DriverResult SetVibration(const VibrationValue& vibration);
+ DriverResult SetLedConfig(u8 led_pattern);
+ DriverResult SetIrsConfig(IrsMode mode_, IrsResolution format_);
+ DriverResult SetPasiveMode();
+ DriverResult SetActiveMode();
+ DriverResult SetIrMode();
+ DriverResult SetNfcMode();
+ DriverResult SetRingConMode();
+
+ void SetCallbacks(const JoyconCallbacks& callbacks);
+
+ // Returns device type from hidapi handle
+ static DriverResult GetDeviceType(SDL_hid_device_info* device_info,
+ ControllerType& controller_type);
+
+ // Returns serial number from hidapi handle
+ static DriverResult GetSerialNumber(SDL_hid_device_info* device_info,
+ SerialNumber& serial_number);
+
+private:
+ struct SupportedFeatures {
+ bool passive{};
+ bool hidbus{};
+ bool irs{};
+ bool motion{};
+ bool nfc{};
+ bool vibration{};
+ };
+
+ /// Main thread, actively request new data from the handle
+ void InputThread(std::stop_token stop_token);
+
+ /// Called everytime a valid package arrives
+ void OnNewData(std::span<u8> buffer);
+
+ /// Updates device configuration to enable or disable features
+ DriverResult SetPollingMode();
+
+ /// Returns true if input thread is valid and doesn't need to be stopped
+ bool IsInputThreadValid() const;
+
+ /// Returns true if the data should be interpreted. Otherwise the error counter is incremented
+ bool IsPayloadCorrect(int status, std::span<const u8> buffer);
+
+ /// Returns a list of supported features that can be enabled on this device
+ SupportedFeatures GetSupportedFeatures();
+
+ // Protocol Features
+ std::unique_ptr<CalibrationProtocol> calibration_protocol;
+ std::unique_ptr<GenericProtocol> generic_protocol;
+ std::unique_ptr<IrsProtocol> irs_protocol;
+ std::unique_ptr<NfcProtocol> nfc_protocol;
+ std::unique_ptr<JoyconPoller> joycon_poller;
+ std::unique_ptr<RingConProtocol> ring_protocol;
+ std::unique_ptr<RumbleProtocol> rumble_protocol;
+
+ // Connection status
+ std::atomic<bool> is_connected{};
+ u64 delta_time;
+ std::size_t error_counter{};
+ std::shared_ptr<JoyconHandle> hidapi_handle;
+ std::chrono::time_point<std::chrono::steady_clock> last_update;
+
+ // External device status
+ bool starlink_connected{};
+ bool ring_connected{};
+ bool amiibo_detected{};
+ bool is_ring_disabled_by_irs{};
+
+ // Harware configuration
+ u8 leds{};
+ ReportMode mode{};
+ bool passive_enabled{}; // Low power mode, Ideal for multiple controllers at the same time
+ bool hidbus_enabled{}; // External device support
+ bool irs_enabled{}; // Infrared camera input
+ bool motion_enabled{}; // Enables motion input
+ bool nfc_enabled{}; // Enables Amiibo detection
+ bool vibration_enabled{}; // Allows vibrations
+
+ // Calibration data
+ GyroSensitivity gyro_sensitivity{};
+ GyroPerformance gyro_performance{};
+ AccelerometerSensitivity accelerometer_sensitivity{};
+ AccelerometerPerformance accelerometer_performance{};
+ JoyStickCalibration left_stick_calibration{};
+ JoyStickCalibration right_stick_calibration{};
+ MotionCalibration motion_calibration{};
+ RingCalibration ring_calibration{};
+
+ // Fixed joycon info
+ FirmwareVersion version{};
+ Color color{};
+ std::size_t port{};
+ ControllerType device_type{}; // Device type reported by controller
+ ControllerType handle_device_type{}; // Device type reported by hidapi
+ SerialNumber serial_number{}; // Serial number reported by controller
+ SerialNumber handle_serial_number{}; // Serial number type reported by hidapi
+ SupportedFeatures supported_features{};
+
+ // Thread related
+ mutable std::mutex mutex;
+ std::jthread input_thread;
+ bool input_thread_running{};
+ bool disable_input_thread{};
+};
+
+} // 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 @@
+// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include <cstring>
+
+#include "input_common/helpers/joycon_protocol/calibration.h"
+#include "input_common/helpers/joycon_protocol/joycon_types.h"
+
+namespace InputCommon::Joycon {
+
+CalibrationProtocol::CalibrationProtocol(std::shared_ptr<JoyconHandle> handle)
+ : JoyconCommonProtocol(std::move(handle)) {}
+
+DriverResult CalibrationProtocol::GetLeftJoyStickCalibration(JoyStickCalibration& calibration) {
+ ScopedSetBlocking sb(this);
+ DriverResult result{DriverResult::Success};
+ JoystickLeftSpiCalibration spi_calibration{};
+ bool has_user_calibration = false;
+ calibration = {};
+
+ if (result == DriverResult::Success) {
+ result = HasUserCalibration(SpiAddress::USER_LEFT_MAGIC, has_user_calibration);
+ }
+
+ // Read User defined calibration
+ if (result == DriverResult::Success && has_user_calibration) {
+ result = ReadSPI(SpiAddress::USER_LEFT_DATA, spi_calibration);
+ }
+
+ // Read Factory calibration
+ if (result == DriverResult::Success && !has_user_calibration) {
+ result = ReadSPI(SpiAddress::FACT_LEFT_DATA, spi_calibration);
+ }
+
+ if (result == DriverResult::Success) {
+ calibration.x.center = GetXAxisCalibrationValue(spi_calibration.center);
+ calibration.y.center = GetYAxisCalibrationValue(spi_calibration.center);
+ calibration.x.min = GetXAxisCalibrationValue(spi_calibration.min);
+ calibration.y.min = GetYAxisCalibrationValue(spi_calibration.min);
+ calibration.x.max = GetXAxisCalibrationValue(spi_calibration.max);
+ calibration.y.max = GetYAxisCalibrationValue(spi_calibration.max);
+ }
+
+ // Set a valid default calibration if data is missing
+ ValidateCalibration(calibration);
+
+ return result;
+}
+
+DriverResult CalibrationProtocol::GetRightJoyStickCalibration(JoyStickCalibration& calibration) {
+ ScopedSetBlocking sb(this);
+ DriverResult result{DriverResult::Success};
+ JoystickRightSpiCalibration spi_calibration{};
+ bool has_user_calibration = false;
+ calibration = {};
+
+ if (result == DriverResult::Success) {
+ result = HasUserCalibration(SpiAddress::USER_RIGHT_MAGIC, has_user_calibration);
+ }
+
+ // Read User defined calibration
+ if (result == DriverResult::Success && has_user_calibration) {
+ result = ReadSPI(SpiAddress::USER_RIGHT_DATA, spi_calibration);
+ }
+
+ // Read Factory calibration
+ if (result == DriverResult::Success && !has_user_calibration) {
+ result = ReadSPI(SpiAddress::FACT_RIGHT_DATA, spi_calibration);
+ }
+
+ if (result == DriverResult::Success) {
+ calibration.x.center = GetXAxisCalibrationValue(spi_calibration.center);
+ calibration.y.center = GetYAxisCalibrationValue(spi_calibration.center);
+ calibration.x.min = GetXAxisCalibrationValue(spi_calibration.min);
+ calibration.y.min = GetYAxisCalibrationValue(spi_calibration.min);
+ calibration.x.max = GetXAxisCalibrationValue(spi_calibration.max);
+ calibration.y.max = GetYAxisCalibrationValue(spi_calibration.max);
+ }
+
+ // Set a valid default calibration if data is missing
+ ValidateCalibration(calibration);
+
+ return result;
+}
+
+DriverResult CalibrationProtocol::GetImuCalibration(MotionCalibration& calibration) {
+ ScopedSetBlocking sb(this);
+ DriverResult result{DriverResult::Success};
+ ImuSpiCalibration spi_calibration{};
+ bool has_user_calibration = false;
+ calibration = {};
+
+ if (result == DriverResult::Success) {
+ result = HasUserCalibration(SpiAddress::USER_IMU_MAGIC, has_user_calibration);
+ }
+
+ // Read User defined calibration
+ if (result == DriverResult::Success && has_user_calibration) {
+ result = ReadSPI(SpiAddress::USER_IMU_DATA, spi_calibration);
+ }
+
+ // Read Factory calibration
+ if (result == DriverResult::Success && !has_user_calibration) {
+ result = ReadSPI(SpiAddress::FACT_IMU_DATA, spi_calibration);
+ }
+
+ if (result == DriverResult::Success) {
+ calibration.accelerometer[0].offset = spi_calibration.accelerometer_offset[0];
+ calibration.accelerometer[1].offset = spi_calibration.accelerometer_offset[1];
+ calibration.accelerometer[2].offset = spi_calibration.accelerometer_offset[2];
+
+ calibration.accelerometer[0].scale = spi_calibration.accelerometer_scale[0];
+ calibration.accelerometer[1].scale = spi_calibration.accelerometer_scale[1];
+ calibration.accelerometer[2].scale = spi_calibration.accelerometer_scale[2];
+
+ calibration.gyro[0].offset = spi_calibration.gyroscope_offset[0];
+ calibration.gyro[1].offset = spi_calibration.gyroscope_offset[1];
+ calibration.gyro[2].offset = spi_calibration.gyroscope_offset[2];
+
+ calibration.gyro[0].scale = spi_calibration.gyroscope_scale[0];
+ calibration.gyro[1].scale = spi_calibration.gyroscope_scale[1];
+ calibration.gyro[2].scale = spi_calibration.gyroscope_scale[2];
+ }
+
+ ValidateCalibration(calibration);
+
+ return result;
+}
+
+DriverResult CalibrationProtocol::GetRingCalibration(RingCalibration& calibration,
+ s16 current_value) {
+ constexpr s16 DefaultRingRange{800};
+
+ // TODO: Get default calibration form ring itself
+ if (ring_data_max == 0 && ring_data_min == 0) {
+ ring_data_max = current_value + DefaultRingRange;
+ ring_data_min = current_value - DefaultRingRange;
+ ring_data_default = current_value;
+ }
+ ring_data_max = std::max(ring_data_max, current_value);
+ ring_data_min = std::min(ring_data_min, current_value);
+ calibration = {
+ .default_value = ring_data_default,
+ .max_value = ring_data_max,
+ .min_value = ring_data_min,
+ };
+ return DriverResult::Success;
+}
+
+DriverResult CalibrationProtocol::HasUserCalibration(SpiAddress address,
+ bool& has_user_calibration) {
+ MagicSpiCalibration spi_magic{};
+ const DriverResult result{ReadSPI(address, spi_magic)};
+ has_user_calibration = false;
+ if (result == DriverResult::Success) {
+ has_user_calibration = spi_magic.first == CalibrationMagic::USR_MAGIC_0 &&
+ spi_magic.second == CalibrationMagic::USR_MAGIC_1;
+ }
+ return result;
+}
+
+u16 CalibrationProtocol::GetXAxisCalibrationValue(std::span<u8> block) const {
+ return static_cast<u16>(((block[1] & 0x0F) << 8) | block[0]);
+}
+
+u16 CalibrationProtocol::GetYAxisCalibrationValue(std::span<u8> block) const {
+ return static_cast<u16>((block[2] << 4) | (block[1] >> 4));
+}
+
+void CalibrationProtocol::ValidateCalibration(JoyStickCalibration& calibration) {
+ constexpr u16 DefaultStickCenter{0x800};
+ constexpr u16 DefaultStickRange{0x6cc};
+
+ calibration.x.center = ValidateValue(calibration.x.center, DefaultStickCenter);
+ calibration.x.max = ValidateValue(calibration.x.max, DefaultStickRange);
+ calibration.x.min = ValidateValue(calibration.x.min, DefaultStickRange);
+
+ calibration.y.center = ValidateValue(calibration.y.center, DefaultStickCenter);
+ calibration.y.max = ValidateValue(calibration.y.max, DefaultStickRange);
+ calibration.y.min = ValidateValue(calibration.y.min, DefaultStickRange);
+}
+
+void CalibrationProtocol::ValidateCalibration(MotionCalibration& calibration) {
+ constexpr s16 DefaultAccelerometerScale{0x4000};
+ constexpr s16 DefaultGyroScale{0x3be7};
+ constexpr s16 DefaultOffset{0};
+
+ for (auto& sensor : calibration.accelerometer) {
+ sensor.scale = ValidateValue(sensor.scale, DefaultAccelerometerScale);
+ sensor.offset = ValidateValue(sensor.offset, DefaultOffset);
+ }
+ for (auto& sensor : calibration.gyro) {
+ sensor.scale = ValidateValue(sensor.scale, DefaultGyroScale);
+ sensor.offset = ValidateValue(sensor.offset, DefaultOffset);
+ }
+}
+
+u16 CalibrationProtocol::ValidateValue(u16 value, u16 default_value) const {
+ if (value == 0) {
+ return default_value;
+ }
+ if (value == 0xFFF) {
+ return default_value;
+ }
+ return value;
+}
+
+s16 CalibrationProtocol::ValidateValue(s16 value, s16 default_value) const {
+ if (value == 0) {
+ return default_value;
+ }
+ if (value == 0xFFF) {
+ return default_value;
+ }
+ return value;
+}
+
+} // 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 @@
+// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+// Based on dkms-hid-nintendo implementation, CTCaer joycon toolkit and dekuNukem reverse
+// engineering https://github.com/nicman23/dkms-hid-nintendo/blob/master/src/hid-nintendo.c
+// https://github.com/CTCaer/jc_toolkit
+// https://github.com/dekuNukem/Nintendo_Switch_Reverse_Engineering
+
+#pragma once
+
+#include <vector>
+
+#include "input_common/helpers/joycon_protocol/common_protocol.h"
+
+namespace InputCommon::Joycon {
+enum class DriverResult;
+struct JoyStickCalibration;
+struct IMUCalibration;
+struct JoyconHandle;
+} // namespace InputCommon::Joycon
+
+namespace InputCommon::Joycon {
+
+/// Driver functions related to retrieving calibration data from the device
+class CalibrationProtocol final : private JoyconCommonProtocol {
+public:
+ explicit CalibrationProtocol(std::shared_ptr<JoyconHandle> handle);
+
+ /**
+ * Sends a request to obtain the left stick calibration from memory
+ * @param is_factory_calibration if true factory values will be returned
+ * @returns JoyStickCalibration of the left joystick
+ */
+ DriverResult GetLeftJoyStickCalibration(JoyStickCalibration& calibration);
+
+ /**
+ * Sends a request to obtain the right stick calibration from memory
+ * @param is_factory_calibration if true factory values will be returned
+ * @returns JoyStickCalibration of the right joystick
+ */
+ DriverResult GetRightJoyStickCalibration(JoyStickCalibration& calibration);
+
+ /**
+ * Sends a request to obtain the motion calibration from memory
+ * @returns ImuCalibration of the motion sensor
+ */
+ DriverResult GetImuCalibration(MotionCalibration& calibration);
+
+ /**
+ * Calculates on run time the proper calibration of the ring controller
+ * @returns RingCalibration of the ring sensor
+ */
+ DriverResult GetRingCalibration(RingCalibration& calibration, s16 current_value);
+
+private:
+ /// Returns true if the specified address corresponds to the magic value of user calibration
+ DriverResult HasUserCalibration(SpiAddress address, bool& has_user_calibration);
+
+ /// Converts a raw calibration block to an u16 value containing the x axis value
+ u16 GetXAxisCalibrationValue(std::span<u8> block) const;
+
+ /// Converts a raw calibration block to an u16 value containing the y axis value
+ u16 GetYAxisCalibrationValue(std::span<u8> block) const;
+
+ /// Ensures that all joystick calibration values are set
+ void ValidateCalibration(JoyStickCalibration& calibration);
+
+ /// Ensures that all motion calibration values are set
+ void ValidateCalibration(MotionCalibration& calibration);
+
+ /// Returns the default value if the value is either zero or 0xFFF
+ u16 ValidateValue(u16 value, u16 default_value) const;
+
+ /// Returns the default value if the value is either zero or 0xFFF
+ s16 ValidateValue(s16 value, s16 default_value) const;
+
+ s16 ring_data_max = 0;
+ s16 ring_data_default = 0;
+ s16 ring_data_min = 0;
+};
+
+} // 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 @@
+// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include "common/logging/log.h"
+#include "input_common/helpers/joycon_protocol/common_protocol.h"
+
+namespace InputCommon::Joycon {
+JoyconCommonProtocol::JoyconCommonProtocol(std::shared_ptr<JoyconHandle> hidapi_handle_)
+ : hidapi_handle{std::move(hidapi_handle_)} {}
+
+u8 JoyconCommonProtocol::GetCounter() {
+ hidapi_handle->packet_counter = (hidapi_handle->packet_counter + 1) & 0x0F;
+ return hidapi_handle->packet_counter;
+}
+
+void JoyconCommonProtocol::SetBlocking() {
+ SDL_hid_set_nonblocking(hidapi_handle->handle, 0);
+}
+
+void JoyconCommonProtocol::SetNonBlocking() {
+ SDL_hid_set_nonblocking(hidapi_handle->handle, 1);
+}
+
+DriverResult JoyconCommonProtocol::GetDeviceType(ControllerType& controller_type) {
+ const auto result = ReadSPI(SpiAddress::DEVICE_TYPE, controller_type);
+
+ if (result == DriverResult::Success) {
+ // Fallback to 3rd party pro controllers
+ if (controller_type == ControllerType::None) {
+ controller_type = ControllerType::Pro;
+ }
+ }
+
+ return result;
+}
+
+DriverResult JoyconCommonProtocol::CheckDeviceAccess(SDL_hid_device_info* device_info) {
+ ControllerType controller_type{ControllerType::None};
+ const auto result = GetDeviceType(controller_type);
+
+ if (result != DriverResult::Success || controller_type == ControllerType::None) {
+ return DriverResult::UnsupportedControllerType;
+ }
+
+ hidapi_handle->handle =
+ SDL_hid_open(device_info->vendor_id, device_info->product_id, device_info->serial_number);
+
+ if (!hidapi_handle->handle) {
+ LOG_ERROR(Input, "Yuzu can't gain access to this device: ID {:04X}:{:04X}.",
+ device_info->vendor_id, device_info->product_id);
+ return DriverResult::HandleInUse;
+ }
+
+ SetNonBlocking();
+ return DriverResult::Success;
+}
+
+DriverResult JoyconCommonProtocol::SetReportMode(ReportMode report_mode) {
+ const std::array<u8, 1> buffer{static_cast<u8>(report_mode)};
+ return SendSubCommand(SubCommand::SET_REPORT_MODE, buffer);
+}
+
+DriverResult JoyconCommonProtocol::SendRawData(std::span<const u8> buffer) {
+ const auto result = SDL_hid_write(hidapi_handle->handle, buffer.data(), buffer.size());
+
+ if (result == -1) {
+ return DriverResult::ErrorWritingData;
+ }
+
+ return DriverResult::Success;
+}
+
+DriverResult JoyconCommonProtocol::GetSubCommandResponse(SubCommand sc,
+ SubCommandResponse& output) {
+ constexpr int timeout_mili = 66;
+ constexpr int MaxTries = 15;
+ int tries = 0;
+
+ do {
+ int result = SDL_hid_read_timeout(hidapi_handle->handle, reinterpret_cast<u8*>(&output),
+ sizeof(SubCommandResponse), timeout_mili);
+
+ if (result < 1) {
+ LOG_ERROR(Input, "No response from joycon");
+ }
+ if (tries++ > MaxTries) {
+ return DriverResult::Timeout;
+ }
+ } while (output.input_report.report_mode != ReportMode::SUBCMD_REPLY &&
+ output.sub_command != sc);
+
+ return DriverResult::Success;
+}
+
+DriverResult JoyconCommonProtocol::SendSubCommand(SubCommand sc, std::span<const u8> buffer,
+ SubCommandResponse& output) {
+ SubCommandPacket packet{
+ .output_report = OutputReport::RUMBLE_AND_SUBCMD,
+ .packet_counter = GetCounter(),
+ .sub_command = sc,
+ .command_data = {},
+ };
+
+ if (buffer.size() > packet.command_data.size()) {
+ return DriverResult::InvalidParameters;
+ }
+
+ memcpy(packet.command_data.data(), buffer.data(), buffer.size());
+
+ auto result = SendData(packet);
+
+ if (result != DriverResult::Success) {
+ return result;
+ }
+
+ result = GetSubCommandResponse(sc, output);
+
+ return DriverResult::Success;
+}
+
+DriverResult JoyconCommonProtocol::SendSubCommand(SubCommand sc, std::span<const u8> buffer) {
+ SubCommandResponse output{};
+ return SendSubCommand(sc, buffer, output);
+}
+
+DriverResult JoyconCommonProtocol::SendMCUCommand(SubCommand sc, std::span<const u8> buffer) {
+ SubCommandPacket packet{
+ .output_report = OutputReport::MCU_DATA,
+ .packet_counter = GetCounter(),
+ .sub_command = sc,
+ .command_data = {},
+ };
+
+ if (buffer.size() > packet.command_data.size()) {
+ return DriverResult::InvalidParameters;
+ }
+
+ memcpy(packet.command_data.data(), buffer.data(), buffer.size());
+
+ return SendData(packet);
+}
+
+DriverResult JoyconCommonProtocol::SendVibrationReport(std::span<const u8> buffer) {
+ VibrationPacket packet{
+ .output_report = OutputReport::RUMBLE_ONLY,
+ .packet_counter = GetCounter(),
+ .vibration_data = {},
+ };
+
+ if (buffer.size() > packet.vibration_data.size()) {
+ return DriverResult::InvalidParameters;
+ }
+
+ memcpy(packet.vibration_data.data(), buffer.data(), buffer.size());
+
+ return SendData(packet);
+}
+
+DriverResult JoyconCommonProtocol::ReadRawSPI(SpiAddress addr, std::span<u8> output) {
+ constexpr std::size_t HeaderSize = 5;
+ constexpr std::size_t MaxTries = 10;
+ std::size_t tries = 0;
+ SubCommandResponse response{};
+ std::array<u8, sizeof(ReadSpiPacket)> buffer{};
+ const ReadSpiPacket packet_data{
+ .spi_address = addr,
+ .size = static_cast<u8>(output.size()),
+ };
+
+ memcpy(buffer.data(), &packet_data, sizeof(ReadSpiPacket));
+ do {
+ const auto result = SendSubCommand(SubCommand::SPI_FLASH_READ, buffer, response);
+ if (result != DriverResult::Success) {
+ return result;
+ }
+
+ if (tries++ > MaxTries) {
+ return DriverResult::Timeout;
+ }
+ } while (response.spi_address != addr);
+
+ if (response.command_data.size() < packet_data.size + HeaderSize) {
+ return DriverResult::WrongReply;
+ }
+
+ // Remove header from output
+ memcpy(output.data(), response.command_data.data() + HeaderSize, packet_data.size);
+ return DriverResult::Success;
+}
+
+DriverResult JoyconCommonProtocol::EnableMCU(bool enable) {
+ const std::array<u8, 1> mcu_state{static_cast<u8>(enable ? 1 : 0)};
+ const auto result = SendSubCommand(SubCommand::SET_MCU_STATE, mcu_state);
+
+ if (result != DriverResult::Success) {
+ LOG_ERROR(Input, "Failed with error {}", result);
+ }
+
+ return result;
+}
+
+DriverResult JoyconCommonProtocol::ConfigureMCU(const MCUConfig& config) {
+ LOG_DEBUG(Input, "ConfigureMCU");
+ std::array<u8, sizeof(MCUConfig)> config_buffer;
+ memcpy(config_buffer.data(), &config, sizeof(MCUConfig));
+ config_buffer[37] = CalculateMCU_CRC8(config_buffer.data() + 1, 36);
+
+ const auto result = SendSubCommand(SubCommand::SET_MCU_CONFIG, config_buffer);
+
+ if (result != DriverResult::Success) {
+ LOG_ERROR(Input, "Failed with error {}", result);
+ }
+
+ return result;
+}
+
+DriverResult JoyconCommonProtocol::GetMCUDataResponse(ReportMode report_mode,
+ MCUCommandResponse& output) {
+ constexpr int TimeoutMili = 200;
+ constexpr int MaxTries = 9;
+ int tries = 0;
+
+ do {
+ int result = SDL_hid_read_timeout(hidapi_handle->handle, reinterpret_cast<u8*>(&output),
+ sizeof(MCUCommandResponse), TimeoutMili);
+
+ if (result < 1) {
+ LOG_ERROR(Input, "No response from joycon attempt {}", tries);
+ }
+ if (tries++ > MaxTries) {
+ return DriverResult::Timeout;
+ }
+ } while (output.input_report.report_mode != report_mode ||
+ output.mcu_report == MCUReport::EmptyAwaitingCmd);
+
+ return DriverResult::Success;
+}
+
+DriverResult JoyconCommonProtocol::SendMCUData(ReportMode report_mode, SubCommand sc,
+ std::span<const u8> buffer,
+ MCUCommandResponse& output) {
+ SubCommandPacket packet{
+ .output_report = OutputReport::MCU_DATA,
+ .packet_counter = GetCounter(),
+ .sub_command = sc,
+ .command_data = {},
+ };
+
+ if (buffer.size() > packet.command_data.size()) {
+ return DriverResult::InvalidParameters;
+ }
+
+ memcpy(packet.command_data.data(), buffer.data(), buffer.size());
+
+ auto result = SendData(packet);
+
+ if (result != DriverResult::Success) {
+ return result;
+ }
+
+ result = GetMCUDataResponse(report_mode, output);
+
+ return DriverResult::Success;
+}
+
+DriverResult JoyconCommonProtocol::WaitSetMCUMode(ReportMode report_mode, MCUMode mode) {
+ MCUCommandResponse output{};
+ constexpr std::size_t MaxTries{8};
+ std::size_t tries{};
+
+ do {
+ const std::vector<u8> mcu_data{static_cast<u8>(MCUMode::Standby)};
+ const auto result = SendMCUData(report_mode, SubCommand::STATE, mcu_data, output);
+
+ if (result != DriverResult::Success) {
+ return result;
+ }
+
+ if (tries++ > MaxTries) {
+ return DriverResult::WrongReply;
+ }
+ } while (output.mcu_report != MCUReport::StateReport ||
+ output.mcu_data[6] != static_cast<u8>(mode));
+
+ return DriverResult::Success;
+}
+
+// crc-8-ccitt / polynomial 0x07 look up table
+constexpr std::array<u8, 256> mcu_crc8_table = {
+ 0x00, 0x07, 0x0E, 0x09, 0x1C, 0x1B, 0x12, 0x15, 0x38, 0x3F, 0x36, 0x31, 0x24, 0x23, 0x2A, 0x2D,
+ 0x70, 0x77, 0x7E, 0x79, 0x6C, 0x6B, 0x62, 0x65, 0x48, 0x4F, 0x46, 0x41, 0x54, 0x53, 0x5A, 0x5D,
+ 0xE0, 0xE7, 0xEE, 0xE9, 0xFC, 0xFB, 0xF2, 0xF5, 0xD8, 0xDF, 0xD6, 0xD1, 0xC4, 0xC3, 0xCA, 0xCD,
+ 0x90, 0x97, 0x9E, 0x99, 0x8C, 0x8B, 0x82, 0x85, 0xA8, 0xAF, 0xA6, 0xA1, 0xB4, 0xB3, 0xBA, 0xBD,
+ 0xC7, 0xC0, 0xC9, 0xCE, 0xDB, 0xDC, 0xD5, 0xD2, 0xFF, 0xF8, 0xF1, 0xF6, 0xE3, 0xE4, 0xED, 0xEA,
+ 0xB7, 0xB0, 0xB9, 0xBE, 0xAB, 0xAC, 0xA5, 0xA2, 0x8F, 0x88, 0x81, 0x86, 0x93, 0x94, 0x9D, 0x9A,
+ 0x27, 0x20, 0x29, 0x2E, 0x3B, 0x3C, 0x35, 0x32, 0x1F, 0x18, 0x11, 0x16, 0x03, 0x04, 0x0D, 0x0A,
+ 0x57, 0x50, 0x59, 0x5E, 0x4B, 0x4C, 0x45, 0x42, 0x6F, 0x68, 0x61, 0x66, 0x73, 0x74, 0x7D, 0x7A,
+ 0x89, 0x8E, 0x87, 0x80, 0x95, 0x92, 0x9B, 0x9C, 0xB1, 0xB6, 0xBF, 0xB8, 0xAD, 0xAA, 0xA3, 0xA4,
+ 0xF9, 0xFE, 0xF7, 0xF0, 0xE5, 0xE2, 0xEB, 0xEC, 0xC1, 0xC6, 0xCF, 0xC8, 0xDD, 0xDA, 0xD3, 0xD4,
+ 0x69, 0x6E, 0x67, 0x60, 0x75, 0x72, 0x7B, 0x7C, 0x51, 0x56, 0x5F, 0x58, 0x4D, 0x4A, 0x43, 0x44,
+ 0x19, 0x1E, 0x17, 0x10, 0x05, 0x02, 0x0B, 0x0C, 0x21, 0x26, 0x2F, 0x28, 0x3D, 0x3A, 0x33, 0x34,
+ 0x4E, 0x49, 0x40, 0x47, 0x52, 0x55, 0x5C, 0x5B, 0x76, 0x71, 0x78, 0x7F, 0x6A, 0x6D, 0x64, 0x63,
+ 0x3E, 0x39, 0x30, 0x37, 0x22, 0x25, 0x2C, 0x2B, 0x06, 0x01, 0x08, 0x0F, 0x1A, 0x1D, 0x14, 0x13,
+ 0xAE, 0xA9, 0xA0, 0xA7, 0xB2, 0xB5, 0xBC, 0xBB, 0x96, 0x91, 0x98, 0x9F, 0x8A, 0x8D, 0x84, 0x83,
+ 0xDE, 0xD9, 0xD0, 0xD7, 0xC2, 0xC5, 0xCC, 0xCB, 0xE6, 0xE1, 0xE8, 0xEF, 0xFA, 0xFD, 0xF4, 0xF3};
+
+u8 JoyconCommonProtocol::CalculateMCU_CRC8(u8* buffer, u8 size) const {
+ u8 crc8 = 0x0;
+
+ for (int i = 0; i < size; ++i) {
+ crc8 = mcu_crc8_table[static_cast<u8>(crc8 ^ buffer[i])];
+ }
+ return crc8;
+}
+
+} // 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 @@
+// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+// Based on dkms-hid-nintendo implementation, CTCaer joycon toolkit and dekuNukem reverse
+// engineering https://github.com/nicman23/dkms-hid-nintendo/blob/master/src/hid-nintendo.c
+// https://github.com/CTCaer/jc_toolkit
+// https://github.com/dekuNukem/Nintendo_Switch_Reverse_Engineering
+
+#pragma once
+
+#include <memory>
+#include <span>
+#include <vector>
+
+#include "common/common_types.h"
+#include "input_common/helpers/joycon_protocol/joycon_types.h"
+
+namespace InputCommon::Joycon {
+
+/// Joycon driver functions that handle low level communication
+class JoyconCommonProtocol {
+public:
+ explicit JoyconCommonProtocol(std::shared_ptr<JoyconHandle> hidapi_handle_);
+
+ /**
+ * Sets handle to blocking. In blocking mode, SDL_hid_read() will wait (block) until there is
+ * data to read before returning.
+ */
+ void SetBlocking();
+
+ /**
+ * Sets handle to non blocking. In non-blocking mode calls to SDL_hid_read() will return
+ * immediately with a value of 0 if there is no data to be read
+ */
+ void SetNonBlocking();
+
+ /**
+ * Sends a request to obtain the joycon type from device
+ * @returns controller type of the joycon
+ */
+ DriverResult GetDeviceType(ControllerType& controller_type);
+
+ /**
+ * Verifies and sets the joycon_handle if device is valid
+ * @param device info from the driver
+ * @returns success if the device is valid
+ */
+ DriverResult CheckDeviceAccess(SDL_hid_device_info* device);
+
+ /**
+ * Sends a request to set the polling mode of the joycon
+ * @param report_mode polling mode to be set
+ */
+ DriverResult SetReportMode(Joycon::ReportMode report_mode);
+
+ /**
+ * Sends data to the joycon device
+ * @param buffer data to be send
+ */
+ DriverResult SendRawData(std::span<const u8> buffer);
+
+ template <typename Output>
+ requires std::is_trivially_copyable_v<Output>
+ DriverResult SendData(const Output& output) {
+ std::array<u8, sizeof(Output)> buffer;
+ std::memcpy(buffer.data(), &output, sizeof(Output));
+ return SendRawData(buffer);
+ }
+
+ /**
+ * Waits for incoming data of the joycon device that matchs the subcommand
+ * @param sub_command type of data to be returned
+ * @returns a buffer containing the response
+ */
+ DriverResult GetSubCommandResponse(SubCommand sub_command, SubCommandResponse& output);
+
+ /**
+ * Sends a sub command to the device and waits for it's reply
+ * @param sc sub command to be send
+ * @param buffer data to be send
+ * @returns output buffer containing the response
+ */
+ DriverResult SendSubCommand(SubCommand sc, std::span<const u8> buffer,
+ SubCommandResponse& output);
+
+ /**
+ * Sends a sub command to the device and waits for it's reply and ignores the output
+ * @param sc sub command to be send
+ * @param buffer data to be send
+ */
+ DriverResult SendSubCommand(SubCommand sc, std::span<const u8> buffer);
+
+ /**
+ * Sends a mcu command to the device
+ * @param sc sub command to be send
+ * @param buffer data to be send
+ */
+ DriverResult SendMCUCommand(SubCommand sc, std::span<const u8> buffer);
+
+ /**
+ * Sends vibration data to the joycon
+ * @param buffer data to be send
+ */
+ DriverResult SendVibrationReport(std::span<const u8> buffer);
+
+ /**
+ * Reads the SPI memory stored on the joycon
+ * @param Initial address location
+ * @returns output buffer containing the response
+ */
+ DriverResult ReadRawSPI(SpiAddress addr, std::span<u8> output);
+
+ /**
+ * Reads the SPI memory stored on the joycon
+ * @param Initial address location
+ * @returns output object containing the response
+ */
+ template <typename Output>
+ requires std::is_trivially_copyable_v<Output>
+ DriverResult ReadSPI(SpiAddress addr, Output& output) {
+ std::array<u8, sizeof(Output)> buffer;
+ output = {};
+
+ const auto result = ReadRawSPI(addr, buffer);
+ if (result != DriverResult::Success) {
+ return result;
+ }
+
+ std::memcpy(&output, buffer.data(), sizeof(Output));
+ return DriverResult::Success;
+ }
+
+ /**
+ * Enables MCU chip on the joycon
+ * @param enable if true the chip will be enabled
+ */
+ DriverResult EnableMCU(bool enable);
+
+ /**
+ * Configures the MCU to the correspoinding mode
+ * @param MCUConfig configuration
+ */
+ DriverResult ConfigureMCU(const MCUConfig& config);
+
+ /**
+ * Waits until there's MCU data available. On timeout returns error
+ * @param report mode of the expected reply
+ * @returns a buffer containing the response
+ */
+ DriverResult GetMCUDataResponse(ReportMode report_mode_, MCUCommandResponse& output);
+
+ /**
+ * Sends data to the MCU chip and waits for it's reply
+ * @param report mode of the expected reply
+ * @param sub command to be send
+ * @param buffer data to be send
+ * @returns output buffer containing the response
+ */
+ DriverResult SendMCUData(ReportMode report_mode, SubCommand sc, std::span<const u8> buffer,
+ MCUCommandResponse& output);
+
+ /**
+ * Wait's until the MCU chip is on the specified mode
+ * @param report mode of the expected reply
+ * @param MCUMode configuration
+ */
+ DriverResult WaitSetMCUMode(ReportMode report_mode, MCUMode mode);
+
+ /**
+ * Calculates the checksum from the MCU data
+ * @param buffer containing the data to be send
+ * @param size of the buffer in bytes
+ * @returns byte with the correct checksum
+ */
+ u8 CalculateMCU_CRC8(u8* buffer, u8 size) const;
+
+private:
+ /**
+ * Increments and returns the packet counter of the handle
+ * @param joycon_handle device to send the data
+ * @returns packet counter value
+ */
+ u8 GetCounter();
+
+ std::shared_ptr<JoyconHandle> hidapi_handle;
+};
+
+class ScopedSetBlocking {
+public:
+ explicit ScopedSetBlocking(JoyconCommonProtocol* self) : m_self{self} {
+ m_self->SetBlocking();
+ }
+
+ ~ScopedSetBlocking() {
+ m_self->SetNonBlocking();
+ }
+
+private:
+ JoyconCommonProtocol* m_self{};
+};
+} // 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 @@
+// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include "common/logging/log.h"
+#include "input_common/helpers/joycon_protocol/generic_functions.h"
+
+namespace InputCommon::Joycon {
+
+GenericProtocol::GenericProtocol(std::shared_ptr<JoyconHandle> handle)
+ : JoyconCommonProtocol(std::move(handle)) {}
+
+DriverResult GenericProtocol::EnablePassiveMode() {
+ ScopedSetBlocking sb(this);
+ return SetReportMode(ReportMode::SIMPLE_HID_MODE);
+}
+
+DriverResult GenericProtocol::EnableActiveMode() {
+ ScopedSetBlocking sb(this);
+ return SetReportMode(ReportMode::STANDARD_FULL_60HZ);
+}
+
+DriverResult GenericProtocol::SetLowPowerMode(bool enable) {
+ ScopedSetBlocking sb(this);
+ const std::array<u8, 1> buffer{static_cast<u8>(enable ? 1 : 0)};
+ return SendSubCommand(SubCommand::LOW_POWER_MODE, buffer);
+}
+
+DriverResult GenericProtocol::TriggersElapsed() {
+ ScopedSetBlocking sb(this);
+ return SendSubCommand(SubCommand::TRIGGERS_ELAPSED, {});
+}
+
+DriverResult GenericProtocol::GetDeviceInfo(DeviceInfo& device_info) {
+ ScopedSetBlocking sb(this);
+ SubCommandResponse output{};
+
+ const auto result = SendSubCommand(SubCommand::REQ_DEV_INFO, {}, output);
+
+ device_info = {};
+ if (result == DriverResult::Success) {
+ device_info = output.device_info;
+ }
+
+ return result;
+}
+
+DriverResult GenericProtocol::GetControllerType(ControllerType& controller_type) {
+ return GetDeviceType(controller_type);
+}
+
+DriverResult GenericProtocol::EnableImu(bool enable) {
+ ScopedSetBlocking sb(this);
+ const std::array<u8, 1> buffer{static_cast<u8>(enable ? 1 : 0)};
+ return SendSubCommand(SubCommand::ENABLE_IMU, buffer);
+}
+
+DriverResult GenericProtocol::SetImuConfig(GyroSensitivity gsen, GyroPerformance gfrec,
+ AccelerometerSensitivity asen,
+ AccelerometerPerformance afrec) {
+ ScopedSetBlocking sb(this);
+ const std::array<u8, 4> buffer{static_cast<u8>(gsen), static_cast<u8>(asen),
+ static_cast<u8>(gfrec), static_cast<u8>(afrec)};
+ return SendSubCommand(SubCommand::SET_IMU_SENSITIVITY, buffer);
+}
+
+DriverResult GenericProtocol::GetBattery(u32& battery_level) {
+ // This function is meant to request the high resolution battery status
+ battery_level = 0;
+ return DriverResult::NotSupported;
+}
+
+DriverResult GenericProtocol::GetColor(Color& color) {
+ ScopedSetBlocking sb(this);
+ std::array<u8, 12> buffer{};
+ const auto result = ReadRawSPI(SpiAddress::COLOR_DATA, buffer);
+
+ color = {};
+ if (result == DriverResult::Success) {
+ color.body = static_cast<u32>((buffer[0] << 16) | (buffer[1] << 8) | buffer[2]);
+ color.buttons = static_cast<u32>((buffer[3] << 16) | (buffer[4] << 8) | buffer[5]);
+ color.left_grip = static_cast<u32>((buffer[6] << 16) | (buffer[7] << 8) | buffer[8]);
+ color.right_grip = static_cast<u32>((buffer[9] << 16) | (buffer[10] << 8) | buffer[11]);
+ }
+
+ return result;
+}
+
+DriverResult GenericProtocol::GetSerialNumber(SerialNumber& serial_number) {
+ ScopedSetBlocking sb(this);
+ std::array<u8, 16> buffer{};
+ const auto result = ReadRawSPI(SpiAddress::SERIAL_NUMBER, buffer);
+
+ serial_number = {};
+ if (result == DriverResult::Success) {
+ memcpy(serial_number.data(), buffer.data() + 1, sizeof(SerialNumber));
+ }
+
+ return result;
+}
+
+DriverResult GenericProtocol::GetTemperature(u32& temperature) {
+ // Not all devices have temperature sensor
+ temperature = 25;
+ return DriverResult::NotSupported;
+}
+
+DriverResult GenericProtocol::GetVersionNumber(FirmwareVersion& version) {
+ DeviceInfo device_info{};
+
+ const auto result = GetDeviceInfo(device_info);
+ version = device_info.firmware;
+
+ return result;
+}
+
+DriverResult GenericProtocol::SetHomeLight() {
+ ScopedSetBlocking sb(this);
+ static constexpr std::array<u8, 3> buffer{0x0f, 0xf0, 0x00};
+ return SendSubCommand(SubCommand::SET_HOME_LIGHT, buffer);
+}
+
+DriverResult GenericProtocol::SetLedBusy() {
+ return DriverResult::NotSupported;
+}
+
+DriverResult GenericProtocol::SetLedPattern(u8 leds) {
+ ScopedSetBlocking sb(this);
+ const std::array<u8, 1> buffer{leds};
+ return SendSubCommand(SubCommand::SET_PLAYER_LIGHTS, buffer);
+}
+
+DriverResult GenericProtocol::SetLedBlinkPattern(u8 leds) {
+ return SetLedPattern(static_cast<u8>(leds << 4));
+}
+
+} // 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 @@
+// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+// Based on dkms-hid-nintendo implementation, CTCaer joycon toolkit and dekuNukem reverse
+// engineering https://github.com/nicman23/dkms-hid-nintendo/blob/master/src/hid-nintendo.c
+// https://github.com/CTCaer/jc_toolkit
+// https://github.com/dekuNukem/Nintendo_Switch_Reverse_Engineering
+
+#pragma once
+
+#include "input_common/helpers/joycon_protocol/common_protocol.h"
+#include "input_common/helpers/joycon_protocol/joycon_types.h"
+
+namespace InputCommon::Joycon {
+
+/// Joycon driver functions that easily implemented
+class GenericProtocol final : private JoyconCommonProtocol {
+public:
+ explicit GenericProtocol(std::shared_ptr<JoyconHandle> handle);
+
+ /// Enables passive mode. This mode only sends button data on change. Sticks will return digital
+ /// data instead of analog. Motion will be disabled
+ DriverResult EnablePassiveMode();
+
+ /// Enables active mode. This mode will return the current status every 5-15ms
+ DriverResult EnableActiveMode();
+
+ /// Enables or disables the low power mode
+ DriverResult SetLowPowerMode(bool enable);
+
+ /// Unknown function used by the switch
+ DriverResult TriggersElapsed();
+
+ /**
+ * Sends a request to obtain the joycon firmware and mac from handle
+ * @returns controller device info
+ */
+ DriverResult GetDeviceInfo(DeviceInfo& controller_type);
+
+ /**
+ * Sends a request to obtain the joycon type from handle
+ * @returns controller type of the joycon
+ */
+ DriverResult GetControllerType(ControllerType& controller_type);
+
+ /**
+ * Enables motion input
+ * @param enable if true motion data will be enabled
+ */
+ DriverResult EnableImu(bool enable);
+
+ /**
+ * Configures the motion sensor with the specified parameters
+ * @param gsen gyroscope sensor sensitvity in degrees per second
+ * @param gfrec gyroscope sensor frequency in hertz
+ * @param asen accelerometer sensitivity in G force
+ * @param afrec accelerometer frequency in hertz
+ */
+ DriverResult SetImuConfig(GyroSensitivity gsen, GyroPerformance gfrec,
+ AccelerometerSensitivity asen, AccelerometerPerformance afrec);
+
+ /**
+ * Request battery level from the device
+ * @returns battery level
+ */
+ DriverResult GetBattery(u32& battery_level);
+
+ /**
+ * Request joycon colors from the device
+ * @returns colors of the body and buttons
+ */
+ DriverResult GetColor(Color& color);
+
+ /**
+ * Request joycon serial number from the device
+ * @returns 16 byte serial number
+ */
+ DriverResult GetSerialNumber(SerialNumber& serial_number);
+
+ /**
+ * Request joycon serial number from the device
+ * @returns 16 byte serial number
+ */
+ DriverResult GetTemperature(u32& temperature);
+
+ /**
+ * Request joycon serial number from the device
+ * @returns 16 byte serial number
+ */
+ DriverResult GetVersionNumber(FirmwareVersion& version);
+
+ /**
+ * Sets home led behaviour
+ */
+ DriverResult SetHomeLight();
+
+ /**
+ * Sets home led into a slow breathing state
+ */
+ DriverResult SetLedBusy();
+
+ /**
+ * Sets the 4 player leds on the joycon on a solid state
+ * @params bit flag containing the led state
+ */
+ DriverResult SetLedPattern(u8 leds);
+
+ /**
+ * Sets the 4 player leds on the joycon on a blinking state
+ * @returns bit flag containing the led state
+ */
+ DriverResult SetLedBlinkPattern(u8 leds);
+};
+} // 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 @@
+// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include <thread>
+#include "common/logging/log.h"
+#include "input_common/helpers/joycon_protocol/irs.h"
+
+namespace InputCommon::Joycon {
+
+IrsProtocol::IrsProtocol(std::shared_ptr<JoyconHandle> handle)
+ : JoyconCommonProtocol(std::move(handle)) {}
+
+DriverResult IrsProtocol::EnableIrs() {
+ LOG_INFO(Input, "Enable IRS");
+ ScopedSetBlocking sb(this);
+ DriverResult result{DriverResult::Success};
+
+ if (result == DriverResult::Success) {
+ result = SetReportMode(ReportMode::NFC_IR_MODE_60HZ);
+ }
+ if (result == DriverResult::Success) {
+ result = EnableMCU(true);
+ }
+ if (result == DriverResult::Success) {
+ result = WaitSetMCUMode(ReportMode::NFC_IR_MODE_60HZ, MCUMode::Standby);
+ }
+ if (result == DriverResult::Success) {
+ const MCUConfig config{
+ .command = MCUCommand::ConfigureMCU,
+ .sub_command = MCUSubCommand::SetMCUMode,
+ .mode = MCUMode::IR,
+ .crc = {},
+ };
+
+ result = ConfigureMCU(config);
+ }
+ if (result == DriverResult::Success) {
+ result = WaitSetMCUMode(ReportMode::NFC_IR_MODE_60HZ, MCUMode::IR);
+ }
+ if (result == DriverResult::Success) {
+ result = ConfigureIrs();
+ }
+ if (result == DriverResult::Success) {
+ result = WriteRegistersStep1();
+ }
+ if (result == DriverResult::Success) {
+ result = WriteRegistersStep2();
+ }
+
+ is_enabled = true;
+
+ return result;
+}
+
+DriverResult IrsProtocol::DisableIrs() {
+ LOG_DEBUG(Input, "Disable IRS");
+ ScopedSetBlocking sb(this);
+ DriverResult result{DriverResult::Success};
+
+ if (result == DriverResult::Success) {
+ result = EnableMCU(false);
+ }
+
+ is_enabled = false;
+
+ return result;
+}
+
+DriverResult IrsProtocol::SetIrsConfig(IrsMode mode, IrsResolution format) {
+ irs_mode = mode;
+ switch (format) {
+ case IrsResolution::Size320x240:
+ resolution_code = IrsResolutionCode::Size320x240;
+ fragments = IrsFragments::Size320x240;
+ resolution = IrsResolution::Size320x240;
+ break;
+ case IrsResolution::Size160x120:
+ resolution_code = IrsResolutionCode::Size160x120;
+ fragments = IrsFragments::Size160x120;
+ resolution = IrsResolution::Size160x120;
+ break;
+ case IrsResolution::Size80x60:
+ resolution_code = IrsResolutionCode::Size80x60;
+ fragments = IrsFragments::Size80x60;
+ resolution = IrsResolution::Size80x60;
+ break;
+ case IrsResolution::Size20x15:
+ resolution_code = IrsResolutionCode::Size20x15;
+ fragments = IrsFragments::Size20x15;
+ resolution = IrsResolution::Size20x15;
+ break;
+ case IrsResolution::Size40x30:
+ default:
+ resolution_code = IrsResolutionCode::Size40x30;
+ fragments = IrsFragments::Size40x30;
+ resolution = IrsResolution::Size40x30;
+ break;
+ }
+
+ // Restart feature
+ if (is_enabled) {
+ DisableIrs();
+ return EnableIrs();
+ }
+
+ return DriverResult::Success;
+}
+
+DriverResult IrsProtocol::RequestImage(std::span<u8> buffer) {
+ const u8 next_packet_fragment =
+ static_cast<u8>((packet_fragment + 1) % (static_cast<u8>(fragments) + 1));
+
+ if (buffer[0] == 0x31 && buffer[49] == 0x03) {
+ u8 new_packet_fragment = buffer[52];
+ if (new_packet_fragment == next_packet_fragment) {
+ packet_fragment = next_packet_fragment;
+ memcpy(buf_image.data() + (300 * packet_fragment), buffer.data() + 59, 300);
+
+ return RequestFrame(packet_fragment);
+ }
+
+ if (new_packet_fragment == packet_fragment) {
+ return RequestFrame(packet_fragment);
+ }
+
+ return ResendFrame(next_packet_fragment);
+ }
+
+ return RequestFrame(packet_fragment);
+}
+
+DriverResult IrsProtocol::ConfigureIrs() {
+ LOG_DEBUG(Input, "Configure IRS");
+ constexpr std::size_t max_tries = 28;
+ SubCommandResponse output{};
+ std::size_t tries = 0;
+
+ const IrsConfigure irs_configuration{
+ .command = MCUCommand::ConfigureIR,
+ .sub_command = MCUSubCommand::SetDeviceMode,
+ .irs_mode = IrsMode::ImageTransfer,
+ .number_of_fragments = fragments,
+ .mcu_major_version = 0x0500,
+ .mcu_minor_version = 0x1800,
+ .crc = {},
+ };
+ buf_image.resize((static_cast<u8>(fragments) + 1) * 300);
+
+ std::array<u8, sizeof(IrsConfigure)> request_data{};
+ memcpy(request_data.data(), &irs_configuration, sizeof(IrsConfigure));
+ request_data[37] = CalculateMCU_CRC8(request_data.data() + 1, 36);
+ do {
+ const auto result = SendSubCommand(SubCommand::SET_MCU_CONFIG, request_data, output);
+
+ if (result != DriverResult::Success) {
+ return result;
+ }
+ if (tries++ >= max_tries) {
+ return DriverResult::WrongReply;
+ }
+ } while (output.command_data[0] != 0x0b);
+
+ return DriverResult::Success;
+}
+
+DriverResult IrsProtocol::WriteRegistersStep1() {
+ LOG_DEBUG(Input, "WriteRegistersStep1");
+ DriverResult result{DriverResult::Success};
+ constexpr std::size_t max_tries = 28;
+ SubCommandResponse output{};
+ std::size_t tries = 0;
+
+ const IrsWriteRegisters irs_registers{
+ .command = MCUCommand::ConfigureIR,
+ .sub_command = MCUSubCommand::WriteDeviceRegisters,
+ .number_of_registers = 0x9,
+ .registers =
+ {
+ IrsRegister{IrRegistersAddress::Resolution, static_cast<u8>(resolution_code)},
+ {IrRegistersAddress::ExposureLSB, static_cast<u8>(exposure & 0xff)},
+ {IrRegistersAddress::ExposureMSB, static_cast<u8>(exposure >> 8)},
+ {IrRegistersAddress::ExposureTime, 0x00},
+ {IrRegistersAddress::Leds, static_cast<u8>(leds)},
+ {IrRegistersAddress::DigitalGainLSB, static_cast<u8>((digital_gain & 0x0f) << 4)},
+ {IrRegistersAddress::DigitalGainMSB, static_cast<u8>((digital_gain & 0xf0) >> 4)},
+ {IrRegistersAddress::LedFilter, static_cast<u8>(led_filter)},
+ {IrRegistersAddress::WhitePixelThreshold, 0xc8},
+ },
+ .crc = {},
+ };
+
+ std::array<u8, sizeof(IrsWriteRegisters)> request_data{};
+ memcpy(request_data.data(), &irs_registers, sizeof(IrsWriteRegisters));
+ request_data[37] = CalculateMCU_CRC8(request_data.data() + 1, 36);
+
+ std::array<u8, 38> mcu_request{0x02};
+ mcu_request[36] = CalculateMCU_CRC8(mcu_request.data(), 36);
+ mcu_request[37] = 0xFF;
+
+ if (result != DriverResult::Success) {
+ return result;
+ }
+
+ do {
+ result = SendSubCommand(SubCommand::SET_MCU_CONFIG, request_data, output);
+
+ // First time we need to set the report mode
+ if (result == DriverResult::Success && tries == 0) {
+ result = SendMCUCommand(SubCommand::SET_REPORT_MODE, mcu_request);
+ }
+ if (result == DriverResult::Success && tries == 0) {
+ GetSubCommandResponse(SubCommand::SET_MCU_CONFIG, output);
+ }
+
+ if (result != DriverResult::Success) {
+ return result;
+ }
+ if (tries++ >= max_tries) {
+ return DriverResult::WrongReply;
+ }
+ } while (!(output.command_data[0] == 0x13 && output.command_data[2] == 0x07) &&
+ output.command_data[0] != 0x23);
+
+ return DriverResult::Success;
+}
+
+DriverResult IrsProtocol::WriteRegistersStep2() {
+ LOG_DEBUG(Input, "WriteRegistersStep2");
+ constexpr std::size_t max_tries = 28;
+ SubCommandResponse output{};
+ std::size_t tries = 0;
+
+ const IrsWriteRegisters irs_registers{
+ .command = MCUCommand::ConfigureIR,
+ .sub_command = MCUSubCommand::WriteDeviceRegisters,
+ .number_of_registers = 0x8,
+ .registers =
+ {
+ IrsRegister{IrRegistersAddress::LedIntensitiyMSB,
+ static_cast<u8>(led_intensity >> 8)},
+ {IrRegistersAddress::LedIntensitiyLSB, static_cast<u8>(led_intensity & 0xff)},
+ {IrRegistersAddress::ImageFlip, static_cast<u8>(image_flip)},
+ {IrRegistersAddress::DenoiseSmoothing, static_cast<u8>((denoise >> 16) & 0xff)},
+ {IrRegistersAddress::DenoiseEdge, static_cast<u8>((denoise >> 8) & 0xff)},
+ {IrRegistersAddress::DenoiseColor, static_cast<u8>(denoise & 0xff)},
+ {IrRegistersAddress::UpdateTime, 0x2d},
+ {IrRegistersAddress::FinalizeConfig, 0x01},
+ },
+ .crc = {},
+ };
+
+ std::array<u8, sizeof(IrsWriteRegisters)> request_data{};
+ memcpy(request_data.data(), &irs_registers, sizeof(IrsWriteRegisters));
+ request_data[37] = CalculateMCU_CRC8(request_data.data() + 1, 36);
+ do {
+ const auto result = SendSubCommand(SubCommand::SET_MCU_CONFIG, request_data, output);
+
+ if (result != DriverResult::Success) {
+ return result;
+ }
+ if (tries++ >= max_tries) {
+ return DriverResult::WrongReply;
+ }
+ } while (output.command_data[0] != 0x13 && output.command_data[0] != 0x23);
+
+ return DriverResult::Success;
+}
+
+DriverResult IrsProtocol::RequestFrame(u8 frame) {
+ std::array<u8, 38> mcu_request{};
+ mcu_request[3] = frame;
+ mcu_request[36] = CalculateMCU_CRC8(mcu_request.data(), 36);
+ mcu_request[37] = 0xFF;
+ return SendMCUCommand(SubCommand::SET_REPORT_MODE, mcu_request);
+}
+
+DriverResult IrsProtocol::ResendFrame(u8 frame) {
+ std::array<u8, 38> mcu_request{};
+ mcu_request[1] = 0x1;
+ mcu_request[2] = frame;
+ mcu_request[3] = 0x0;
+ mcu_request[36] = CalculateMCU_CRC8(mcu_request.data(), 36);
+ mcu_request[37] = 0xFF;
+ return SendMCUCommand(SubCommand::SET_REPORT_MODE, mcu_request);
+}
+
+std::vector<u8> IrsProtocol::GetImage() const {
+ return buf_image;
+}
+
+IrsResolution IrsProtocol::GetIrsFormat() const {
+ return resolution;
+}
+
+bool IrsProtocol::IsEnabled() const {
+ return is_enabled;
+}
+
+} // 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 @@
+// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+// Based on dkms-hid-nintendo implementation, CTCaer joycon toolkit and dekuNukem reverse
+// engineering https://github.com/nicman23/dkms-hid-nintendo/blob/master/src/hid-nintendo.c
+// https://github.com/CTCaer/jc_toolkit
+// https://github.com/dekuNukem/Nintendo_Switch_Reverse_Engineering
+
+#pragma once
+
+#include <vector>
+
+#include "input_common/helpers/joycon_protocol/common_protocol.h"
+#include "input_common/helpers/joycon_protocol/joycon_types.h"
+
+namespace InputCommon::Joycon {
+
+class IrsProtocol final : private JoyconCommonProtocol {
+public:
+ explicit IrsProtocol(std::shared_ptr<JoyconHandle> handle);
+
+ DriverResult EnableIrs();
+
+ DriverResult DisableIrs();
+
+ DriverResult SetIrsConfig(IrsMode mode, IrsResolution format);
+
+ DriverResult RequestImage(std::span<u8> buffer);
+
+ std::vector<u8> GetImage() const;
+
+ IrsResolution GetIrsFormat() const;
+
+ bool IsEnabled() const;
+
+private:
+ DriverResult ConfigureIrs();
+
+ DriverResult WriteRegistersStep1();
+ DriverResult WriteRegistersStep2();
+
+ DriverResult RequestFrame(u8 frame);
+ DriverResult ResendFrame(u8 frame);
+
+ IrsMode irs_mode{IrsMode::ImageTransfer};
+ IrsResolution resolution{IrsResolution::Size40x30};
+ IrsResolutionCode resolution_code{IrsResolutionCode::Size40x30};
+ IrsFragments fragments{IrsFragments::Size40x30};
+ IrLeds leds{IrLeds::BrightAndDim};
+ IrExLedFilter led_filter{IrExLedFilter::Enabled};
+ IrImageFlip image_flip{IrImageFlip::Normal};
+ u8 digital_gain{0x01};
+ u16 exposure{0x2490};
+ u16 led_intensity{0x0f10};
+ u32 denoise{0x012344};
+
+ u8 packet_fragment{};
+ std::vector<u8> buf_image; // 8bpp greyscale image.
+
+ bool is_enabled{};
+};
+
+} // 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 @@
+// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+// Based on dkms-hid-nintendo implementation, CTCaer joycon toolkit and dekuNukem reverse
+// engineering https://github.com/nicman23/dkms-hid-nintendo/blob/master/src/hid-nintendo.c
+// https://github.com/CTCaer/jc_toolkit
+// https://github.com/dekuNukem/Nintendo_Switch_Reverse_Engineering
+
+#pragma once
+
+#include <array>
+#include <functional>
+#include <SDL_hidapi.h>
+
+#include "common/bit_field.h"
+#include "common/common_funcs.h"
+#include "common/common_types.h"
+
+namespace InputCommon::Joycon {
+constexpr u32 MaxErrorCount = 50;
+constexpr u32 MaxBufferSize = 368;
+constexpr std::array<u8, 8> DefaultVibrationBuffer{0x0, 0x1, 0x40, 0x40, 0x0, 0x1, 0x40, 0x40};
+
+using MacAddress = std::array<u8, 6>;
+using SerialNumber = std::array<u8, 15>;
+
+enum class ControllerType : u8 {
+ None = 0x00,
+ Left = 0x01,
+ Right = 0x02,
+ Pro = 0x03,
+ Dual = 0x05, // TODO: Verify this id
+ LarkHvc1 = 0x07,
+ LarkHvc2 = 0x08,
+ LarkNesLeft = 0x09,
+ LarkNesRight = 0x0A,
+ Lucia = 0x0B,
+ Lagon = 0x0C,
+ Lager = 0x0D,
+};
+
+enum class PadAxes {
+ LeftStickX,
+ LeftStickY,
+ RightStickX,
+ RightStickY,
+ Undefined,
+};
+
+enum class PadMotion {
+ LeftMotion,
+ RightMotion,
+ Undefined,
+};
+
+enum class PadButton : u32 {
+ Down = 0x000001,
+ Up = 0x000002,
+ Right = 0x000004,
+ Left = 0x000008,
+ LeftSR = 0x000010,
+ LeftSL = 0x000020,
+ L = 0x000040,
+ ZL = 0x000080,
+ Y = 0x000100,
+ X = 0x000200,
+ B = 0x000400,
+ A = 0x000800,
+ RightSR = 0x001000,
+ RightSL = 0x002000,
+ R = 0x004000,
+ ZR = 0x008000,
+ Minus = 0x010000,
+ Plus = 0x020000,
+ StickR = 0x040000,
+ StickL = 0x080000,
+ Home = 0x100000,
+ Capture = 0x200000,
+};
+
+enum class PasivePadButton : u32 {
+ Down_A = 0x0001,
+ Right_X = 0x0002,
+ Left_B = 0x0004,
+ Up_Y = 0x0008,
+ SL = 0x0010,
+ SR = 0x0020,
+ Minus = 0x0100,
+ Plus = 0x0200,
+ StickL = 0x0400,
+ StickR = 0x0800,
+ Home = 0x1000,
+ Capture = 0x2000,
+ L_R = 0x4000,
+ ZL_ZR = 0x8000,
+};
+
+enum class OutputReport : u8 {
+ RUMBLE_AND_SUBCMD = 0x01,
+ FW_UPDATE_PKT = 0x03,
+ RUMBLE_ONLY = 0x10,
+ MCU_DATA = 0x11,
+ USB_CMD = 0x80,
+};
+
+enum class FeatureReport : u8 {
+ Last_SUBCMD = 0x02,
+ OTA_GW_UPGRADE = 0x70,
+ SETUP_MEM_READ = 0x71,
+ MEM_READ = 0x72,
+ ERASE_MEM_SECTOR = 0x73,
+ MEM_WRITE = 0x74,
+ LAUNCH = 0x75,
+};
+
+enum class SubCommand : u8 {
+ STATE = 0x00,
+ MANUAL_BT_PAIRING = 0x01,
+ REQ_DEV_INFO = 0x02,
+ SET_REPORT_MODE = 0x03,
+ TRIGGERS_ELAPSED = 0x04,
+ GET_PAGE_LIST_STATE = 0x05,
+ SET_HCI_STATE = 0x06,
+ RESET_PAIRING_INFO = 0x07,
+ LOW_POWER_MODE = 0x08,
+ SPI_FLASH_READ = 0x10,
+ SPI_FLASH_WRITE = 0x11,
+ SPI_SECTOR_ERASE = 0x12,
+ RESET_MCU = 0x20,
+ SET_MCU_CONFIG = 0x21,
+ SET_MCU_STATE = 0x22,
+ SET_PLAYER_LIGHTS = 0x30,
+ GET_PLAYER_LIGHTS = 0x31,
+ SET_HOME_LIGHT = 0x38,
+ ENABLE_IMU = 0x40,
+ SET_IMU_SENSITIVITY = 0x41,
+ WRITE_IMU_REG = 0x42,
+ READ_IMU_REG = 0x43,
+ ENABLE_VIBRATION = 0x48,
+ GET_REGULATED_VOLTAGE = 0x50,
+ SET_EXTERNAL_CONFIG = 0x58,
+ GET_EXTERNAL_DEVICE_INFO = 0x59,
+ ENABLE_EXTERNAL_POLLING = 0x5A,
+ DISABLE_EXTERNAL_POLLING = 0x5B,
+ SET_EXTERNAL_FORMAT_CONFIG = 0x5C,
+};
+
+enum class UsbSubCommand : u8 {
+ CONN_STATUS = 0x01,
+ HADSHAKE = 0x02,
+ BAUDRATE_3M = 0x03,
+ NO_TIMEOUT = 0x04,
+ EN_TIMEOUT = 0x05,
+ RESET = 0x06,
+ PRE_HANDSHAKE = 0x91,
+ SEND_UART = 0x92,
+};
+
+enum class CalibrationMagic : u8 {
+ USR_MAGIC_0 = 0xB2,
+ USR_MAGIC_1 = 0xA1,
+};
+
+enum class SpiAddress : u16 {
+ MAGIC = 0x0000,
+ MAC_ADDRESS = 0x0015,
+ PAIRING_INFO = 0x2000,
+ SHIPMENT = 0x5000,
+ SERIAL_NUMBER = 0x6000,
+ DEVICE_TYPE = 0x6012,
+ FORMAT_VERSION = 0x601B,
+ FACT_IMU_DATA = 0x6020,
+ FACT_LEFT_DATA = 0x603d,
+ FACT_RIGHT_DATA = 0x6046,
+ COLOR_DATA = 0x6050,
+ DESIGN_VARIATION = 0x605C,
+ SENSOR_DATA = 0x6080,
+ USER_LEFT_MAGIC = 0x8010,
+ USER_LEFT_DATA = 0x8012,
+ USER_RIGHT_MAGIC = 0x801B,
+ USER_RIGHT_DATA = 0x801D,
+ USER_IMU_MAGIC = 0x8026,
+ USER_IMU_DATA = 0x8028,
+};
+
+enum class ReportMode : u8 {
+ ACTIVE_POLLING_NFC_IR_CAMERA_DATA = 0x00,
+ ACTIVE_POLLING_NFC_IR_CAMERA_CONFIGURATION = 0x01,
+ ACTIVE_POLLING_NFC_IR_CAMERA_DATA_CONFIGURATION = 0x02,
+ ACTIVE_POLLING_IR_CAMERA_DATA = 0x03,
+ SUBCMD_REPLY = 0x21,
+ MCU_UPDATE_STATE = 0x23,
+ STANDARD_FULL_60HZ = 0x30,
+ NFC_IR_MODE_60HZ = 0x31,
+ SIMPLE_HID_MODE = 0x3F,
+ INPUT_USB_RESPONSE = 0x81,
+};
+
+enum class GyroSensitivity : u8 {
+ DPS250,
+ DPS500,
+ DPS1000,
+ DPS2000, // Default
+};
+
+enum class AccelerometerSensitivity : u8 {
+ G8, // Default
+ G4,
+ G2,
+ G16,
+};
+
+enum class GyroPerformance : u8 {
+ HZ833,
+ HZ208, // Default
+};
+
+enum class AccelerometerPerformance : u8 {
+ HZ200,
+ HZ100, // Default
+};
+
+enum class MCUCommand : u8 {
+ ConfigureMCU = 0x21,
+ ConfigureIR = 0x23,
+};
+
+enum class MCUSubCommand : u8 {
+ SetMCUMode = 0x0,
+ SetDeviceMode = 0x1,
+ ReadDeviceMode = 0x02,
+ WriteDeviceRegisters = 0x4,
+};
+
+enum class MCUMode : u8 {
+ Suspend = 0,
+ Standby = 1,
+ Ringcon = 3,
+ NFC = 4,
+ IR = 5,
+ MaybeFWUpdate = 6,
+};
+
+enum class MCURequest : u8 {
+ GetMCUStatus = 1,
+ GetNFCData = 2,
+ GetIRData = 3,
+};
+
+enum class MCUReport : u8 {
+ Empty = 0x00,
+ StateReport = 0x01,
+ IRData = 0x03,
+ BusyInitializing = 0x0b,
+ IRStatus = 0x13,
+ IRRegisters = 0x1b,
+ NFCState = 0x2a,
+ NFCReadData = 0x3a,
+ EmptyAwaitingCmd = 0xff,
+};
+
+enum class MCUPacketFlag : u8 {
+ MorePacketsRemaining = 0x00,
+ LastCommandPacket = 0x08,
+};
+
+enum class NFCReadCommand : u8 {
+ CancelAll = 0x00,
+ StartPolling = 0x01,
+ StopPolling = 0x02,
+ StartWaitingRecieve = 0x04,
+ Ntag = 0x06,
+ Mifare = 0x0F,
+};
+
+enum class NFCTagType : u8 {
+ AllTags = 0x00,
+ Ntag215 = 0x01,
+};
+
+enum class NFCPages {
+ Block0 = 0,
+ Block45 = 45,
+ Block135 = 135,
+ Block231 = 231,
+};
+
+enum class NFCStatus : u8 {
+ LastPackage = 0x04,
+ TagLost = 0x07,
+};
+
+enum class IrsMode : u8 {
+ None = 0x02,
+ Moment = 0x03,
+ Dpd = 0x04,
+ Clustering = 0x06,
+ ImageTransfer = 0x07,
+ Silhouette = 0x08,
+ TeraImage = 0x09,
+ SilhouetteTeraImage = 0x0A,
+};
+
+enum class IrsResolution {
+ Size320x240,
+ Size160x120,
+ Size80x60,
+ Size40x30,
+ Size20x15,
+ None,
+};
+
+enum class IrsResolutionCode : u8 {
+ Size320x240 = 0x00, // Full pixel array
+ Size160x120 = 0x50, // Sensor Binning [2 X 2]
+ Size80x60 = 0x64, // Sensor Binning [4 x 2] and Skipping [1 x 2]
+ Size40x30 = 0x69, // Sensor Binning [4 x 2] and Skipping [2 x 4]
+ Size20x15 = 0x6A, // Sensor Binning [4 x 2] and Skipping [4 x 4]
+};
+
+// Size of image divided by 300
+enum class IrsFragments : u8 {
+ Size20x15 = 0x00,
+ Size40x30 = 0x03,
+ Size80x60 = 0x0f,
+ Size160x120 = 0x3f,
+ Size320x240 = 0xFF,
+};
+
+enum class IrLeds : u8 {
+ BrightAndDim = 0x00,
+ Bright = 0x20,
+ Dim = 0x10,
+ None = 0x30,
+};
+
+enum class IrExLedFilter : u8 {
+ Disabled = 0x00,
+ Enabled = 0x03,
+};
+
+enum class IrImageFlip : u8 {
+ Normal = 0x00,
+ Inverted = 0x02,
+};
+
+enum class IrRegistersAddress : u16 {
+ UpdateTime = 0x0400,
+ FinalizeConfig = 0x0700,
+ LedFilter = 0x0e00,
+ Leds = 0x1000,
+ LedIntensitiyMSB = 0x1100,
+ LedIntensitiyLSB = 0x1200,
+ ImageFlip = 0x2d00,
+ Resolution = 0x2e00,
+ DigitalGainLSB = 0x2e01,
+ DigitalGainMSB = 0x2f01,
+ ExposureLSB = 0x3001,
+ ExposureMSB = 0x3101,
+ ExposureTime = 0x3201,
+ WhitePixelThreshold = 0x4301,
+ DenoiseSmoothing = 0x6701,
+ DenoiseEdge = 0x6801,
+ DenoiseColor = 0x6901,
+};
+
+enum class ExternalDeviceId : u16 {
+ RingController = 0x2000,
+ Starlink = 0x2800,
+};
+
+enum class DriverResult {
+ Success,
+ WrongReply,
+ Timeout,
+ InvalidParameters,
+ UnsupportedControllerType,
+ HandleInUse,
+ ErrorReadingData,
+ ErrorWritingData,
+ NoDeviceDetected,
+ InvalidHandle,
+ NotSupported,
+ Disabled,
+ Unknown,
+};
+
+struct MotionSensorCalibration {
+ s16 offset;
+ s16 scale;
+};
+
+struct MotionCalibration {
+ std::array<MotionSensorCalibration, 3> accelerometer;
+ std::array<MotionSensorCalibration, 3> gyro;
+};
+
+// Basic motion data containing data from the sensors and a timestamp in microseconds
+struct MotionData {
+ float gyro_x{};
+ float gyro_y{};
+ float gyro_z{};
+ float accel_x{};
+ float accel_y{};
+ float accel_z{};
+ u64 delta_timestamp{};
+};
+
+// Output from SPI read command containing user calibration magic
+struct MagicSpiCalibration {
+ CalibrationMagic first;
+ CalibrationMagic second;
+};
+static_assert(sizeof(MagicSpiCalibration) == 0x2, "MagicSpiCalibration is an invalid size");
+
+// Output from SPI read command containing left joystick calibration
+struct JoystickLeftSpiCalibration {
+ std::array<u8, 3> max;
+ std::array<u8, 3> center;
+ std::array<u8, 3> min;
+};
+static_assert(sizeof(JoystickLeftSpiCalibration) == 0x9,
+ "JoystickLeftSpiCalibration is an invalid size");
+
+// Output from SPI read command containing right joystick calibration
+struct JoystickRightSpiCalibration {
+ std::array<u8, 3> center;
+ std::array<u8, 3> min;
+ std::array<u8, 3> max;
+};
+static_assert(sizeof(JoystickRightSpiCalibration) == 0x9,
+ "JoystickRightSpiCalibration is an invalid size");
+
+struct JoyStickAxisCalibration {
+ u16 max;
+ u16 min;
+ u16 center;
+};
+
+struct JoyStickCalibration {
+ JoyStickAxisCalibration x;
+ JoyStickAxisCalibration y;
+};
+
+struct ImuSpiCalibration {
+ std::array<s16, 3> accelerometer_offset;
+ std::array<s16, 3> accelerometer_scale;
+ std::array<s16, 3> gyroscope_offset;
+ std::array<s16, 3> gyroscope_scale;
+};
+static_assert(sizeof(ImuSpiCalibration) == 0x18, "ImuSpiCalibration is an invalid size");
+
+struct RingCalibration {
+ s16 default_value;
+ s16 max_value;
+ s16 min_value;
+};
+
+struct Color {
+ u32 body;
+ u32 buttons;
+ u32 left_grip;
+ u32 right_grip;
+};
+
+struct Battery {
+ union {
+ u8 raw{};
+
+ BitField<0, 4, u8> unknown;
+ BitField<4, 1, u8> charging;
+ BitField<5, 3, u8> status;
+ };
+};
+
+struct VibrationValue {
+ f32 low_amplitude;
+ f32 low_frequency;
+ f32 high_amplitude;
+ f32 high_frequency;
+};
+
+struct JoyconHandle {
+ SDL_hid_device* handle = nullptr;
+ u8 packet_counter{};
+};
+
+struct MCUConfig {
+ MCUCommand command;
+ MCUSubCommand sub_command;
+ MCUMode mode;
+ INSERT_PADDING_BYTES(0x22);
+ u8 crc;
+};
+static_assert(sizeof(MCUConfig) == 0x26, "MCUConfig is an invalid size");
+
+#pragma pack(push, 1)
+struct InputReportPassive {
+ ReportMode report_mode;
+ u16 button_input;
+ u8 stick_state;
+ std::array<u8, 10> unknown_data;
+};
+static_assert(sizeof(InputReportPassive) == 0xE, "InputReportPassive is an invalid size");
+
+struct InputReportActive {
+ ReportMode report_mode;
+ u8 packet_id;
+ Battery battery_status;
+ std::array<u8, 3> button_input;
+ std::array<u8, 3> left_stick_state;
+ std::array<u8, 3> right_stick_state;
+ u8 vibration_code;
+ std::array<s16, 6 * 2> motion_input;
+ INSERT_PADDING_BYTES(0x2);
+ s16 ring_input;
+};
+static_assert(sizeof(InputReportActive) == 0x29, "InputReportActive is an invalid size");
+
+struct InputReportNfcIr {
+ ReportMode report_mode;
+ u8 packet_id;
+ Battery battery_status;
+ std::array<u8, 3> button_input;
+ std::array<u8, 3> left_stick_state;
+ std::array<u8, 3> right_stick_state;
+ u8 vibration_code;
+ std::array<s16, 6 * 2> motion_input;
+ INSERT_PADDING_BYTES(0x4);
+};
+static_assert(sizeof(InputReportNfcIr) == 0x29, "InputReportNfcIr is an invalid size");
+#pragma pack(pop)
+
+struct NFCReadBlock {
+ u8 start;
+ u8 end;
+};
+static_assert(sizeof(NFCReadBlock) == 0x2, "NFCReadBlock is an invalid size");
+
+struct NFCReadBlockCommand {
+ u8 block_count{};
+ std::array<NFCReadBlock, 4> blocks{};
+};
+static_assert(sizeof(NFCReadBlockCommand) == 0x9, "NFCReadBlockCommand is an invalid size");
+
+struct NFCReadCommandData {
+ u8 unknown;
+ u8 uuid_length;
+ u8 unknown_2;
+ std::array<u8, 6> uid;
+ NFCTagType tag_type;
+ NFCReadBlockCommand read_block;
+};
+static_assert(sizeof(NFCReadCommandData) == 0x13, "NFCReadCommandData is an invalid size");
+
+struct NFCPollingCommandData {
+ u8 enable_mifare;
+ u8 unknown_1;
+ u8 unknown_2;
+ u8 unknown_3;
+ u8 unknown_4;
+};
+static_assert(sizeof(NFCPollingCommandData) == 0x05, "NFCPollingCommandData is an invalid size");
+
+struct NFCRequestState {
+ MCUSubCommand sub_command;
+ NFCReadCommand command_argument;
+ u8 packet_id;
+ INSERT_PADDING_BYTES(0x1);
+ MCUPacketFlag packet_flag;
+ u8 data_length;
+ union {
+ std::array<u8, 0x1F> raw_data;
+ NFCReadCommandData nfc_read;
+ NFCPollingCommandData nfc_polling;
+ };
+ u8 crc;
+};
+static_assert(sizeof(NFCRequestState) == 0x26, "NFCRequestState is an invalid size");
+
+struct IrsConfigure {
+ MCUCommand command;
+ MCUSubCommand sub_command;
+ IrsMode irs_mode;
+ IrsFragments number_of_fragments;
+ u16 mcu_major_version;
+ u16 mcu_minor_version;
+ INSERT_PADDING_BYTES(0x1D);
+ u8 crc;
+};
+static_assert(sizeof(IrsConfigure) == 0x26, "IrsConfigure is an invalid size");
+
+#pragma pack(push, 1)
+struct IrsRegister {
+ IrRegistersAddress address;
+ u8 value;
+};
+static_assert(sizeof(IrsRegister) == 0x3, "IrsRegister is an invalid size");
+
+struct IrsWriteRegisters {
+ MCUCommand command;
+ MCUSubCommand sub_command;
+ u8 number_of_registers;
+ std::array<IrsRegister, 9> registers;
+ INSERT_PADDING_BYTES(0x7);
+ u8 crc;
+};
+static_assert(sizeof(IrsWriteRegisters) == 0x26, "IrsWriteRegisters is an invalid size");
+#pragma pack(pop)
+
+struct FirmwareVersion {
+ u8 major;
+ u8 minor;
+};
+static_assert(sizeof(FirmwareVersion) == 0x2, "FirmwareVersion is an invalid size");
+
+struct DeviceInfo {
+ FirmwareVersion firmware;
+ std::array<u8, 2> unknown_1;
+ MacAddress mac_address;
+ std::array<u8, 2> unknown_2;
+};
+static_assert(sizeof(DeviceInfo) == 0xC, "DeviceInfo is an invalid size");
+
+struct MotionStatus {
+ bool is_enabled;
+ u64 delta_time;
+ GyroSensitivity gyro_sensitivity;
+ AccelerometerSensitivity accelerometer_sensitivity;
+};
+
+struct RingStatus {
+ bool is_enabled;
+ s16 default_value;
+ s16 max_value;
+ s16 min_value;
+};
+
+struct VibrationPacket {
+ OutputReport output_report;
+ u8 packet_counter;
+ std::array<u8, 0x8> vibration_data;
+};
+static_assert(sizeof(VibrationPacket) == 0xA, "VibrationPacket is an invalid size");
+
+struct SubCommandPacket {
+ OutputReport output_report;
+ u8 packet_counter;
+ INSERT_PADDING_BYTES(0x8); // This contains vibration data
+ SubCommand sub_command;
+ std::array<u8, 0x26> command_data;
+};
+static_assert(sizeof(SubCommandPacket) == 0x31, "SubCommandPacket is an invalid size");
+
+#pragma pack(push, 1)
+struct ReadSpiPacket {
+ SpiAddress spi_address;
+ INSERT_PADDING_BYTES(0x2);
+ u8 size;
+};
+static_assert(sizeof(ReadSpiPacket) == 0x5, "ReadSpiPacket is an invalid size");
+
+struct SubCommandResponse {
+ InputReportPassive input_report;
+ SubCommand sub_command;
+ union {
+ std::array<u8, 0x30> command_data;
+ SpiAddress spi_address; // Reply from SPI_FLASH_READ subcommand
+ ExternalDeviceId external_device_id; // Reply from GET_EXTERNAL_DEVICE_INFO subcommand
+ DeviceInfo device_info; // Reply from REQ_DEV_INFO subcommand
+ };
+ u8 crc; // This is never used
+};
+static_assert(sizeof(SubCommandResponse) == 0x40, "SubCommandResponse is an invalid size");
+#pragma pack(pop)
+
+struct MCUCommandResponse {
+ InputReportNfcIr input_report;
+ INSERT_PADDING_BYTES(0x8);
+ MCUReport mcu_report;
+ std::array<u8, 0x13D> mcu_data;
+ u8 crc;
+};
+static_assert(sizeof(MCUCommandResponse) == 0x170, "MCUCommandResponse is an invalid size");
+
+struct JoyconCallbacks {
+ std::function<void(Battery)> on_battery_data;
+ std::function<void(Color)> on_color_data;
+ std::function<void(int, bool)> on_button_data;
+ std::function<void(int, f32)> on_stick_data;
+ std::function<void(int, const MotionData&)> on_motion_data;
+ std::function<void(f32)> on_ring_data;
+ std::function<void(const std::vector<u8>&)> on_amiibo_data;
+ std::function<void(const std::vector<u8>&, IrsResolution)> on_camera_data;
+};
+
+} // 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 @@
+// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include <thread>
+#include "common/logging/log.h"
+#include "input_common/helpers/joycon_protocol/nfc.h"
+
+namespace InputCommon::Joycon {
+
+NfcProtocol::NfcProtocol(std::shared_ptr<JoyconHandle> handle)
+ : JoyconCommonProtocol(std::move(handle)) {}
+
+DriverResult NfcProtocol::EnableNfc() {
+ LOG_INFO(Input, "Enable NFC");
+ ScopedSetBlocking sb(this);
+ DriverResult result{DriverResult::Success};
+
+ if (result == DriverResult::Success) {
+ result = SetReportMode(ReportMode::NFC_IR_MODE_60HZ);
+ }
+ if (result == DriverResult::Success) {
+ result = EnableMCU(true);
+ }
+ if (result == DriverResult::Success) {
+ result = WaitSetMCUMode(ReportMode::NFC_IR_MODE_60HZ, MCUMode::Standby);
+ }
+ if (result == DriverResult::Success) {
+ const MCUConfig config{
+ .command = MCUCommand::ConfigureMCU,
+ .sub_command = MCUSubCommand::SetMCUMode,
+ .mode = MCUMode::NFC,
+ .crc = {},
+ };
+
+ result = ConfigureMCU(config);
+ }
+
+ return result;
+}
+
+DriverResult NfcProtocol::DisableNfc() {
+ LOG_DEBUG(Input, "Disable NFC");
+ ScopedSetBlocking sb(this);
+ DriverResult result{DriverResult::Success};
+
+ if (result == DriverResult::Success) {
+ result = EnableMCU(false);
+ }
+
+ is_enabled = false;
+
+ return result;
+}
+
+DriverResult NfcProtocol::StartNFCPollingMode() {
+ LOG_DEBUG(Input, "Start NFC pooling Mode");
+ ScopedSetBlocking sb(this);
+ DriverResult result{DriverResult::Success};
+ TagFoundData tag_data{};
+
+ if (result == DriverResult::Success) {
+ result = WaitSetMCUMode(ReportMode::NFC_IR_MODE_60HZ, MCUMode::NFC);
+ }
+ if (result == DriverResult::Success) {
+ result = WaitUntilNfcIsReady();
+ }
+ if (result == DriverResult::Success) {
+ is_enabled = true;
+ }
+
+ return result;
+}
+
+DriverResult NfcProtocol::ScanAmiibo(std::vector<u8>& data) {
+ LOG_DEBUG(Input, "Start NFC pooling Mode");
+ ScopedSetBlocking sb(this);
+ DriverResult result{DriverResult::Success};
+ TagFoundData tag_data{};
+
+ if (result == DriverResult::Success) {
+ result = StartPolling(tag_data);
+ }
+ if (result == DriverResult::Success) {
+ result = ReadTag(tag_data);
+ }
+ if (result == DriverResult::Success) {
+ result = WaitUntilNfcIsReady();
+ }
+ if (result == DriverResult::Success) {
+ result = StartPolling(tag_data);
+ }
+ if (result == DriverResult::Success) {
+ result = GetAmiiboData(data);
+ }
+
+ return result;
+}
+
+bool NfcProtocol::HasAmiibo() {
+ ScopedSetBlocking sb(this);
+ DriverResult result{DriverResult::Success};
+ TagFoundData tag_data{};
+
+ if (result == DriverResult::Success) {
+ result = StartPolling(tag_data);
+ }
+
+ return result == DriverResult::Success;
+}
+
+DriverResult NfcProtocol::WaitUntilNfcIsReady() {
+ constexpr std::size_t timeout_limit = 10;
+ MCUCommandResponse output{};
+ std::size_t tries = 0;
+
+ do {
+ auto result = SendStartWaitingRecieveRequest(output);
+
+ if (result != DriverResult::Success) {
+ return result;
+ }
+ if (tries++ > timeout_limit) {
+ return DriverResult::Timeout;
+ }
+ } while (output.mcu_report != MCUReport::NFCState ||
+ (output.mcu_data[1] << 8) + output.mcu_data[0] != 0x0500 ||
+ output.mcu_data[5] != 0x31 || output.mcu_data[6] != 0x00);
+
+ return DriverResult::Success;
+}
+
+DriverResult NfcProtocol::StartPolling(TagFoundData& data) {
+ LOG_DEBUG(Input, "Start Polling for tag");
+ constexpr std::size_t timeout_limit = 7;
+ MCUCommandResponse output{};
+ std::size_t tries = 0;
+
+ do {
+ const auto result = SendStartPollingRequest(output);
+ if (result != DriverResult::Success) {
+ return result;
+ }
+ if (tries++ > timeout_limit) {
+ return DriverResult::Timeout;
+ }
+ } while (output.mcu_report != MCUReport::NFCState ||
+ (output.mcu_data[1] << 8) + output.mcu_data[0] != 0x0500 ||
+ output.mcu_data[6] != 0x09);
+
+ data.type = output.mcu_data[12];
+ data.uuid.resize(output.mcu_data[14]);
+ memcpy(data.uuid.data(), output.mcu_data.data() + 15, data.uuid.size());
+
+ return DriverResult::Success;
+}
+
+DriverResult NfcProtocol::ReadTag(const TagFoundData& data) {
+ constexpr std::size_t timeout_limit = 10;
+ MCUCommandResponse output{};
+ std::size_t tries = 0;
+
+ std::string uuid_string;
+ for (auto& content : data.uuid) {
+ uuid_string += fmt::format(" {:02x}", content);
+ }
+
+ LOG_INFO(Input, "Tag detected, type={}, uuid={}", data.type, uuid_string);
+
+ tries = 0;
+ NFCPages ntag_pages = NFCPages::Block0;
+ // Read Tag data
+ while (true) {
+ auto result = SendReadAmiiboRequest(output, ntag_pages);
+ const auto nfc_status = static_cast<NFCStatus>(output.mcu_data[6]);
+
+ if (result != DriverResult::Success) {
+ return result;
+ }
+
+ if ((output.mcu_report == MCUReport::NFCReadData ||
+ output.mcu_report == MCUReport::NFCState) &&
+ nfc_status == NFCStatus::TagLost) {
+ return DriverResult::ErrorReadingData;
+ }
+
+ if (output.mcu_report == MCUReport::NFCReadData && output.mcu_data[1] == 0x07 &&
+ output.mcu_data[2] == 0x01) {
+ if (data.type != 2) {
+ continue;
+ }
+ switch (output.mcu_data[24]) {
+ case 0:
+ ntag_pages = NFCPages::Block135;
+ break;
+ case 3:
+ ntag_pages = NFCPages::Block45;
+ break;
+ case 4:
+ ntag_pages = NFCPages::Block231;
+ break;
+ default:
+ return DriverResult::ErrorReadingData;
+ }
+ continue;
+ }
+
+ if (output.mcu_report == MCUReport::NFCState && nfc_status == NFCStatus::LastPackage) {
+ // finished
+ SendStopPollingRequest(output);
+ return DriverResult::Success;
+ }
+
+ // Ignore other state reports
+ if (output.mcu_report == MCUReport::NFCState) {
+ continue;
+ }
+
+ if (tries++ > timeout_limit) {
+ return DriverResult::Timeout;
+ }
+ }
+
+ return DriverResult::Success;
+}
+
+DriverResult NfcProtocol::GetAmiiboData(std::vector<u8>& ntag_data) {
+ constexpr std::size_t timeout_limit = 10;
+ MCUCommandResponse output{};
+ std::size_t tries = 0;
+
+ NFCPages ntag_pages = NFCPages::Block135;
+ std::size_t ntag_buffer_pos = 0;
+ // Read Tag data
+ while (true) {
+ auto result = SendReadAmiiboRequest(output, ntag_pages);
+ const auto nfc_status = static_cast<NFCStatus>(output.mcu_data[6]);
+
+ if (result != DriverResult::Success) {
+ return result;
+ }
+
+ if ((output.mcu_report == MCUReport::NFCReadData ||
+ output.mcu_report == MCUReport::NFCState) &&
+ nfc_status == NFCStatus::TagLost) {
+ return DriverResult::ErrorReadingData;
+ }
+
+ if (output.mcu_report == MCUReport::NFCReadData && output.mcu_data[1] == 0x07) {
+ std::size_t payload_size = (output.mcu_data[4] << 8 | output.mcu_data[5]) & 0x7FF;
+ if (output.mcu_data[2] == 0x01) {
+ memcpy(ntag_data.data() + ntag_buffer_pos, output.mcu_data.data() + 66,
+ payload_size - 60);
+ ntag_buffer_pos += payload_size - 60;
+ } else {
+ memcpy(ntag_data.data() + ntag_buffer_pos, output.mcu_data.data() + 6,
+ payload_size);
+ }
+ continue;
+ }
+
+ if (output.mcu_report == MCUReport::NFCState && nfc_status == NFCStatus::LastPackage) {
+ LOG_INFO(Input, "Finished reading amiibo");
+ return DriverResult::Success;
+ }
+
+ // Ignore other state reports
+ if (output.mcu_report == MCUReport::NFCState) {
+ continue;
+ }
+
+ if (tries++ > timeout_limit) {
+ return DriverResult::Timeout;
+ }
+ }
+
+ return DriverResult::Success;
+}
+
+DriverResult NfcProtocol::SendStartPollingRequest(MCUCommandResponse& output) {
+ NFCRequestState request{
+ .sub_command = MCUSubCommand::ReadDeviceMode,
+ .command_argument = NFCReadCommand::StartPolling,
+ .packet_id = 0x0,
+ .packet_flag = MCUPacketFlag::LastCommandPacket,
+ .data_length = sizeof(NFCPollingCommandData),
+ .nfc_polling =
+ {
+ .enable_mifare = 0x01,
+ .unknown_1 = 0x00,
+ .unknown_2 = 0x00,
+ .unknown_3 = 0x2c,
+ .unknown_4 = 0x01,
+ },
+ .crc = {},
+ };
+
+ std::array<u8, sizeof(NFCRequestState)> request_data{};
+ memcpy(request_data.data(), &request, sizeof(NFCRequestState));
+ request_data[37] = CalculateMCU_CRC8(request_data.data() + 1, 36);
+ return SendMCUData(ReportMode::NFC_IR_MODE_60HZ, SubCommand::STATE, request_data, output);
+}
+
+DriverResult NfcProtocol::SendStopPollingRequest(MCUCommandResponse& output) {
+ NFCRequestState request{
+ .sub_command = MCUSubCommand::ReadDeviceMode,
+ .command_argument = NFCReadCommand::StopPolling,
+ .packet_id = 0x0,
+ .packet_flag = MCUPacketFlag::LastCommandPacket,
+ .data_length = 0,
+ .raw_data = {},
+ .crc = {},
+ };
+
+ std::array<u8, sizeof(NFCRequestState)> request_data{};
+ memcpy(request_data.data(), &request, sizeof(NFCRequestState));
+ request_data[37] = CalculateMCU_CRC8(request_data.data() + 1, 36);
+ return SendMCUData(ReportMode::NFC_IR_MODE_60HZ, SubCommand::STATE, request_data, output);
+}
+
+DriverResult NfcProtocol::SendStartWaitingRecieveRequest(MCUCommandResponse& output) {
+ NFCRequestState request{
+ .sub_command = MCUSubCommand::ReadDeviceMode,
+ .command_argument = NFCReadCommand::StartWaitingRecieve,
+ .packet_id = 0x0,
+ .packet_flag = MCUPacketFlag::LastCommandPacket,
+ .data_length = 0,
+ .raw_data = {},
+ .crc = {},
+ };
+
+ std::vector<u8> request_data(sizeof(NFCRequestState));
+ memcpy(request_data.data(), &request, sizeof(NFCRequestState));
+ request_data[37] = CalculateMCU_CRC8(request_data.data() + 1, 36);
+ return SendMCUData(ReportMode::NFC_IR_MODE_60HZ, SubCommand::STATE, request_data, output);
+}
+
+DriverResult NfcProtocol::SendReadAmiiboRequest(MCUCommandResponse& output, NFCPages ntag_pages) {
+ NFCRequestState request{
+ .sub_command = MCUSubCommand::ReadDeviceMode,
+ .command_argument = NFCReadCommand::Ntag,
+ .packet_id = 0x0,
+ .packet_flag = MCUPacketFlag::LastCommandPacket,
+ .data_length = sizeof(NFCReadCommandData),
+ .nfc_read =
+ {
+ .unknown = 0xd0,
+ .uuid_length = 0x07,
+ .unknown_2 = 0x00,
+ .uid = {},
+ .tag_type = NFCTagType::AllTags,
+ .read_block = GetReadBlockCommand(ntag_pages),
+ },
+ .crc = {},
+ };
+
+ std::array<u8, sizeof(NFCRequestState)> request_data{};
+ memcpy(request_data.data(), &request, sizeof(NFCRequestState));
+ request_data[37] = CalculateMCU_CRC8(request_data.data() + 1, 36);
+ return SendMCUData(ReportMode::NFC_IR_MODE_60HZ, SubCommand::STATE, request_data, output);
+}
+
+NFCReadBlockCommand NfcProtocol::GetReadBlockCommand(NFCPages pages) const {
+ switch (pages) {
+ case NFCPages::Block0:
+ return {
+ .block_count = 1,
+ };
+ case NFCPages::Block45:
+ return {
+ .block_count = 1,
+ .blocks =
+ {
+ NFCReadBlock{0x00, 0x2C},
+ },
+ };
+ case NFCPages::Block135:
+ return {
+ .block_count = 3,
+ .blocks =
+ {
+ NFCReadBlock{0x00, 0x3b},
+ {0x3c, 0x77},
+ {0x78, 0x86},
+ },
+ };
+ case NFCPages::Block231:
+ return {
+ .block_count = 4,
+ .blocks =
+ {
+ NFCReadBlock{0x00, 0x3b},
+ {0x3c, 0x77},
+ {0x78, 0x83},
+ {0xb4, 0xe6},
+ },
+ };
+ default:
+ return {};
+ };
+}
+
+bool NfcProtocol::IsEnabled() const {
+ return is_enabled;
+}
+
+} // 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 @@
+// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+// Based on dkms-hid-nintendo implementation, CTCaer joycon toolkit and dekuNukem reverse
+// engineering https://github.com/nicman23/dkms-hid-nintendo/blob/master/src/hid-nintendo.c
+// https://github.com/CTCaer/jc_toolkit
+// https://github.com/dekuNukem/Nintendo_Switch_Reverse_Engineering
+
+#pragma once
+
+#include <vector>
+
+#include "input_common/helpers/joycon_protocol/common_protocol.h"
+#include "input_common/helpers/joycon_protocol/joycon_types.h"
+
+namespace InputCommon::Joycon {
+
+class NfcProtocol final : private JoyconCommonProtocol {
+public:
+ explicit NfcProtocol(std::shared_ptr<JoyconHandle> handle);
+
+ DriverResult EnableNfc();
+
+ DriverResult DisableNfc();
+
+ DriverResult StartNFCPollingMode();
+
+ DriverResult ScanAmiibo(std::vector<u8>& data);
+
+ bool HasAmiibo();
+
+ bool IsEnabled() const;
+
+private:
+ struct TagFoundData {
+ u8 type;
+ std::vector<u8> uuid;
+ };
+
+ DriverResult WaitUntilNfcIsReady();
+
+ DriverResult StartPolling(TagFoundData& data);
+
+ DriverResult ReadTag(const TagFoundData& data);
+
+ DriverResult GetAmiiboData(std::vector<u8>& data);
+
+ DriverResult SendStartPollingRequest(MCUCommandResponse& output);
+
+ DriverResult SendStopPollingRequest(MCUCommandResponse& output);
+
+ DriverResult SendStartWaitingRecieveRequest(MCUCommandResponse& output);
+
+ DriverResult SendReadAmiiboRequest(MCUCommandResponse& output, NFCPages ntag_pages);
+
+ NFCReadBlockCommand GetReadBlockCommand(NFCPages pages) const;
+
+ bool is_enabled{};
+};
+
+} // 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 @@
+// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include "common/logging/log.h"
+#include "input_common/helpers/joycon_protocol/poller.h"
+
+namespace InputCommon::Joycon {
+
+JoyconPoller::JoyconPoller(ControllerType device_type_, JoyStickCalibration left_stick_calibration_,
+ JoyStickCalibration right_stick_calibration_,
+ MotionCalibration motion_calibration_)
+ : device_type{device_type_}, left_stick_calibration{left_stick_calibration_},
+ right_stick_calibration{right_stick_calibration_}, motion_calibration{motion_calibration_} {}
+
+void JoyconPoller::SetCallbacks(const Joycon::JoyconCallbacks& callbacks_) {
+ callbacks = std::move(callbacks_);
+}
+
+void JoyconPoller::ReadActiveMode(std::span<u8> buffer, const MotionStatus& motion_status,
+ const RingStatus& ring_status) {
+ InputReportActive data{};
+ memcpy(&data, buffer.data(), sizeof(InputReportActive));
+
+ switch (device_type) {
+ case Joycon::ControllerType::Left:
+ UpdateActiveLeftPadInput(data, motion_status);
+ break;
+ case Joycon::ControllerType::Right:
+ UpdateActiveRightPadInput(data, motion_status);
+ break;
+ case Joycon::ControllerType::Pro:
+ UpdateActiveProPadInput(data, motion_status);
+ break;
+ default:
+ break;
+ }
+
+ if (ring_status.is_enabled) {
+ UpdateRing(data.ring_input, ring_status);
+ }
+
+ callbacks.on_battery_data(data.battery_status);
+}
+
+void JoyconPoller::ReadPassiveMode(std::span<u8> buffer) {
+ InputReportPassive data{};
+ memcpy(&data, buffer.data(), sizeof(InputReportPassive));
+
+ switch (device_type) {
+ case Joycon::ControllerType::Left:
+ UpdatePasiveLeftPadInput(data);
+ break;
+ case Joycon::ControllerType::Right:
+ UpdatePasiveRightPadInput(data);
+ break;
+ case Joycon::ControllerType::Pro:
+ UpdatePasiveProPadInput(data);
+ break;
+ default:
+ break;
+ }
+}
+
+void JoyconPoller::ReadNfcIRMode(std::span<u8> buffer, const MotionStatus& motion_status) {
+ // This mode is compatible with the active mode
+ ReadActiveMode(buffer, motion_status, {});
+}
+
+void JoyconPoller::UpdateColor(const Color& color) {
+ callbacks.on_color_data(color);
+}
+
+void JoyconPoller::UpdateAmiibo(const std::vector<u8>& amiibo_data) {
+ callbacks.on_amiibo_data(amiibo_data);
+}
+
+void JoyconPoller::UpdateCamera(const std::vector<u8>& camera_data, IrsResolution format) {
+ callbacks.on_camera_data(camera_data, format);
+}
+
+void JoyconPoller::UpdateRing(s16 value, const RingStatus& ring_status) {
+ float normalized_value = static_cast<float>(value - ring_status.default_value);
+ if (normalized_value > 0) {
+ normalized_value = normalized_value /
+ static_cast<float>(ring_status.max_value - ring_status.default_value);
+ }
+ if (normalized_value < 0) {
+ normalized_value = normalized_value /
+ static_cast<float>(ring_status.default_value - ring_status.min_value);
+ }
+ callbacks.on_ring_data(normalized_value);
+}
+
+void JoyconPoller::UpdateActiveLeftPadInput(const InputReportActive& input,
+ const MotionStatus& motion_status) {
+ static constexpr std::array<Joycon::PadButton, 11> left_buttons{
+ Joycon::PadButton::Down, Joycon::PadButton::Up, Joycon::PadButton::Right,
+ Joycon::PadButton::Left, Joycon::PadButton::LeftSL, Joycon::PadButton::LeftSR,
+ Joycon::PadButton::L, Joycon::PadButton::ZL, Joycon::PadButton::Minus,
+ Joycon::PadButton::Capture, Joycon::PadButton::StickL,
+ };
+
+ const u32 raw_button =
+ static_cast<u32>(input.button_input[2] | ((input.button_input[1] & 0b00101001) << 16));
+ for (std::size_t i = 0; i < left_buttons.size(); ++i) {
+ const bool button_status = (raw_button & static_cast<u32>(left_buttons[i])) != 0;
+ const int button = static_cast<int>(left_buttons[i]);
+ callbacks.on_button_data(button, button_status);
+ }
+
+ const u16 raw_left_axis_x =
+ static_cast<u16>(input.left_stick_state[0] | ((input.left_stick_state[1] & 0xf) << 8));
+ const u16 raw_left_axis_y =
+ static_cast<u16>((input.left_stick_state[1] >> 4) | (input.left_stick_state[2] << 4));
+ const f32 left_axis_x = GetAxisValue(raw_left_axis_x, left_stick_calibration.x);
+ const f32 left_axis_y = GetAxisValue(raw_left_axis_y, left_stick_calibration.y);
+ callbacks.on_stick_data(static_cast<int>(PadAxes::LeftStickX), left_axis_x);
+ callbacks.on_stick_data(static_cast<int>(PadAxes::LeftStickY), left_axis_y);
+
+ if (motion_status.is_enabled) {
+ auto left_motion = GetMotionInput(input, motion_status);
+ // Rotate motion axis to the correct direction
+ left_motion.accel_y = -left_motion.accel_y;
+ left_motion.accel_z = -left_motion.accel_z;
+ left_motion.gyro_x = -left_motion.gyro_x;
+ callbacks.on_motion_data(static_cast<int>(PadMotion::LeftMotion), left_motion);
+ }
+}
+
+void JoyconPoller::UpdateActiveRightPadInput(const InputReportActive& input,
+ const MotionStatus& motion_status) {
+ static constexpr std::array<Joycon::PadButton, 11> right_buttons{
+ Joycon::PadButton::Y, Joycon::PadButton::X, Joycon::PadButton::B,
+ Joycon::PadButton::A, Joycon::PadButton::RightSL, Joycon::PadButton::RightSR,
+ Joycon::PadButton::R, Joycon::PadButton::ZR, Joycon::PadButton::Plus,
+ Joycon::PadButton::Home, Joycon::PadButton::StickR,
+ };
+
+ const u32 raw_button =
+ static_cast<u32>((input.button_input[0] << 8) | (input.button_input[1] << 16));
+ for (std::size_t i = 0; i < right_buttons.size(); ++i) {
+ const bool button_status = (raw_button & static_cast<u32>(right_buttons[i])) != 0;
+ const int button = static_cast<int>(right_buttons[i]);
+ callbacks.on_button_data(button, button_status);
+ }
+
+ const u16 raw_right_axis_x =
+ static_cast<u16>(input.right_stick_state[0] | ((input.right_stick_state[1] & 0xf) << 8));
+ const u16 raw_right_axis_y =
+ static_cast<u16>((input.right_stick_state[1] >> 4) | (input.right_stick_state[2] << 4));
+ const f32 right_axis_x = GetAxisValue(raw_right_axis_x, right_stick_calibration.x);
+ const f32 right_axis_y = GetAxisValue(raw_right_axis_y, right_stick_calibration.y);
+ callbacks.on_stick_data(static_cast<int>(PadAxes::RightStickX), right_axis_x);
+ callbacks.on_stick_data(static_cast<int>(PadAxes::RightStickY), right_axis_y);
+
+ if (motion_status.is_enabled) {
+ auto right_motion = GetMotionInput(input, motion_status);
+ // Rotate motion axis to the correct direction
+ right_motion.accel_x = -right_motion.accel_x;
+ right_motion.accel_y = -right_motion.accel_y;
+ right_motion.gyro_z = -right_motion.gyro_z;
+ callbacks.on_motion_data(static_cast<int>(PadMotion::RightMotion), right_motion);
+ }
+}
+
+void JoyconPoller::UpdateActiveProPadInput(const InputReportActive& input,
+ const MotionStatus& motion_status) {
+ static constexpr std::array<Joycon::PadButton, 18> pro_buttons{
+ Joycon::PadButton::Down, Joycon::PadButton::Up, Joycon::PadButton::Right,
+ Joycon::PadButton::Left, Joycon::PadButton::L, Joycon::PadButton::ZL,
+ Joycon::PadButton::Minus, Joycon::PadButton::Capture, Joycon::PadButton::Y,
+ Joycon::PadButton::X, Joycon::PadButton::B, Joycon::PadButton::A,
+ Joycon::PadButton::R, Joycon::PadButton::ZR, Joycon::PadButton::Plus,
+ Joycon::PadButton::Home, Joycon::PadButton::StickL, Joycon::PadButton::StickR,
+ };
+
+ const u32 raw_button = static_cast<u32>(input.button_input[2] | (input.button_input[0] << 8) |
+ (input.button_input[1] << 16));
+ for (std::size_t i = 0; i < pro_buttons.size(); ++i) {
+ const bool button_status = (raw_button & static_cast<u32>(pro_buttons[i])) != 0;
+ const int button = static_cast<int>(pro_buttons[i]);
+ callbacks.on_button_data(button, button_status);
+ }
+
+ const u16 raw_left_axis_x =
+ static_cast<u16>(input.left_stick_state[0] | ((input.left_stick_state[1] & 0xf) << 8));
+ const u16 raw_left_axis_y =
+ static_cast<u16>((input.left_stick_state[1] >> 4) | (input.left_stick_state[2] << 4));
+ const u16 raw_right_axis_x =
+ static_cast<u16>(input.right_stick_state[0] | ((input.right_stick_state[1] & 0xf) << 8));
+ const u16 raw_right_axis_y =
+ static_cast<u16>((input.right_stick_state[1] >> 4) | (input.right_stick_state[2] << 4));
+
+ const f32 left_axis_x = GetAxisValue(raw_left_axis_x, left_stick_calibration.x);
+ const f32 left_axis_y = GetAxisValue(raw_left_axis_y, left_stick_calibration.y);
+ const f32 right_axis_x = GetAxisValue(raw_right_axis_x, right_stick_calibration.x);
+ const f32 right_axis_y = GetAxisValue(raw_right_axis_y, right_stick_calibration.y);
+ callbacks.on_stick_data(static_cast<int>(PadAxes::LeftStickX), left_axis_x);
+ callbacks.on_stick_data(static_cast<int>(PadAxes::LeftStickY), left_axis_y);
+ callbacks.on_stick_data(static_cast<int>(PadAxes::RightStickX), right_axis_x);
+ callbacks.on_stick_data(static_cast<int>(PadAxes::RightStickY), right_axis_y);
+
+ if (motion_status.is_enabled) {
+ auto pro_motion = GetMotionInput(input, motion_status);
+ pro_motion.gyro_x = -pro_motion.gyro_x;
+ pro_motion.accel_y = -pro_motion.accel_y;
+ pro_motion.accel_z = -pro_motion.accel_z;
+ callbacks.on_motion_data(static_cast<int>(PadMotion::LeftMotion), pro_motion);
+ callbacks.on_motion_data(static_cast<int>(PadMotion::RightMotion), pro_motion);
+ }
+}
+
+void JoyconPoller::UpdatePasiveLeftPadInput(const InputReportPassive& input) {
+ static constexpr std::array<Joycon::PasivePadButton, 11> left_buttons{
+ Joycon::PasivePadButton::Down_A, Joycon::PasivePadButton::Right_X,
+ Joycon::PasivePadButton::Left_B, Joycon::PasivePadButton::Up_Y,
+ Joycon::PasivePadButton::SL, Joycon::PasivePadButton::SR,
+ Joycon::PasivePadButton::L_R, Joycon::PasivePadButton::ZL_ZR,
+ Joycon::PasivePadButton::Minus, Joycon::PasivePadButton::Capture,
+ Joycon::PasivePadButton::StickL,
+ };
+
+ for (auto left_button : left_buttons) {
+ const bool button_status = (input.button_input & static_cast<u32>(left_button)) != 0;
+ const int button = static_cast<int>(left_button);
+ callbacks.on_button_data(button, button_status);
+ }
+}
+
+void JoyconPoller::UpdatePasiveRightPadInput(const InputReportPassive& input) {
+ static constexpr std::array<Joycon::PasivePadButton, 11> right_buttons{
+ Joycon::PasivePadButton::Down_A, Joycon::PasivePadButton::Right_X,
+ Joycon::PasivePadButton::Left_B, Joycon::PasivePadButton::Up_Y,
+ Joycon::PasivePadButton::SL, Joycon::PasivePadButton::SR,
+ Joycon::PasivePadButton::L_R, Joycon::PasivePadButton::ZL_ZR,
+ Joycon::PasivePadButton::Plus, Joycon::PasivePadButton::Home,
+ Joycon::PasivePadButton::StickR,
+ };
+
+ for (auto right_button : right_buttons) {
+ const bool button_status = (input.button_input & static_cast<u32>(right_button)) != 0;
+ const int button = static_cast<int>(right_button);
+ callbacks.on_button_data(button, button_status);
+ }
+}
+
+void JoyconPoller::UpdatePasiveProPadInput(const InputReportPassive& input) {
+ static constexpr std::array<Joycon::PasivePadButton, 14> pro_buttons{
+ Joycon::PasivePadButton::Down_A, Joycon::PasivePadButton::Right_X,
+ Joycon::PasivePadButton::Left_B, Joycon::PasivePadButton::Up_Y,
+ Joycon::PasivePadButton::SL, Joycon::PasivePadButton::SR,
+ Joycon::PasivePadButton::L_R, Joycon::PasivePadButton::ZL_ZR,
+ Joycon::PasivePadButton::Minus, Joycon::PasivePadButton::Plus,
+ Joycon::PasivePadButton::Capture, Joycon::PasivePadButton::Home,
+ Joycon::PasivePadButton::StickL, Joycon::PasivePadButton::StickR,
+ };
+
+ for (auto pro_button : pro_buttons) {
+ const bool button_status = (input.button_input & static_cast<u32>(pro_button)) != 0;
+ const int button = static_cast<int>(pro_button);
+ callbacks.on_button_data(button, button_status);
+ }
+}
+
+f32 JoyconPoller::GetAxisValue(u16 raw_value, Joycon::JoyStickAxisCalibration calibration) const {
+ const f32 value = static_cast<f32>(raw_value - calibration.center);
+ if (value > 0.0f) {
+ return value / calibration.max;
+ }
+ return value / calibration.min;
+}
+
+f32 JoyconPoller::GetAccelerometerValue(s16 raw, const MotionSensorCalibration& cal,
+ AccelerometerSensitivity sensitivity) const {
+ const f32 value = raw * (1.0f / (cal.scale - cal.offset)) * 4;
+ switch (sensitivity) {
+ case Joycon::AccelerometerSensitivity::G2:
+ return value / 4.0f;
+ case Joycon::AccelerometerSensitivity::G4:
+ return value / 2.0f;
+ case Joycon::AccelerometerSensitivity::G8:
+ return value;
+ case Joycon::AccelerometerSensitivity::G16:
+ return value * 2.0f;
+ }
+ return value;
+}
+
+f32 JoyconPoller::GetGyroValue(s16 raw, const MotionSensorCalibration& cal,
+ GyroSensitivity sensitivity) const {
+ const f32 value = (raw - cal.offset) * (936.0f / (cal.scale - cal.offset)) / 360.0f;
+ switch (sensitivity) {
+ case Joycon::GyroSensitivity::DPS250:
+ return value / 8.0f;
+ case Joycon::GyroSensitivity::DPS500:
+ return value / 4.0f;
+ case Joycon::GyroSensitivity::DPS1000:
+ return value / 2.0f;
+ case Joycon::GyroSensitivity::DPS2000:
+ return value;
+ }
+ return value;
+}
+
+s16 JoyconPoller::GetRawIMUValues(std::size_t sensor, size_t axis,
+ const InputReportActive& input) const {
+ return input.motion_input[(sensor * 3) + axis];
+}
+
+MotionData JoyconPoller::GetMotionInput(const InputReportActive& input,
+ const MotionStatus& motion_status) const {
+ MotionData motion{};
+ const auto& accel_cal = motion_calibration.accelerometer;
+ const auto& gyro_cal = motion_calibration.gyro;
+ const s16 raw_accel_x = input.motion_input[1];
+ const s16 raw_accel_y = input.motion_input[0];
+ const s16 raw_accel_z = input.motion_input[2];
+ const s16 raw_gyro_x = input.motion_input[4];
+ const s16 raw_gyro_y = input.motion_input[3];
+ const s16 raw_gyro_z = input.motion_input[5];
+
+ motion.delta_timestamp = motion_status.delta_time;
+ motion.accel_x =
+ GetAccelerometerValue(raw_accel_x, accel_cal[1], motion_status.accelerometer_sensitivity);
+ motion.accel_y =
+ GetAccelerometerValue(raw_accel_y, accel_cal[0], motion_status.accelerometer_sensitivity);
+ motion.accel_z =
+ GetAccelerometerValue(raw_accel_z, accel_cal[2], motion_status.accelerometer_sensitivity);
+ motion.gyro_x = GetGyroValue(raw_gyro_x, gyro_cal[1], motion_status.gyro_sensitivity);
+ motion.gyro_y = GetGyroValue(raw_gyro_y, gyro_cal[0], motion_status.gyro_sensitivity);
+ motion.gyro_z = GetGyroValue(raw_gyro_z, gyro_cal[2], motion_status.gyro_sensitivity);
+
+ // TODO(German77): Return all three samples data
+ return motion;
+}
+
+} // 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 @@
+// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+// Based on dkms-hid-nintendo implementation, CTCaer joycon toolkit and dekuNukem reverse
+// engineering https://github.com/nicman23/dkms-hid-nintendo/blob/master/src/hid-nintendo.c
+// https://github.com/CTCaer/jc_toolkit
+// https://github.com/dekuNukem/Nintendo_Switch_Reverse_Engineering
+
+#pragma once
+
+#include <functional>
+#include <span>
+
+#include "input_common/helpers/joycon_protocol/joycon_types.h"
+
+namespace InputCommon::Joycon {
+
+// Handles input packages and triggers the corresponding input events
+class JoyconPoller {
+public:
+ JoyconPoller(ControllerType device_type_, JoyStickCalibration left_stick_calibration_,
+ JoyStickCalibration right_stick_calibration_,
+ MotionCalibration motion_calibration_);
+
+ void SetCallbacks(const Joycon::JoyconCallbacks& callbacks_);
+
+ /// Handles data from passive packages
+ void ReadPassiveMode(std::span<u8> buffer);
+
+ /// Handles data from active packages
+ void ReadActiveMode(std::span<u8> buffer, const MotionStatus& motion_status,
+ const RingStatus& ring_status);
+
+ /// Handles data from nfc or ir packages
+ void ReadNfcIRMode(std::span<u8> buffer, const MotionStatus& motion_status);
+
+ void UpdateColor(const Color& color);
+ void UpdateRing(s16 value, const RingStatus& ring_status);
+ void UpdateAmiibo(const std::vector<u8>& amiibo_data);
+ void UpdateCamera(const std::vector<u8>& amiibo_data, IrsResolution format);
+
+private:
+ void UpdateActiveLeftPadInput(const InputReportActive& input,
+ const MotionStatus& motion_status);
+ void UpdateActiveRightPadInput(const InputReportActive& input,
+ const MotionStatus& motion_status);
+ void UpdateActiveProPadInput(const InputReportActive& input, const MotionStatus& motion_status);
+
+ void UpdatePasiveLeftPadInput(const InputReportPassive& buffer);
+ void UpdatePasiveRightPadInput(const InputReportPassive& buffer);
+ void UpdatePasiveProPadInput(const InputReportPassive& buffer);
+
+ /// Returns a calibrated joystick axis from raw axis data
+ f32 GetAxisValue(u16 raw_value, Joycon::JoyStickAxisCalibration calibration) const;
+
+ /// Returns a calibrated accelerometer axis from raw motion data
+ f32 GetAccelerometerValue(s16 raw, const MotionSensorCalibration& cal,
+ AccelerometerSensitivity sensitivity) const;
+
+ /// Returns a calibrated gyro axis from raw motion data
+ f32 GetGyroValue(s16 raw_value, const MotionSensorCalibration& cal,
+ GyroSensitivity sensitivity) const;
+
+ /// Returns a raw motion value from a buffer
+ s16 GetRawIMUValues(size_t sensor, size_t axis, const InputReportActive& input) const;
+
+ /// Returns motion data from a buffer
+ MotionData GetMotionInput(const InputReportActive& input,
+ const MotionStatus& motion_status) const;
+
+ ControllerType device_type{};
+
+ // Device calibration
+ JoyStickCalibration left_stick_calibration{};
+ JoyStickCalibration right_stick_calibration{};
+ MotionCalibration motion_calibration{};
+
+ Joycon::JoyconCallbacks callbacks{};
+};
+
+} // 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 @@
+// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include "common/logging/log.h"
+#include "input_common/helpers/joycon_protocol/ringcon.h"
+
+namespace InputCommon::Joycon {
+
+RingConProtocol::RingConProtocol(std::shared_ptr<JoyconHandle> handle)
+ : JoyconCommonProtocol(std::move(handle)) {}
+
+DriverResult RingConProtocol::EnableRingCon() {
+ LOG_DEBUG(Input, "Enable Ringcon");
+ ScopedSetBlocking sb(this);
+ DriverResult result{DriverResult::Success};
+
+ if (result == DriverResult::Success) {
+ result = SetReportMode(ReportMode::STANDARD_FULL_60HZ);
+ }
+ if (result == DriverResult::Success) {
+ result = EnableMCU(true);
+ }
+ if (result == DriverResult::Success) {
+ const MCUConfig config{
+ .command = MCUCommand::ConfigureMCU,
+ .sub_command = MCUSubCommand::SetDeviceMode,
+ .mode = MCUMode::Standby,
+ .crc = {},
+ };
+ result = ConfigureMCU(config);
+ }
+
+ return result;
+}
+
+DriverResult RingConProtocol::DisableRingCon() {
+ LOG_DEBUG(Input, "Disable RingCon");
+ ScopedSetBlocking sb(this);
+ DriverResult result{DriverResult::Success};
+
+ if (result == DriverResult::Success) {
+ result = EnableMCU(false);
+ }
+
+ is_enabled = false;
+
+ return result;
+}
+
+DriverResult RingConProtocol::StartRingconPolling() {
+ LOG_DEBUG(Input, "Enable Ringcon");
+ ScopedSetBlocking sb(this);
+ DriverResult result{DriverResult::Success};
+ bool is_connected = false;
+
+ if (result == DriverResult::Success) {
+ result = IsRingConnected(is_connected);
+ }
+ if (result == DriverResult::Success && is_connected) {
+ LOG_INFO(Input, "Ringcon detected");
+ result = ConfigureRing();
+ }
+ if (result == DriverResult::Success) {
+ is_enabled = true;
+ }
+
+ return result;
+}
+
+DriverResult RingConProtocol::IsRingConnected(bool& is_connected) {
+ LOG_DEBUG(Input, "IsRingConnected");
+ constexpr std::size_t max_tries = 28;
+ SubCommandResponse output{};
+ std::size_t tries = 0;
+ is_connected = false;
+
+ do {
+ const auto result = SendSubCommand(SubCommand::GET_EXTERNAL_DEVICE_INFO, {}, output);
+
+ if (result != DriverResult::Success) {
+ return result;
+ }
+
+ if (tries++ >= max_tries) {
+ return DriverResult::NoDeviceDetected;
+ }
+ } while (output.external_device_id != ExternalDeviceId::RingController);
+
+ is_connected = true;
+ return DriverResult::Success;
+}
+
+DriverResult RingConProtocol::ConfigureRing() {
+ LOG_DEBUG(Input, "ConfigureRing");
+
+ static constexpr std::array<u8, 37> ring_config{
+ 0x06, 0x03, 0x25, 0x06, 0x00, 0x00, 0x00, 0x00, 0x1C, 0x16, 0xED, 0x34, 0x36,
+ 0x00, 0x00, 0x00, 0x0A, 0x64, 0x0B, 0xE6, 0xA9, 0x22, 0x00, 0x00, 0x04, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x90, 0xA8, 0xE1, 0x34, 0x36};
+
+ const DriverResult result = SendSubCommand(SubCommand::SET_EXTERNAL_FORMAT_CONFIG, ring_config);
+
+ if (result != DriverResult::Success) {
+ return result;
+ }
+
+ static constexpr std::array<u8, 4> ringcon_data{0x04, 0x01, 0x01, 0x02};
+ return SendSubCommand(SubCommand::ENABLE_EXTERNAL_POLLING, ringcon_data);
+}
+
+bool RingConProtocol::IsEnabled() const {
+ return is_enabled;
+}
+
+} // 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 @@
+// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+// Based on dkms-hid-nintendo implementation, CTCaer joycon toolkit and dekuNukem reverse
+// engineering https://github.com/nicman23/dkms-hid-nintendo/blob/master/src/hid-nintendo.c
+// https://github.com/CTCaer/jc_toolkit
+// https://github.com/dekuNukem/Nintendo_Switch_Reverse_Engineering
+
+#pragma once
+
+#include <vector>
+
+#include "input_common/helpers/joycon_protocol/common_protocol.h"
+#include "input_common/helpers/joycon_protocol/joycon_types.h"
+
+namespace InputCommon::Joycon {
+
+class RingConProtocol final : private JoyconCommonProtocol {
+public:
+ explicit RingConProtocol(std::shared_ptr<JoyconHandle> handle);
+
+ DriverResult EnableRingCon();
+
+ DriverResult DisableRingCon();
+
+ DriverResult StartRingconPolling();
+
+ bool IsEnabled() const;
+
+private:
+ DriverResult IsRingConnected(bool& is_connected);
+
+ DriverResult ConfigureRing();
+
+ bool is_enabled{};
+};
+
+} // 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 @@
+// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include <algorithm>
+#include <cmath>
+
+#include "common/logging/log.h"
+#include "input_common/helpers/joycon_protocol/rumble.h"
+
+namespace InputCommon::Joycon {
+
+RumbleProtocol::RumbleProtocol(std::shared_ptr<JoyconHandle> handle)
+ : JoyconCommonProtocol(std::move(handle)) {}
+
+DriverResult RumbleProtocol::EnableRumble(bool is_enabled) {
+ LOG_DEBUG(Input, "Enable Rumble");
+ ScopedSetBlocking sb(this);
+ const std::array<u8, 1> buffer{static_cast<u8>(is_enabled ? 1 : 0)};
+ return SendSubCommand(SubCommand::ENABLE_VIBRATION, buffer);
+}
+
+DriverResult RumbleProtocol::SendVibration(const VibrationValue& vibration) {
+ std::array<u8, sizeof(DefaultVibrationBuffer)> buffer{};
+
+ if (vibration.high_amplitude <= 0.0f && vibration.low_amplitude <= 0.0f) {
+ return SendVibrationReport(DefaultVibrationBuffer);
+ }
+
+ // Protect joycons from damage from strong vibrations
+ const f32 clamp_amplitude =
+ 1.0f / std::max(1.0f, vibration.high_amplitude + vibration.low_amplitude);
+
+ const u16 encoded_high_frequency = EncodeHighFrequency(vibration.high_frequency);
+ const u8 encoded_high_amplitude =
+ EncodeHighAmplitude(vibration.high_amplitude * clamp_amplitude);
+ const u8 encoded_low_frequency = EncodeLowFrequency(vibration.low_frequency);
+ const u16 encoded_low_amplitude = EncodeLowAmplitude(vibration.low_amplitude * clamp_amplitude);
+
+ buffer[0] = static_cast<u8>(encoded_high_frequency & 0xFF);
+ buffer[1] = static_cast<u8>(encoded_high_amplitude | ((encoded_high_frequency >> 8) & 0x01));
+ buffer[2] = static_cast<u8>(encoded_low_frequency | ((encoded_low_amplitude >> 8) & 0x80));
+ buffer[3] = static_cast<u8>(encoded_low_amplitude & 0xFF);
+
+ // Duplicate rumble for now
+ buffer[4] = buffer[0];
+ buffer[5] = buffer[1];
+ buffer[6] = buffer[2];
+ buffer[7] = buffer[3];
+
+ return SendVibrationReport(buffer);
+}
+
+u16 RumbleProtocol::EncodeHighFrequency(f32 frequency) const {
+ const u8 new_frequency =
+ static_cast<u8>(std::clamp(std::log2(frequency / 10.0f) * 32.0f, 0.0f, 255.0f));
+ return static_cast<u16>((new_frequency - 0x60) * 4);
+}
+
+u8 RumbleProtocol::EncodeLowFrequency(f32 frequency) const {
+ const u8 new_frequency =
+ static_cast<u8>(std::clamp(std::log2(frequency / 10.0f) * 32.0f, 0.0f, 255.0f));
+ return static_cast<u8>(new_frequency - 0x40);
+}
+
+u8 RumbleProtocol::EncodeHighAmplitude(f32 amplitude) const {
+ // More information about these values can be found here:
+ // https://github.com/dekuNukem/Nintendo_Switch_Reverse_Engineering/blob/master/rumble_data_table.md
+
+ static constexpr std::array<std::pair<f32, int>, 101> high_fequency_amplitude{
+ std::pair<f32, int>{0.0f, 0x0},
+ {0.01f, 0x2},
+ {0.012f, 0x4},
+ {0.014f, 0x6},
+ {0.017f, 0x8},
+ {0.02f, 0x0a},
+ {0.024f, 0x0c},
+ {0.028f, 0x0e},
+ {0.033f, 0x10},
+ {0.04f, 0x12},
+ {0.047f, 0x14},
+ {0.056f, 0x16},
+ {0.067f, 0x18},
+ {0.08f, 0x1a},
+ {0.095f, 0x1c},
+ {0.112f, 0x1e},
+ {0.117f, 0x20},
+ {0.123f, 0x22},
+ {0.128f, 0x24},
+ {0.134f, 0x26},
+ {0.14f, 0x28},
+ {0.146f, 0x2a},
+ {0.152f, 0x2c},
+ {0.159f, 0x2e},
+ {0.166f, 0x30},
+ {0.173f, 0x32},
+ {0.181f, 0x34},
+ {0.189f, 0x36},
+ {0.198f, 0x38},
+ {0.206f, 0x3a},
+ {0.215f, 0x3c},
+ {0.225f, 0x3e},
+ {0.23f, 0x40},
+ {0.235f, 0x42},
+ {0.24f, 0x44},
+ {0.245f, 0x46},
+ {0.251f, 0x48},
+ {0.256f, 0x4a},
+ {0.262f, 0x4c},
+ {0.268f, 0x4e},
+ {0.273f, 0x50},
+ {0.279f, 0x52},
+ {0.286f, 0x54},
+ {0.292f, 0x56},
+ {0.298f, 0x58},
+ {0.305f, 0x5a},
+ {0.311f, 0x5c},
+ {0.318f, 0x5e},
+ {0.325f, 0x60},
+ {0.332f, 0x62},
+ {0.34f, 0x64},
+ {0.347f, 0x66},
+ {0.355f, 0x68},
+ {0.362f, 0x6a},
+ {0.37f, 0x6c},
+ {0.378f, 0x6e},
+ {0.387f, 0x70},
+ {0.395f, 0x72},
+ {0.404f, 0x74},
+ {0.413f, 0x76},
+ {0.422f, 0x78},
+ {0.431f, 0x7a},
+ {0.44f, 0x7c},
+ {0.45f, 0x7e},
+ {0.46f, 0x80},
+ {0.47f, 0x82},
+ {0.48f, 0x84},
+ {0.491f, 0x86},
+ {0.501f, 0x88},
+ {0.512f, 0x8a},
+ {0.524f, 0x8c},
+ {0.535f, 0x8e},
+ {0.547f, 0x90},
+ {0.559f, 0x92},
+ {0.571f, 0x94},
+ {0.584f, 0x96},
+ {0.596f, 0x98},
+ {0.609f, 0x9a},
+ {0.623f, 0x9c},
+ {0.636f, 0x9e},
+ {0.65f, 0xa0},
+ {0.665f, 0xa2},
+ {0.679f, 0xa4},
+ {0.694f, 0xa6},
+ {0.709f, 0xa8},
+ {0.725f, 0xaa},
+ {0.741f, 0xac},
+ {0.757f, 0xae},
+ {0.773f, 0xb0},
+ {0.79f, 0xb2},
+ {0.808f, 0xb4},
+ {0.825f, 0xb6},
+ {0.843f, 0xb8},
+ {0.862f, 0xba},
+ {0.881f, 0xbc},
+ {0.9f, 0xbe},
+ {0.92f, 0xc0},
+ {0.94f, 0xc2},
+ {0.96f, 0xc4},
+ {0.981f, 0xc6},
+ {1.003f, 0xc8},
+ };
+
+ for (const auto& [amplitude_value, code] : high_fequency_amplitude) {
+ if (amplitude <= amplitude_value) {
+ return static_cast<u8>(code);
+ }
+ }
+
+ return static_cast<u8>(high_fequency_amplitude[high_fequency_amplitude.size() - 1].second);
+}
+
+u16 RumbleProtocol::EncodeLowAmplitude(f32 amplitude) const {
+ // More information about these values can be found here:
+ // https://github.com/dekuNukem/Nintendo_Switch_Reverse_Engineering/blob/master/rumble_data_table.md
+
+ static constexpr std::array<std::pair<f32, int>, 101> high_fequency_amplitude{
+ std::pair<f32, int>{0.0f, 0x0040},
+ {0.01f, 0x8040},
+ {0.012f, 0x0041},
+ {0.014f, 0x8041},
+ {0.017f, 0x0042},
+ {0.02f, 0x8042},
+ {0.024f, 0x0043},
+ {0.028f, 0x8043},
+ {0.033f, 0x0044},
+ {0.04f, 0x8044},
+ {0.047f, 0x0045},
+ {0.056f, 0x8045},
+ {0.067f, 0x0046},
+ {0.08f, 0x8046},
+ {0.095f, 0x0047},
+ {0.112f, 0x8047},
+ {0.117f, 0x0048},
+ {0.123f, 0x8048},
+ {0.128f, 0x0049},
+ {0.134f, 0x8049},
+ {0.14f, 0x004a},
+ {0.146f, 0x804a},
+ {0.152f, 0x004b},
+ {0.159f, 0x804b},
+ {0.166f, 0x004c},
+ {0.173f, 0x804c},
+ {0.181f, 0x004d},
+ {0.189f, 0x804d},
+ {0.198f, 0x004e},
+ {0.206f, 0x804e},
+ {0.215f, 0x004f},
+ {0.225f, 0x804f},
+ {0.23f, 0x0050},
+ {0.235f, 0x8050},
+ {0.24f, 0x0051},
+ {0.245f, 0x8051},
+ {0.251f, 0x0052},
+ {0.256f, 0x8052},
+ {0.262f, 0x0053},
+ {0.268f, 0x8053},
+ {0.273f, 0x0054},
+ {0.279f, 0x8054},
+ {0.286f, 0x0055},
+ {0.292f, 0x8055},
+ {0.298f, 0x0056},
+ {0.305f, 0x8056},
+ {0.311f, 0x0057},
+ {0.318f, 0x8057},
+ {0.325f, 0x0058},
+ {0.332f, 0x8058},
+ {0.34f, 0x0059},
+ {0.347f, 0x8059},
+ {0.355f, 0x005a},
+ {0.362f, 0x805a},
+ {0.37f, 0x005b},
+ {0.378f, 0x805b},
+ {0.387f, 0x005c},
+ {0.395f, 0x805c},
+ {0.404f, 0x005d},
+ {0.413f, 0x805d},
+ {0.422f, 0x005e},
+ {0.431f, 0x805e},
+ {0.44f, 0x005f},
+ {0.45f, 0x805f},
+ {0.46f, 0x0060},
+ {0.47f, 0x8060},
+ {0.48f, 0x0061},
+ {0.491f, 0x8061},
+ {0.501f, 0x0062},
+ {0.512f, 0x8062},
+ {0.524f, 0x0063},
+ {0.535f, 0x8063},
+ {0.547f, 0x0064},
+ {0.559f, 0x8064},
+ {0.571f, 0x0065},
+ {0.584f, 0x8065},
+ {0.596f, 0x0066},
+ {0.609f, 0x8066},
+ {0.623f, 0x0067},
+ {0.636f, 0x8067},
+ {0.65f, 0x0068},
+ {0.665f, 0x8068},
+ {0.679f, 0x0069},
+ {0.694f, 0x8069},
+ {0.709f, 0x006a},
+ {0.725f, 0x806a},
+ {0.741f, 0x006b},
+ {0.757f, 0x806b},
+ {0.773f, 0x006c},
+ {0.79f, 0x806c},
+ {0.808f, 0x006d},
+ {0.825f, 0x806d},
+ {0.843f, 0x006e},
+ {0.862f, 0x806e},
+ {0.881f, 0x006f},
+ {0.9f, 0x806f},
+ {0.92f, 0x0070},
+ {0.94f, 0x8070},
+ {0.96f, 0x0071},
+ {0.981f, 0x8071},
+ {1.003f, 0x0072},
+ };
+
+ for (const auto& [amplitude_value, code] : high_fequency_amplitude) {
+ if (amplitude <= amplitude_value) {
+ return static_cast<u16>(code);
+ }
+ }
+
+ return static_cast<u16>(high_fequency_amplitude[high_fequency_amplitude.size() - 1].second);
+}
+
+} // 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 @@
+// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+// Based on dkms-hid-nintendo implementation, CTCaer joycon toolkit and dekuNukem reverse
+// engineering https://github.com/nicman23/dkms-hid-nintendo/blob/master/src/hid-nintendo.c
+// https://github.com/CTCaer/jc_toolkit
+// https://github.com/dekuNukem/Nintendo_Switch_Reverse_Engineering
+
+#pragma once
+
+#include <vector>
+
+#include "input_common/helpers/joycon_protocol/common_protocol.h"
+#include "input_common/helpers/joycon_protocol/joycon_types.h"
+
+namespace InputCommon::Joycon {
+
+class RumbleProtocol final : private JoyconCommonProtocol {
+public:
+ explicit RumbleProtocol(std::shared_ptr<JoyconHandle> handle);
+
+ DriverResult EnableRumble(bool is_enabled);
+
+ DriverResult SendVibration(const VibrationValue& vibration);
+
+private:
+ u16 EncodeHighFrequency(f32 frequency) const;
+ u8 EncodeLowFrequency(f32 frequency) const;
+ u8 EncodeHighAmplitude(f32 amplitude) const;
+ u16 EncodeLowAmplitude(f32 amplitude) const;
+};
+
+} // namespace InputCommon::Joycon
diff --git a/src/input_common/helpers/stick_from_buttons.cpp b/src/input_common/helpers/stick_from_buttons.cpp
index 82aa6ac2f..a6be6dac1 100644
--- a/src/input_common/helpers/stick_from_buttons.cpp
+++ b/src/input_common/helpers/stick_from_buttons.cpp
@@ -11,13 +11,21 @@ namespace InputCommon {
class Stick final : public Common::Input::InputDevice {
public:
+ // Some games such as EARTH DEFENSE FORCE: WORLD BROTHERS
+ // do not play nicely with the theoretical maximum range.
+ // Using a value one lower from the maximum emulates real stick behavior.
+ static constexpr float MAX_RANGE = 32766.0f / 32767.0f;
+ static constexpr float TAU = Common::PI * 2.0f;
+ // Use wider angle to ease the transition.
+ static constexpr float APERTURE = TAU * 0.15f;
+
using Button = std::unique_ptr<Common::Input::InputDevice>;
- Stick(Button up_, Button down_, Button left_, Button right_, Button modifier_,
+ Stick(Button up_, Button down_, Button left_, Button right_, Button modifier_, Button updater_,
float modifier_scale_, float modifier_angle_)
: up(std::move(up_)), down(std::move(down_)), left(std::move(left_)),
- right(std::move(right_)), modifier(std::move(modifier_)), modifier_scale(modifier_scale_),
- modifier_angle(modifier_angle_) {
+ right(std::move(right_)), modifier(std::move(modifier_)), updater(std::move(updater_)),
+ modifier_scale(modifier_scale_), modifier_angle(modifier_angle_) {
up->SetCallback({
.on_change =
[this](const Common::Input::CallbackStatus& callback_) {
@@ -48,35 +56,31 @@ public:
UpdateModButtonStatus(callback_);
},
});
+ updater->SetCallback({
+ .on_change = [this](const Common::Input::CallbackStatus& callback_) { SoftUpdate(); },
+ });
last_x_axis_value = 0.0f;
last_y_axis_value = 0.0f;
}
bool IsAngleGreater(float old_angle, float new_angle) const {
- constexpr float TAU = Common::PI * 2.0f;
- // Use wider angle to ease the transition.
- constexpr float aperture = TAU * 0.15f;
- const float top_limit = new_angle + aperture;
+ const float top_limit = new_angle + APERTURE;
return (old_angle > new_angle && old_angle <= top_limit) ||
(old_angle + TAU > new_angle && old_angle + TAU <= top_limit);
}
bool IsAngleSmaller(float old_angle, float new_angle) const {
- constexpr float TAU = Common::PI * 2.0f;
- // Use wider angle to ease the transition.
- constexpr float aperture = TAU * 0.15f;
- const float bottom_limit = new_angle - aperture;
+ const float bottom_limit = new_angle - APERTURE;
return (old_angle >= bottom_limit && old_angle < new_angle) ||
(old_angle - TAU >= bottom_limit && old_angle - TAU < new_angle);
}
float GetAngle(std::chrono::time_point<std::chrono::steady_clock> now) const {
- constexpr float TAU = Common::PI * 2.0f;
float new_angle = angle;
auto time_difference = static_cast<float>(
- std::chrono::duration_cast<std::chrono::microseconds>(now - last_update).count());
- time_difference /= 1000.0f * 1000.0f;
+ std::chrono::duration_cast<std::chrono::milliseconds>(now - last_update).count());
+ time_difference /= 1000.0f;
if (time_difference > 0.5f) {
time_difference = 0.5f;
}
@@ -193,8 +197,6 @@ public:
}
void UpdateStatus() {
- const float coef = modifier_status.value ? modifier_scale : 1.0f;
-
bool r = right_status;
bool l = left_status;
bool u = up_status;
@@ -212,7 +214,7 @@ public:
// Move if a key is pressed
if (r || l || u || d) {
- amplitude = coef;
+ amplitude = modifier_status.value ? modifier_scale : MAX_RANGE;
} else {
amplitude = 0;
}
@@ -248,7 +250,7 @@ public:
modifier->ForceUpdate();
}
- void SoftUpdate() override {
+ void SoftUpdate() {
Common::Input::CallbackStatus status{
.type = Common::Input::InputType::Stick,
.stick_status = GetStatus(),
@@ -266,30 +268,17 @@ public:
Common::Input::StickStatus status{};
status.x.properties = properties;
status.y.properties = properties;
+
if (Settings::values.emulate_analog_keyboard) {
const auto now = std::chrono::steady_clock::now();
- float angle_ = GetAngle(now);
+ const float angle_ = GetAngle(now);
status.x.raw_value = std::cos(angle_) * amplitude;
status.y.raw_value = std::sin(angle_) * amplitude;
return status;
}
- constexpr float SQRT_HALF = 0.707106781f;
- int x = 0, y = 0;
- if (right_status) {
- ++x;
- }
- if (left_status) {
- --x;
- }
- if (up_status) {
- ++y;
- }
- if (down_status) {
- --y;
- }
- const float coef = modifier_status.value ? modifier_scale : 1.0f;
- status.x.raw_value = static_cast<float>(x) * coef * (y == 0 ? 1.0f : SQRT_HALF);
- status.y.raw_value = static_cast<float>(y) * coef * (x == 0 ? 1.0f : SQRT_HALF);
+
+ status.x.raw_value = std::cos(goal_angle) * amplitude;
+ status.y.raw_value = std::sin(goal_angle) * amplitude;
return status;
}
@@ -308,6 +297,7 @@ private:
Button left;
Button right;
Button modifier;
+ Button updater;
float modifier_scale{};
float modifier_angle{};
float angle{};
@@ -331,11 +321,12 @@ std::unique_ptr<Common::Input::InputDevice> StickFromButton::Create(
auto left = Common::Input::CreateInputDeviceFromString(params.Get("left", null_engine));
auto right = Common::Input::CreateInputDeviceFromString(params.Get("right", null_engine));
auto modifier = Common::Input::CreateInputDeviceFromString(params.Get("modifier", null_engine));
+ auto updater = Common::Input::CreateInputDeviceFromString("engine:updater,button:0");
auto modifier_scale = params.Get("modifier_scale", 0.5f);
auto modifier_angle = params.Get("modifier_angle", 5.5f);
return std::make_unique<Stick>(std::move(up), std::move(down), std::move(left),
- std::move(right), std::move(modifier), modifier_scale,
- modifier_angle);
+ std::move(right), std::move(modifier), std::move(updater),
+ modifier_scale, modifier_angle);
}
} // namespace InputCommon
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
TriggerOnBatteryChange(identifier, value);
}
+void InputEngine::SetColor(const PadIdentifier& identifier, Common::Input::BodyColorStatus value) {
+ {
+ std::scoped_lock lock{mutex};
+ ControllerData& controller = controller_list.at(identifier);
+ if (!configuring) {
+ controller.color = value;
+ }
+ }
+ TriggerOnColorChange(identifier, value);
+}
+
void InputEngine::SetMotion(const PadIdentifier& identifier, int motion, const BasicMotion& value) {
{
std::scoped_lock lock{mutex};
@@ -176,6 +187,18 @@ Common::Input::BatteryLevel InputEngine::GetBattery(const PadIdentifier& identif
return controller.battery;
}
+Common::Input::BodyColorStatus InputEngine::GetColor(const PadIdentifier& identifier) const {
+ std::scoped_lock lock{mutex};
+ const auto controller_iter = controller_list.find(identifier);
+ if (controller_iter == controller_list.cend()) {
+ LOG_ERROR(Input, "Invalid identifier guid={}, pad={}, port={}", identifier.guid.RawString(),
+ identifier.pad, identifier.port);
+ return {};
+ }
+ const ControllerData& controller = controller_iter->second;
+ return controller.color;
+}
+
BasicMotion InputEngine::GetMotion(const PadIdentifier& identifier, int motion) const {
std::scoped_lock lock{mutex};
const auto controller_iter = controller_list.find(identifier);
@@ -328,6 +351,20 @@ void InputEngine::TriggerOnBatteryChange(const PadIdentifier& identifier,
}
}
+void InputEngine::TriggerOnColorChange(const PadIdentifier& identifier,
+ [[maybe_unused]] Common::Input::BodyColorStatus value) {
+ std::scoped_lock lock{mutex_callback};
+ for (const auto& poller_pair : callback_list) {
+ const InputIdentifier& poller = poller_pair.second;
+ if (!IsInputIdentifierEqual(poller, identifier, EngineInputType::Color, 0)) {
+ continue;
+ }
+ if (poller.callback.on_change) {
+ poller.callback.on_change();
+ }
+ }
+}
+
void InputEngine::TriggerOnMotionChange(const PadIdentifier& identifier, int motion,
const BasicMotion& value) {
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 {
Battery,
Button,
Camera,
+ Color,
HatButton,
Motion,
Nfc,
@@ -104,14 +105,17 @@ public:
void EndConfiguration();
// Sets a led pattern for a controller
- virtual void SetLeds([[maybe_unused]] const PadIdentifier& identifier,
- [[maybe_unused]] const Common::Input::LedStatus& led_status) {}
+ virtual Common::Input::DriverResult SetLeds(
+ [[maybe_unused]] const PadIdentifier& identifier,
+ [[maybe_unused]] const Common::Input::LedStatus& led_status) {
+ return Common::Input::DriverResult::NotSupported;
+ }
// Sets rumble to a controller
- virtual Common::Input::VibrationError SetVibration(
+ virtual Common::Input::DriverResult SetVibration(
[[maybe_unused]] const PadIdentifier& identifier,
[[maybe_unused]] const Common::Input::VibrationStatus& vibration) {
- return Common::Input::VibrationError::NotSupported;
+ return Common::Input::DriverResult::NotSupported;
}
// Returns true if device supports vibrations
@@ -120,17 +124,17 @@ public:
}
// Sets polling mode to a controller
- virtual Common::Input::PollingError SetPollingMode(
+ virtual Common::Input::DriverResult SetPollingMode(
[[maybe_unused]] const PadIdentifier& identifier,
[[maybe_unused]] const Common::Input::PollingMode polling_mode) {
- return Common::Input::PollingError::NotSupported;
+ return Common::Input::DriverResult::NotSupported;
}
// Sets camera format to a controller
- virtual Common::Input::CameraError SetCameraFormat(
+ virtual Common::Input::DriverResult SetCameraFormat(
[[maybe_unused]] const PadIdentifier& identifier,
[[maybe_unused]] Common::Input::CameraFormat camera_format) {
- return Common::Input::CameraError::NotSupported;
+ return Common::Input::DriverResult::NotSupported;
}
// Returns success if nfc is supported
@@ -199,6 +203,7 @@ public:
bool GetHatButton(const PadIdentifier& identifier, int button, u8 direction) const;
f32 GetAxis(const PadIdentifier& identifier, int axis) const;
Common::Input::BatteryLevel GetBattery(const PadIdentifier& identifier) const;
+ Common::Input::BodyColorStatus GetColor(const PadIdentifier& identifier) const;
BasicMotion GetMotion(const PadIdentifier& identifier, int motion) const;
Common::Input::CameraStatus GetCamera(const PadIdentifier& identifier) const;
Common::Input::NfcStatus GetNfc(const PadIdentifier& identifier) const;
@@ -212,6 +217,7 @@ protected:
void SetHatButton(const PadIdentifier& identifier, int button, u8 value);
void SetAxis(const PadIdentifier& identifier, int axis, f32 value);
void SetBattery(const PadIdentifier& identifier, Common::Input::BatteryLevel value);
+ void SetColor(const PadIdentifier& identifier, Common::Input::BodyColorStatus value);
void SetMotion(const PadIdentifier& identifier, int motion, const BasicMotion& value);
void SetCamera(const PadIdentifier& identifier, const Common::Input::CameraStatus& value);
void SetNfc(const PadIdentifier& identifier, const Common::Input::NfcStatus& value);
@@ -227,6 +233,7 @@ private:
std::unordered_map<int, float> axes;
std::unordered_map<int, BasicMotion> motions;
Common::Input::BatteryLevel battery{};
+ Common::Input::BodyColorStatus color{};
Common::Input::CameraStatus camera{};
Common::Input::NfcStatus nfc{};
};
@@ -235,6 +242,8 @@ private:
void TriggerOnHatButtonChange(const PadIdentifier& identifier, int button, u8 value);
void TriggerOnAxisChange(const PadIdentifier& identifier, int axis, f32 value);
void TriggerOnBatteryChange(const PadIdentifier& identifier, Common::Input::BatteryLevel value);
+ void TriggerOnColorChange(const PadIdentifier& identifier,
+ Common::Input::BodyColorStatus value);
void TriggerOnMotionChange(const PadIdentifier& identifier, int motion,
const BasicMotion& value);
void TriggerOnCameraChange(const PadIdentifier& identifier,
diff --git a/src/input_common/input_mapping.cpp b/src/input_common/input_mapping.cpp
index edd5287c1..6990a86b9 100644
--- a/src/input_common/input_mapping.cpp
+++ b/src/input_common/input_mapping.cpp
@@ -76,7 +76,7 @@ void MappingFactory::RegisterButton(const MappingData& data) {
break;
case EngineInputType::Analog:
// Ignore mouse axis when mapping buttons
- if (data.engine == "mouse") {
+ if (data.engine == "mouse" && data.index != 4) {
return;
}
new_input.Set("axis", data.index);
@@ -194,6 +194,10 @@ bool MappingFactory::IsDriverValid(const MappingData& data) const {
if (data.engine == "keyboard" && data.pad.port != 0) {
return false;
}
+ // Only port 0 can be mapped on the mouse
+ if (data.engine == "mouse" && data.pad.port != 0) {
+ return false;
+ }
// To prevent mapping with two devices we disable any UDP except motion
if (!Settings::values.enable_udp_controller && data.engine == "cemuhookudp" &&
data.type != EngineInputType::Motion) {
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:
class InputFromButton final : public Common::Input::InputDevice {
public:
- explicit InputFromButton(PadIdentifier identifier_, int button_, bool toggle_, bool inverted_,
- InputEngine* input_engine_)
- : identifier(identifier_), button(button_), toggle(toggle_), inverted(inverted_),
- input_engine(input_engine_) {
+ explicit InputFromButton(PadIdentifier identifier_, int button_, bool turbo_, bool toggle_,
+ bool inverted_, InputEngine* input_engine_)
+ : identifier(identifier_), button(button_), turbo(turbo_), toggle(toggle_),
+ inverted(inverted_), input_engine(input_engine_) {
UpdateCallback engine_callback{[this]() { OnChange(); }};
const InputIdentifier input_identifier{
.identifier = identifier,
@@ -40,6 +40,7 @@ public:
.value = input_engine->GetButton(identifier, button),
.inverted = inverted,
.toggle = toggle,
+ .turbo = turbo,
};
}
@@ -68,6 +69,7 @@ public:
private:
const PadIdentifier identifier;
const int button;
+ const bool turbo;
const bool toggle;
const bool inverted;
int callback_key;
@@ -77,10 +79,10 @@ private:
class InputFromHatButton final : public Common::Input::InputDevice {
public:
- explicit InputFromHatButton(PadIdentifier identifier_, int button_, u8 direction_, bool toggle_,
- bool inverted_, InputEngine* input_engine_)
- : identifier(identifier_), button(button_), direction(direction_), toggle(toggle_),
- inverted(inverted_), input_engine(input_engine_) {
+ explicit InputFromHatButton(PadIdentifier identifier_, int button_, u8 direction_, bool turbo_,
+ bool toggle_, bool inverted_, InputEngine* input_engine_)
+ : identifier(identifier_), button(button_), direction(direction_), turbo(turbo_),
+ toggle(toggle_), inverted(inverted_), input_engine(input_engine_) {
UpdateCallback engine_callback{[this]() { OnChange(); }};
const InputIdentifier input_identifier{
.identifier = identifier,
@@ -101,6 +103,7 @@ public:
.value = input_engine->GetHatButton(identifier, button, direction),
.inverted = inverted,
.toggle = toggle,
+ .turbo = turbo,
};
}
@@ -130,6 +133,7 @@ private:
const PadIdentifier identifier;
const int button;
const u8 direction;
+ const bool turbo;
const bool toggle;
const bool inverted;
int callback_key;
@@ -498,6 +502,58 @@ private:
InputEngine* input_engine;
};
+class InputFromColor final : public Common::Input::InputDevice {
+public:
+ explicit InputFromColor(PadIdentifier identifier_, InputEngine* input_engine_)
+ : identifier(identifier_), input_engine(input_engine_) {
+ UpdateCallback engine_callback{[this]() { OnChange(); }};
+ const InputIdentifier input_identifier{
+ .identifier = identifier,
+ .type = EngineInputType::Color,
+ .index = 0,
+ .callback = engine_callback,
+ };
+ last_color_value = {};
+ callback_key = input_engine->SetCallback(input_identifier);
+ }
+
+ ~InputFromColor() override {
+ input_engine->DeleteCallback(callback_key);
+ }
+
+ Common::Input::BodyColorStatus GetStatus() const {
+ return input_engine->GetColor(identifier);
+ }
+
+ void ForceUpdate() override {
+ const Common::Input::CallbackStatus status{
+ .type = Common::Input::InputType::Color,
+ .color_status = GetStatus(),
+ };
+
+ last_color_value = status.color_status;
+ TriggerOnChange(status);
+ }
+
+ void OnChange() {
+ const Common::Input::CallbackStatus status{
+ .type = Common::Input::InputType::Color,
+ .color_status = GetStatus(),
+ };
+
+ if (status.color_status.body != last_color_value.body) {
+ last_color_value = status.color_status;
+ TriggerOnChange(status);
+ }
+ }
+
+private:
+ const PadIdentifier identifier;
+ int callback_key;
+ Common::Input::BodyColorStatus last_color_value;
+ InputEngine* input_engine;
+};
+
class InputFromMotion final : public Common::Input::InputDevice {
public:
explicit InputFromMotion(PadIdentifier identifier_, int motion_sensor_, float gyro_threshold_,
@@ -754,11 +810,11 @@ public:
explicit OutputFromIdentifier(PadIdentifier identifier_, InputEngine* input_engine_)
: identifier(identifier_), input_engine(input_engine_) {}
- void SetLED(const Common::Input::LedStatus& led_status) override {
- input_engine->SetLeds(identifier, led_status);
+ Common::Input::DriverResult SetLED(const Common::Input::LedStatus& led_status) override {
+ return input_engine->SetLeds(identifier, led_status);
}
- Common::Input::VibrationError SetVibration(
+ Common::Input::DriverResult SetVibration(
const Common::Input::VibrationStatus& vibration_status) override {
return input_engine->SetVibration(identifier, vibration_status);
}
@@ -767,11 +823,12 @@ public:
return input_engine->IsVibrationEnabled(identifier);
}
- Common::Input::PollingError SetPollingMode(Common::Input::PollingMode polling_mode) override {
+ Common::Input::DriverResult SetPollingMode(Common::Input::PollingMode polling_mode) override {
return input_engine->SetPollingMode(identifier, polling_mode);
}
- Common::Input::CameraError SetCameraFormat(Common::Input::CameraFormat camera_format) override {
+ Common::Input::DriverResult SetCameraFormat(
+ Common::Input::CameraFormat camera_format) override {
return input_engine->SetCameraFormat(identifier, camera_format);
}
@@ -800,14 +857,15 @@ std::unique_ptr<Common::Input::InputDevice> InputFactory::CreateButtonDevice(
const auto keyboard_key = params.Get("code", 0);
const auto toggle = params.Get("toggle", false) != 0;
const auto inverted = params.Get("inverted", false) != 0;
+ const auto turbo = params.Get("turbo", false) != 0;
input_engine->PreSetController(identifier);
input_engine->PreSetButton(identifier, button_id);
input_engine->PreSetButton(identifier, keyboard_key);
if (keyboard_key != 0) {
- return std::make_unique<InputFromButton>(identifier, keyboard_key, toggle, inverted,
+ return std::make_unique<InputFromButton>(identifier, keyboard_key, turbo, toggle, inverted,
input_engine.get());
}
- return std::make_unique<InputFromButton>(identifier, button_id, toggle, inverted,
+ return std::make_unique<InputFromButton>(identifier, button_id, turbo, toggle, inverted,
input_engine.get());
}
@@ -823,11 +881,12 @@ std::unique_ptr<Common::Input::InputDevice> InputFactory::CreateHatButtonDevice(
const auto direction = input_engine->GetHatButtonId(params.Get("direction", ""));
const auto toggle = params.Get("toggle", false) != 0;
const auto inverted = params.Get("inverted", false) != 0;
+ const auto turbo = params.Get("turbo", false) != 0;
input_engine->PreSetController(identifier);
input_engine->PreSetHatButton(identifier, button_id);
- return std::make_unique<InputFromHatButton>(identifier, button_id, direction, toggle, inverted,
- input_engine.get());
+ return std::make_unique<InputFromHatButton>(identifier, button_id, direction, turbo, toggle,
+ inverted, input_engine.get());
}
std::unique_ptr<Common::Input::InputDevice> InputFactory::CreateStickDevice(
@@ -966,6 +1025,18 @@ std::unique_ptr<Common::Input::InputDevice> InputFactory::CreateBatteryDevice(
return std::make_unique<InputFromBattery>(identifier, input_engine.get());
}
+std::unique_ptr<Common::Input::InputDevice> InputFactory::CreateColorDevice(
+ const Common::ParamPackage& params) {
+ const PadIdentifier identifier = {
+ .guid = Common::UUID{params.Get("guid", "")},
+ .port = static_cast<std::size_t>(params.Get("port", 0)),
+ .pad = static_cast<std::size_t>(params.Get("pad", 0)),
+ };
+
+ input_engine->PreSetController(identifier);
+ return std::make_unique<InputFromColor>(identifier, input_engine.get());
+}
+
std::unique_ptr<Common::Input::InputDevice> InputFactory::CreateMotionDevice(
Common::ParamPackage params) {
const PadIdentifier identifier = {
@@ -1053,6 +1124,9 @@ std::unique_ptr<Common::Input::InputDevice> InputFactory::Create(
if (params.Has("battery")) {
return CreateBatteryDevice(params);
}
+ if (params.Has("color")) {
+ return CreateColorDevice(params);
+ }
if (params.Has("camera")) {
return CreateCameraDevice(params);
}
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:
const Common::ParamPackage& params);
/**
+ * Creates a color device from the parameters given.
+ * @param params contains parameters for creating the device:
+ * - "guid": text string for identifying controllers
+ * - "port": port of the connected device
+ * - "pad": slot of the connected controller
+ * @returns a unique input device with the parameters specified
+ */
+ std::unique_ptr<Common::Input::InputDevice> CreateColorDevice(
+ const Common::ParamPackage& params);
+
+ /**
* Creates a motion device from the parameters given.
* @param params contains parameters for creating the device:
* - "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 4dc92f482..c77fc04ee 100644
--- a/src/input_common/main.cpp
+++ b/src/input_common/main.cpp
@@ -23,11 +23,34 @@
#include "input_common/drivers/gc_adapter.h"
#endif
#ifdef HAVE_SDL2
+#include "input_common/drivers/joycon.h"
#include "input_common/drivers/sdl_driver.h"
#endif
namespace InputCommon {
+/// Dummy engine to get periodic updates
+class UpdateEngine final : public InputEngine {
+public:
+ explicit UpdateEngine(std::string input_engine_) : InputEngine(std::move(input_engine_)) {
+ PreSetController(identifier);
+ }
+
+ void PumpEvents() {
+ SetButton(identifier, 0, last_state);
+ last_state = !last_state;
+ }
+
+private:
+ static constexpr PadIdentifier identifier = {
+ .guid = Common::UUID{},
+ .port = 0,
+ .pad = 0,
+ };
+
+ bool last_state{};
+};
+
struct InputSubsystem::Impl {
template <typename Engine>
void RegisterEngine(std::string name, std::shared_ptr<Engine>& engine) {
@@ -45,6 +68,7 @@ struct InputSubsystem::Impl {
void Initialize() {
mapping_factory = std::make_shared<MappingFactory>();
+ RegisterEngine("updater", update_engine);
RegisterEngine("keyboard", keyboard);
RegisterEngine("mouse", mouse);
RegisterEngine("touch", touch_screen);
@@ -58,6 +82,7 @@ struct InputSubsystem::Impl {
RegisterEngine("virtual_gamepad", virtual_gamepad);
#ifdef HAVE_SDL2
RegisterEngine("sdl", sdl);
+ RegisterEngine("joycon", joycon);
#endif
Common::Input::RegisterInputFactory("touch_from_button",
@@ -74,6 +99,7 @@ struct InputSubsystem::Impl {
}
void Shutdown() {
+ UnregisterEngine(update_engine);
UnregisterEngine(keyboard);
UnregisterEngine(mouse);
UnregisterEngine(touch_screen);
@@ -87,6 +113,7 @@ struct InputSubsystem::Impl {
UnregisterEngine(virtual_gamepad);
#ifdef HAVE_SDL2
UnregisterEngine(sdl);
+ UnregisterEngine(joycon);
#endif
Common::Input::UnregisterInputFactory("touch_from_button");
@@ -109,6 +136,8 @@ struct InputSubsystem::Impl {
auto udp_devices = udp_client->GetInputDevices();
devices.insert(devices.end(), udp_devices.begin(), udp_devices.end());
#ifdef HAVE_SDL2
+ auto joycon_devices = joycon->GetInputDevices();
+ devices.insert(devices.end(), joycon_devices.begin(), joycon_devices.end());
auto sdl_devices = sdl->GetInputDevices();
devices.insert(devices.end(), sdl_devices.begin(), sdl_devices.end());
#endif
@@ -140,6 +169,9 @@ struct InputSubsystem::Impl {
if (engine == sdl->GetEngineName()) {
return sdl;
}
+ if (engine == joycon->GetEngineName()) {
+ return joycon;
+ }
#endif
return nullptr;
}
@@ -223,6 +255,9 @@ struct InputSubsystem::Impl {
if (engine == sdl->GetEngineName()) {
return true;
}
+ if (engine == joycon->GetEngineName()) {
+ return true;
+ }
#endif
return false;
}
@@ -236,6 +271,7 @@ struct InputSubsystem::Impl {
udp_client->BeginConfiguration();
#ifdef HAVE_SDL2
sdl->BeginConfiguration();
+ joycon->BeginConfiguration();
#endif
}
@@ -248,10 +284,12 @@ struct InputSubsystem::Impl {
udp_client->EndConfiguration();
#ifdef HAVE_SDL2
sdl->EndConfiguration();
+ joycon->EndConfiguration();
#endif
}
void PumpEvents() const {
+ update_engine->PumpEvents();
#ifdef HAVE_SDL2
sdl->PumpEvents();
#endif
@@ -263,6 +301,7 @@ struct InputSubsystem::Impl {
std::shared_ptr<MappingFactory> mapping_factory;
+ std::shared_ptr<UpdateEngine> update_engine;
std::shared_ptr<Keyboard> keyboard;
std::shared_ptr<Mouse> mouse;
std::shared_ptr<TouchScreen> touch_screen;
@@ -278,6 +317,7 @@ struct InputSubsystem::Impl {
#ifdef HAVE_SDL2
std::shared_ptr<SDLDriver> sdl;
+ std::shared_ptr<Joycons> joycon;
#endif
};
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,
header += "OPTION NV_internal;"
"OPTION NV_shader_storage_buffer;"
"OPTION NV_gpu_program_fp64;";
+ // TODO: Enable only when MS is used
+ header += "OPTION NV_texture_multisample;";
if (info.uses_int64_bit_atomics) {
header += "OPTION NV_shader_atomic_int64;";
}
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) {
Alias(inst, value);
}
-void EmitBitCastS32F32(EmitContext&, IR::Inst& inst, const IR::Value& value) {
- Alias(inst, value);
-}
-
void EmitBitCastF16U16(EmitContext&, IR::Inst& inst, const IR::Value& value) {
Alias(inst, value);
}
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,
}
}
-std::string_view TextureType(IR::TextureInstInfo info) {
+bool IsTextureMsaa(EmitContext& ctx, const IR::TextureInstInfo& info) {
+ if (info.type == TextureType::Buffer) {
+ return false;
+ }
+ return ctx.info.texture_descriptors.at(info.descriptor_index).is_multisample;
+}
+
+std::string_view TextureType(IR::TextureInstInfo info, bool is_ms = false) {
if (info.is_depth) {
switch (info.type) {
case TextureType::Color1D:
@@ -88,9 +95,9 @@ std::string_view TextureType(IR::TextureInstInfo info) {
return "ARRAY1D";
case TextureType::Color2D:
case TextureType::Color2DRect:
- return "2D";
+ return is_ms ? "2DMS" : "2D";
case TextureType::ColorArray2D:
- return "ARRAY2D";
+ return is_ms ? "ARRAY2DMS" : "ARRAY2D";
case TextureType::Color3D:
return "3D";
case TextureType::ColorCube:
@@ -510,15 +517,16 @@ void EmitImageFetch(EmitContext& ctx, IR::Inst& inst, const IR::Value& index,
const IR::Value& coord, const IR::Value& offset, ScalarS32 lod, ScalarS32 ms) {
const auto info{inst.Flags<IR::TextureInstInfo>()};
const auto sparse_inst{PrepareSparse(inst)};
+ const bool is_multisample{ms.type != Type::Void};
const std::string_view sparse_mod{sparse_inst ? ".SPARSE" : ""};
- const std::string_view type{TextureType(info)};
+ const std::string_view type{TextureType(info, is_multisample)};
const std::string texture{Texture(ctx, info, index)};
const std::string offset_vec{Offset(ctx, offset)};
const auto [coord_vec, coord_alloc]{Coord(ctx, coord)};
const Register ret{ctx.reg_alloc.Define(inst)};
if (info.type == TextureType::Buffer) {
ctx.Add("TXF.F{} {},{},{},{}{};", sparse_mod, ret, coord_vec, texture, type, offset_vec);
- } else if (ms.type != Type::Void) {
+ } else if (is_multisample) {
ctx.Add("MOV.S {}.w,{};"
"TXFMS.F{} {},{},{},{}{};",
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,
}
void EmitImageQueryDimensions(EmitContext& ctx, IR::Inst& inst, const IR::Value& index,
- ScalarS32 lod) {
+ ScalarS32 lod, [[maybe_unused]] const IR::Value& skip_mips) {
const auto info{inst.Flags<IR::TextureInstInfo>()};
const std::string texture{Texture(ctx, info, index)};
- const std::string_view type{TextureType(info)};
+ const bool is_msaa{IsTextureMsaa(ctx, info)};
+ const std::string_view type{TextureType(info, is_msaa)};
ctx.Add("TXQ {},{},{},{};", inst, lod, texture, type);
}
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
void EmitBitCastU16F16(EmitContext& ctx, IR::Inst& inst, const IR::Value& value);
void EmitBitCastU32F32(EmitContext& ctx, IR::Inst& inst, const IR::Value& value);
void EmitBitCastU64F64(EmitContext& ctx, IR::Inst& inst, const IR::Value& value);
-void EmitBitCastS32F32(EmitContext& ctx, IR::Inst& inst, const IR::Value& value);
void EmitBitCastF16U16(EmitContext& ctx, IR::Inst& inst, const IR::Value& value);
void EmitBitCastF32U32(EmitContext& ctx, IR::Inst& inst, const IR::Value& value);
void 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
void EmitImageFetch(EmitContext& ctx, IR::Inst& inst, const IR::Value& index,
const IR::Value& coord, const IR::Value& offset, ScalarS32 lod, ScalarS32 ms);
void EmitImageQueryDimensions(EmitContext& ctx, IR::Inst& inst, const IR::Value& index,
- ScalarS32 lod);
+ ScalarS32 lod, const IR::Value& skip_mips);
void EmitImageQueryLod(EmitContext& ctx, IR::Inst& inst, const IR::Value& index, Register coord);
void EmitImageGradient(EmitContext& ctx, IR::Inst& inst, const IR::Value& index,
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)
ctx.AddU64("{}=doubleBitsToUint64({});", inst, value);
}
-void EmitBitCastS32F32(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
- ctx.AddF32("{}=ftoi({});", inst, value);
-}
-
void EmitBitCastF16U16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst) {
NotImplemented();
}
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
return fmt::format("img{}{}", def.binding, index_offset);
}
+bool IsTextureMsaa(EmitContext& ctx, const IR::TextureInstInfo& info) {
+ if (info.type == TextureType::Buffer) {
+ return false;
+ }
+ return ctx.info.texture_descriptors.at(info.descriptor_index).is_multisample;
+}
+
std::string CastToIntVec(std::string_view value, const IR::TextureInstInfo& info) {
switch (info.type) {
case TextureType::Color1D:
@@ -414,7 +421,7 @@ void EmitImageGatherDref(EmitContext& ctx, IR::Inst& inst, const IR::Value& inde
void EmitImageFetch(EmitContext& ctx, IR::Inst& inst, const IR::Value& index,
std::string_view coords, std::string_view offset, std::string_view lod,
- [[maybe_unused]] std::string_view ms) {
+ std::string_view ms) {
const auto info{inst.Flags<IR::TextureInstInfo>()};
if (info.has_bias) {
throw NotImplementedException("EmitImageFetch Bias texture samples");
@@ -431,19 +438,24 @@ void EmitImageFetch(EmitContext& ctx, IR::Inst& inst, const IR::Value& index,
ctx.AddU1("{}=true;", *sparse_inst);
}
if (!sparse_inst || !supports_sparse) {
- if (!offset.empty()) {
- ctx.Add("{}=texelFetchOffset({},{},int({}),{});", texel, texture,
- CoordsCastToInt(coords, info), lod, CoordsCastToInt(offset, info));
+ const auto int_coords{CoordsCastToInt(coords, info)};
+ if (!ms.empty()) {
+ ctx.Add("{}=texelFetch({},{},int({}));", texel, texture, int_coords, ms);
+ } else if (!offset.empty()) {
+ ctx.Add("{}=texelFetchOffset({},{},int({}),{});", texel, texture, int_coords, lod,
+ CoordsCastToInt(offset, info));
} else {
if (info.type == TextureType::Buffer) {
ctx.Add("{}=texelFetch({},int({}));", texel, texture, coords);
} else {
- ctx.Add("{}=texelFetch({},{},int({}));", texel, texture,
- CoordsCastToInt(coords, info), lod);
+ ctx.Add("{}=texelFetch({},{},int({}));", texel, texture, int_coords, lod);
}
}
return;
}
+ if (!ms.empty()) {
+ throw NotImplementedException("EmitImageFetch Sparse MSAA samples");
+ }
if (!offset.empty()) {
ctx.AddU1("{}=sparseTexelsResidentARB(sparseTexelFetchOffsetARB({},{},int({}),{},{}));",
*sparse_inst, texture, CastToIntVec(coords, info), lod,
@@ -455,29 +467,36 @@ void EmitImageFetch(EmitContext& ctx, IR::Inst& inst, const IR::Value& index,
}
void EmitImageQueryDimensions(EmitContext& ctx, IR::Inst& inst, const IR::Value& index,
- std::string_view lod) {
+ std::string_view lod, const IR::Value& skip_mips_val) {
const auto info{inst.Flags<IR::TextureInstInfo>()};
const auto texture{Texture(ctx, info, index)};
+ const bool is_msaa{IsTextureMsaa(ctx, info)};
+ const bool skip_mips{skip_mips_val.U1()};
+ const auto mips{skip_mips ? "0u" : fmt::format("uint(textureQueryLevels({}))", texture)};
+ if (is_msaa && !skip_mips) {
+ throw NotImplementedException("EmitImageQueryDimensions MSAA QueryLevels");
+ }
+ if (info.type == TextureType::Buffer && !skip_mips) {
+ throw NotImplementedException("EmitImageQueryDimensions TextureType::Buffer QueryLevels");
+ }
+ const bool uses_lod{!is_msaa && info.type != TextureType::Buffer};
+ const auto lod_str{uses_lod ? fmt::format(",int({})", lod) : ""};
switch (info.type) {
case TextureType::Color1D:
- return ctx.AddU32x4(
- "{}=uvec4(uint(textureSize({},int({}))),0u,0u,uint(textureQueryLevels({})));", inst,
- texture, lod, texture);
+ return ctx.AddU32x4("{}=uvec4(uint(textureSize({}{})),0u,0u,{});", inst, texture, lod_str,
+ mips);
case TextureType::ColorArray1D:
case TextureType::Color2D:
case TextureType::ColorCube:
case TextureType::Color2DRect:
- return ctx.AddU32x4(
- "{}=uvec4(uvec2(textureSize({},int({}))),0u,uint(textureQueryLevels({})));", inst,
- texture, lod, texture);
+ return ctx.AddU32x4("{}=uvec4(uvec2(textureSize({}{})),0u,{});", inst, texture, lod_str,
+ mips);
case TextureType::ColorArray2D:
case TextureType::Color3D:
case TextureType::ColorArrayCube:
- return ctx.AddU32x4(
- "{}=uvec4(uvec3(textureSize({},int({}))),uint(textureQueryLevels({})));", inst, texture,
- lod, texture);
+ return ctx.AddU32x4("{}=uvec4(uvec3(textureSize({}{})),{});", inst, texture, lod_str, mips);
case TextureType::Buffer:
- throw NotImplementedException("EmitImageQueryDimensions Texture buffers");
+ return ctx.AddU32x4("{}=uvec4(uint(textureSize({})),0u,0u,{});", inst, texture, mips);
}
throw LogicError("Unspecified image type {}", info.type.Value());
}
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,
void EmitBitCastU16F16(EmitContext& ctx, IR::Inst& inst);
void EmitBitCastU32F32(EmitContext& ctx, IR::Inst& inst, std::string_view value);
void EmitBitCastU64F64(EmitContext& ctx, IR::Inst& inst, std::string_view value);
-void EmitBitCastS32F32(EmitContext& ctx, IR::Inst& inst, std::string_view value);
void EmitBitCastF16U16(EmitContext& ctx, IR::Inst& inst);
void EmitBitCastF32U32(EmitContext& ctx, IR::Inst& inst, std::string_view value);
void 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,
std::string_view coords, std::string_view offset, std::string_view lod,
std::string_view ms);
void EmitImageQueryDimensions(EmitContext& ctx, IR::Inst& inst, const IR::Value& index,
- std::string_view lod);
+ std::string_view lod, const IR::Value& skip_mips);
void EmitImageQueryLod(EmitContext& ctx, IR::Inst& inst, const IR::Value& index,
std::string_view coords);
void 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..c3c2281bb 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) {
}
}
-std::string_view SamplerType(TextureType type, bool is_depth) {
- if (is_depth) {
- switch (type) {
- case TextureType::Color1D:
- return "sampler1DShadow";
- case TextureType::ColorArray1D:
- return "sampler1DArrayShadow";
- case TextureType::Color2D:
- return "sampler2DShadow";
- case TextureType::ColorArray2D:
- return "sampler2DArrayShadow";
- case TextureType::ColorCube:
- return "samplerCubeShadow";
- case TextureType::ColorArrayCube:
- return "samplerCubeArrayShadow";
- default:
- throw NotImplementedException("Texture type: {}", type);
- }
+std::string_view DepthSamplerType(TextureType type) {
+ switch (type) {
+ case TextureType::Color1D:
+ return "sampler1DShadow";
+ case TextureType::ColorArray1D:
+ return "sampler1DArrayShadow";
+ case TextureType::Color2D:
+ return "sampler2DShadow";
+ case TextureType::ColorArray2D:
+ return "sampler2DArrayShadow";
+ case TextureType::ColorCube:
+ return "samplerCubeShadow";
+ case TextureType::ColorArrayCube:
+ return "samplerCubeArrayShadow";
+ default:
+ throw NotImplementedException("Texture type: {}", type);
+ }
+}
+
+std::string_view ColorSamplerType(TextureType type, bool is_multisample = false) {
+ if (is_multisample) {
+ ASSERT(type == TextureType::Color2D || type == TextureType::ColorArray2D);
}
switch (type) {
case TextureType::Color1D:
@@ -87,9 +91,9 @@ std::string_view SamplerType(TextureType type, bool is_depth) {
return "sampler1DArray";
case TextureType::Color2D:
case TextureType::Color2DRect:
- return "sampler2D";
+ return is_multisample ? "sampler2DMS" : "sampler2D";
case TextureType::ColorArray2D:
- return "sampler2DArray";
+ return is_multisample ? "sampler2DMSArray" : "sampler2DArray";
case TextureType::Color3D:
return "sampler3D";
case TextureType::ColorCube:
@@ -306,12 +310,6 @@ EmitContext::EmitContext(IR::Program& program, Bindings& bindings, const Profile
if (runtime_info.force_early_z) {
header += "layout(early_fragment_tests)in;";
}
- if (info.uses_sample_id) {
- header += "in int gl_SampleID;";
- }
- if (info.stores_sample_mask) {
- header += "out int gl_SampleMask[];";
- }
break;
case Stage::Compute:
stage_name = "cs";
@@ -677,7 +675,7 @@ void EmitContext::SetupTextures(Bindings& bindings) {
texture_buffers.reserve(info.texture_buffer_descriptors.size());
for (const auto& desc : info.texture_buffer_descriptors) {
texture_buffers.push_back({bindings.texture, desc.count});
- const auto sampler_type{SamplerType(TextureType::Buffer, false)};
+ const auto sampler_type{ColorSamplerType(TextureType::Buffer)};
const auto array_decorator{desc.count > 1 ? fmt::format("[{}]", desc.count) : ""};
header += fmt::format("layout(binding={}) uniform {} tex{}{};", bindings.texture,
sampler_type, bindings.texture, array_decorator);
@@ -686,7 +684,8 @@ void EmitContext::SetupTextures(Bindings& bindings) {
textures.reserve(info.texture_descriptors.size());
for (const auto& desc : info.texture_descriptors) {
textures.push_back({bindings.texture, desc.count});
- const auto sampler_type{SamplerType(desc.type, desc.is_depth)};
+ const auto sampler_type{desc.is_depth ? DepthSamplerType(desc.type)
+ : ColorSamplerType(desc.type, desc.is_multisample)};
const auto array_decorator{desc.count > 1 ? fmt::format("[{}]", desc.count) : ""};
header += fmt::format("layout(binding={}) uniform {} tex{}{};", bindings.texture,
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&) {
throw NotImplementedException("SPIR-V Instruction");
}
-void EmitBitCastS32F32(EmitContext&) {
- throw NotImplementedException("SPIR-V Instruction");
-}
-
void EmitBitCastF16U16(EmitContext&) {
throw NotImplementedException("SPIR-V Instruction");
}
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) {
}
}
+bool IsTextureMsaa(EmitContext& ctx, const IR::TextureInstInfo& info) {
+ if (info.type == TextureType::Buffer) {
+ return false;
+ }
+ return ctx.textures.at(info.descriptor_index).is_multisample;
+}
+
Id Decorate(EmitContext& ctx, IR::Inst* inst, Id sample) {
const auto info{inst->Flags<IR::TextureInstInfo>()};
if (info.relaxed_precision != 0) {
@@ -436,34 +443,42 @@ Id EmitImageFetch(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id c
if (info.type == TextureType::Buffer) {
lod = Id{};
}
+ if (Sirit::ValidId(ms)) {
+ // This image is multisampled, lod must be implicit
+ lod = Id{};
+ }
const ImageOperands operands(offset, lod, ms);
return Emit(&EmitContext::OpImageSparseFetch, &EmitContext::OpImageFetch, ctx, inst, ctx.F32[4],
TextureImage(ctx, info, index), coords, operands.MaskOptional(), operands.Span());
}
-Id EmitImageQueryDimensions(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id lod) {
+Id EmitImageQueryDimensions(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id lod,
+ const IR::Value& skip_mips_val) {
const auto info{inst->Flags<IR::TextureInstInfo>()};
const Id image{TextureImage(ctx, info, index)};
const Id zero{ctx.u32_zero_value};
- const auto mips{[&] { return ctx.OpImageQueryLevels(ctx.U32[1], image); }};
+ const bool skip_mips{skip_mips_val.U1()};
+ const auto mips{[&] { return skip_mips ? zero : ctx.OpImageQueryLevels(ctx.U32[1], image); }};
+ const bool is_msaa{IsTextureMsaa(ctx, info)};
+ const bool uses_lod{!is_msaa && info.type != TextureType::Buffer};
+ const auto query{[&](Id type) {
+ return uses_lod ? ctx.OpImageQuerySizeLod(type, image, lod)
+ : ctx.OpImageQuerySize(type, image);
+ }};
switch (info.type) {
case TextureType::Color1D:
- return ctx.OpCompositeConstruct(ctx.U32[4], ctx.OpImageQuerySizeLod(ctx.U32[1], image, lod),
- zero, zero, mips());
+ return ctx.OpCompositeConstruct(ctx.U32[4], query(ctx.U32[1]), zero, zero, mips());
case TextureType::ColorArray1D:
case TextureType::Color2D:
case TextureType::ColorCube:
case TextureType::Color2DRect:
- return ctx.OpCompositeConstruct(ctx.U32[4], ctx.OpImageQuerySizeLod(ctx.U32[2], image, lod),
- zero, mips());
+ return ctx.OpCompositeConstruct(ctx.U32[4], query(ctx.U32[2]), zero, mips());
case TextureType::ColorArray2D:
case TextureType::Color3D:
case TextureType::ColorArrayCube:
- return ctx.OpCompositeConstruct(ctx.U32[4], ctx.OpImageQuerySizeLod(ctx.U32[3], image, lod),
- mips());
+ return ctx.OpCompositeConstruct(ctx.U32[4], query(ctx.U32[3]), mips());
case TextureType::Buffer:
- return ctx.OpCompositeConstruct(ctx.U32[4], ctx.OpImageQuerySize(ctx.U32[1], image), zero,
- zero, mips());
+ return ctx.OpCompositeConstruct(ctx.U32[4], query(ctx.U32[1]), zero, zero, mips());
}
throw LogicError("Unspecified image type {}", info.type.Value());
}
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);
void EmitBitCastU16F16(EmitContext& ctx);
Id EmitBitCastU32F32(EmitContext& ctx, Id value);
void EmitBitCastU64F64(EmitContext& ctx);
-void EmitBitCastS32F32(EmitContext& ctx);
void EmitBitCastF16U16(EmitContext&);
Id EmitBitCastF32U32(EmitContext& ctx, Id value);
void EmitBitCastF64U64(EmitContext& ctx);
@@ -540,7 +539,8 @@ Id EmitImageGatherDref(EmitContext& ctx, IR::Inst* inst, const IR::Value& index,
const IR::Value& offset, const IR::Value& offset2, Id dref);
Id EmitImageFetch(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords, Id offset,
Id lod, Id ms);
-Id EmitImageQueryDimensions(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id lod);
+Id EmitImageQueryDimensions(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id lod,
+ const IR::Value& skip_mips);
Id EmitImageQueryLod(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords);
Id EmitImageGradient(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords,
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) {
const spv::ImageFormat format{spv::ImageFormat::Unknown};
const Id type{ctx.F32[1]};
const bool depth{desc.is_depth};
+ const bool ms{desc.is_multisample};
switch (desc.type) {
case TextureType::Color1D:
return ctx.TypeImage(type, spv::Dim::Dim1D, depth, false, false, 1, format);
@@ -42,9 +43,9 @@ Id ImageType(EmitContext& ctx, const TextureDescriptor& desc) {
return ctx.TypeImage(type, spv::Dim::Dim1D, depth, true, false, 1, format);
case TextureType::Color2D:
case TextureType::Color2DRect:
- return ctx.TypeImage(type, spv::Dim::Dim2D, depth, false, false, 1, format);
+ return ctx.TypeImage(type, spv::Dim::Dim2D, depth, false, ms, 1, format);
case TextureType::ColorArray2D:
- return ctx.TypeImage(type, spv::Dim::Dim2D, depth, true, false, 1, format);
+ return ctx.TypeImage(type, spv::Dim::Dim2D, depth, true, ms, 1, format);
case TextureType::Color3D:
return ctx.TypeImage(type, spv::Dim::Dim3D, depth, false, false, 1, format);
case TextureType::ColorCube:
@@ -1287,6 +1288,7 @@ void EmitContext::DefineTextures(const Info& info, u32& binding, u32& scaling_in
.pointer_type = pointer_type,
.image_type = image_type,
.count = desc.count,
+ .is_multisample = desc.is_multisample,
});
if (profile.supported_spirv >= 0x00010400) {
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 {
Id pointer_type;
Id image_type;
u32 count;
+ bool is_multisample;
};
struct 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) {
}
template <>
-IR::S32 IREmitter::BitCast<IR::S32, IR::F32>(const IR::F32& value) {
- return Inst<IR::S32>(Opcode::BitCastS32F32, value);
-}
-
-template <>
IR::F32 IREmitter::BitCast<IR::F32, IR::U32>(const IR::U32& value) {
return Inst<IR::F32>(Opcode::BitCastF32U32, value);
}
@@ -1851,15 +1846,16 @@ Value IREmitter::ImageFetch(const Value& handle, const Value& coords, const Valu
return Inst(op, Flags{info}, handle, coords, offset, lod, multisampling);
}
-Value IREmitter::ImageQueryDimension(const Value& handle, const IR::U32& lod) {
+Value IREmitter::ImageQueryDimension(const Value& handle, const IR::U32& lod,
+ const IR::U1& skip_mips) {
const Opcode op{handle.IsImmediate() ? Opcode::BoundImageQueryDimensions
: Opcode::BindlessImageQueryDimensions};
- return Inst(op, handle, lod);
+ return Inst(op, handle, lod, skip_mips);
}
Value IREmitter::ImageQueryDimension(const Value& handle, const IR::U32& lod,
- TextureInstInfo info) {
- return Inst(Opcode::ImageQueryDimensions, Flags{info}, handle, lod);
+ const IR::U1& skip_mips, TextureInstInfo info) {
+ return Inst(Opcode::ImageQueryDimensions, Flags{info}, handle, lod, skip_mips);
}
Value 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:
[[nodiscard]] F32 ImageSampleDrefExplicitLod(const Value& handle, const Value& coords,
const F32& dref, const F32& lod,
const Value& offset, TextureInstInfo info);
- [[nodiscard]] Value ImageQueryDimension(const Value& handle, const IR::U32& lod);
[[nodiscard]] Value ImageQueryDimension(const Value& handle, const IR::U32& lod,
- TextureInstInfo info);
+ const IR::U1& skip_mips);
+ [[nodiscard]] Value ImageQueryDimension(const Value& handle, const IR::U32& lod,
+ const IR::U1& skip_mips, TextureInstInfo info);
[[nodiscard]] Value ImageQueryLod(const Value& handle, const Value& coords,
TextureInstInfo info);
@@ -408,7 +409,8 @@ private:
}
template <typename T>
- requires(sizeof(T) <= sizeof(u32) && std::is_trivially_copyable_v<T>) struct Flags {
+ requires(sizeof(T) <= sizeof(u32) && std::is_trivially_copyable_v<T>)
+ struct Flags {
Flags() = default;
Flags(T proxy_) : proxy{proxy_} {}
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};
constexpr Type U16{Type::U16};
constexpr Type U32{Type::U32};
constexpr Type U64{Type::U64};
-constexpr Type S32{Type::S32};
constexpr Type F16{Type::F16};
constexpr Type F32{Type::F32};
constexpr 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,
OPCODE(BitCastU16F16, U16, F16, )
OPCODE(BitCastU32F32, U32, F32, )
OPCODE(BitCastU64F64, U64, F64, )
-OPCODE(BitCastS32F32, S32, F32, )
OPCODE(BitCastF16U16, F16, U16, )
OPCODE(BitCastF32U32, F32, U32, )
OPCODE(BitCastF64U64, F64, U64, )
@@ -483,7 +482,7 @@ OPCODE(BindlessImageSampleDrefExplicitLod, F32, U32,
OPCODE(BindlessImageGather, F32x4, U32, Opaque, Opaque, Opaque, )
OPCODE(BindlessImageGatherDref, F32x4, U32, Opaque, Opaque, Opaque, F32, )
OPCODE(BindlessImageFetch, F32x4, U32, Opaque, Opaque, U32, Opaque, )
-OPCODE(BindlessImageQueryDimensions, U32x4, U32, U32, )
+OPCODE(BindlessImageQueryDimensions, U32x4, U32, U32, U1, )
OPCODE(BindlessImageQueryLod, F32x4, U32, Opaque, )
OPCODE(BindlessImageGradient, F32x4, U32, Opaque, Opaque, Opaque, Opaque, )
OPCODE(BindlessImageRead, U32x4, U32, Opaque, )
@@ -496,7 +495,7 @@ OPCODE(BoundImageSampleDrefExplicitLod, F32, U32,
OPCODE(BoundImageGather, F32x4, U32, Opaque, Opaque, Opaque, )
OPCODE(BoundImageGatherDref, F32x4, U32, Opaque, Opaque, Opaque, F32, )
OPCODE(BoundImageFetch, F32x4, U32, Opaque, Opaque, U32, Opaque, )
-OPCODE(BoundImageQueryDimensions, U32x4, U32, U32, )
+OPCODE(BoundImageQueryDimensions, U32x4, U32, U32, U1, )
OPCODE(BoundImageQueryLod, F32x4, U32, Opaque, )
OPCODE(BoundImageGradient, F32x4, U32, Opaque, Opaque, Opaque, Opaque, )
OPCODE(BoundImageRead, U32x4, U32, Opaque, )
@@ -509,7 +508,7 @@ OPCODE(ImageSampleDrefExplicitLod, F32, Opaq
OPCODE(ImageGather, F32x4, Opaque, Opaque, Opaque, Opaque, )
OPCODE(ImageGatherDref, F32x4, Opaque, Opaque, Opaque, Opaque, F32, )
OPCODE(ImageFetch, F32x4, Opaque, Opaque, Opaque, U32, Opaque, )
-OPCODE(ImageQueryDimensions, U32x4, Opaque, U32, )
+OPCODE(ImageQueryDimensions, U32x4, Opaque, U32, U1, )
OPCODE(ImageQueryLod, F32x4, Opaque, Opaque, )
OPCODE(ImageGradient, F32x4, Opaque, Opaque, Opaque, Opaque, Opaque, )
OPCODE(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 {
U16 = 1 << 7,
U32 = 1 << 8,
U64 = 1 << 9,
- S32 = 1 << 10,
- F16 = 1 << 11,
- F32 = 1 << 12,
- F64 = 1 << 13,
- U32x2 = 1 << 14,
- U32x3 = 1 << 15,
- U32x4 = 1 << 16,
- F16x2 = 1 << 17,
- F16x3 = 1 << 18,
- F16x4 = 1 << 19,
- F32x2 = 1 << 20,
- F32x3 = 1 << 21,
- F32x4 = 1 << 22,
- F64x2 = 1 << 23,
- F64x3 = 1 << 24,
- F64x4 = 1 << 25,
+ F16 = 1 << 10,
+ F32 = 1 << 11,
+ F64 = 1 << 12,
+ U32x2 = 1 << 13,
+ U32x3 = 1 << 14,
+ U32x4 = 1 << 15,
+ F16x2 = 1 << 16,
+ F16x3 = 1 << 17,
+ F16x4 = 1 << 18,
+ F32x2 = 1 << 19,
+ F32x3 = 1 << 20,
+ F32x4 = 1 << 21,
+ F64x2 = 1 << 22,
+ F64x3 = 1 << 23,
+ F64x4 = 1 << 24,
};
DECLARE_ENUM_FLAG_OPERATORS(Type)
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} {}
Value::Value(u32 value) noexcept : type{Type::U32}, imm_u32{value} {}
-Value::Value(s32 value) noexcept : type{Type::S32}, imm_s32{value} {}
-
Value::Value(f32 value) noexcept : type{Type::F32}, imm_f32{value} {}
Value::Value(u64 value) noexcept : type{Type::U64}, imm_u64{value} {}
@@ -71,7 +69,6 @@ bool Value::operator==(const Value& other) const {
return imm_u16 == other.imm_u16;
case Type::U32:
case Type::F32:
- case Type::S32:
return imm_u32 == other.imm_u32;
case Type::U64:
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:
explicit Value(u8 value) noexcept;
explicit Value(u16 value) noexcept;
explicit Value(u32 value) noexcept;
- explicit Value(s32 value) noexcept;
explicit Value(f32 value) noexcept;
explicit Value(u64 value) noexcept;
explicit Value(f64 value) noexcept;
@@ -66,7 +65,6 @@ public:
[[nodiscard]] u8 U8() const;
[[nodiscard]] u16 U16() const;
[[nodiscard]] u32 U32() const;
- [[nodiscard]] s32 S32() const;
[[nodiscard]] f32 F32() const;
[[nodiscard]] u64 U64() const;
[[nodiscard]] f64 F64() const;
@@ -86,7 +84,6 @@ private:
u8 imm_u8;
u16 imm_u16;
u32 imm_u32;
- s32 imm_s32;
f32 imm_f32;
u64 imm_u64;
f64 imm_f64;
@@ -101,9 +98,8 @@ public:
TypedValue() = default;
template <IR::Type other_type>
- requires((other_type & type_) != IR::Type::Void) explicit(false)
- TypedValue(const TypedValue<other_type>& value)
- : Value(value) {}
+ requires((other_type & type_) != IR::Type::Void)
+ explicit(false) TypedValue(const TypedValue<other_type>& value) : Value(value) {}
explicit TypedValue(const Value& value) : Value(value) {
if ((value.Type() & type_) == IR::Type::Void) {
@@ -194,16 +190,16 @@ public:
void ReplaceOpcode(IR::Opcode opcode);
template <typename FlagsType>
- requires(sizeof(FlagsType) <= sizeof(u32) && std::is_trivially_copyable_v<FlagsType>)
- [[nodiscard]] FlagsType Flags() const noexcept {
+ requires(sizeof(FlagsType) <= sizeof(u32) && std::is_trivially_copyable_v<FlagsType>)
+ [[nodiscard]] FlagsType Flags() const noexcept {
FlagsType ret;
std::memcpy(reinterpret_cast<char*>(&ret), &flags, sizeof(ret));
return ret;
}
template <typename FlagsType>
- requires(sizeof(FlagsType) <= sizeof(u32) &&
- std::is_trivially_copyable_v<FlagsType>) void SetFlags(FlagsType value) noexcept {
+ requires(sizeof(FlagsType) <= sizeof(u32) && std::is_trivially_copyable_v<FlagsType>)
+ void SetFlags(FlagsType value) noexcept {
std::memcpy(&flags, &value, sizeof(value));
}
@@ -268,7 +264,6 @@ using U8 = TypedValue<Type::U8>;
using U16 = TypedValue<Type::U16>;
using U32 = TypedValue<Type::U32>;
using U64 = TypedValue<Type::U64>;
-using S32 = TypedValue<Type::S32>;
using F16 = TypedValue<Type::F16>;
using F32 = TypedValue<Type::F32>;
using F64 = TypedValue<Type::F64>;
@@ -380,14 +375,6 @@ inline u32 Value::U32() const {
return imm_u32;
}
-inline s32 Value::S32() const {
- if (IsIdentity()) {
- return inst->Arg(0).S32();
- }
- DEBUG_ASSERT(type == Type::S32);
- return imm_s32;
-}
-
inline f32 Value::F32() const {
if (IsIdentity()) {
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 {
SamplePos = 5,
};
-IR::Value Query(TranslatorVisitor& v, const IR::U32& handle, Mode mode, IR::Reg src_reg) {
+IR::Value Query(TranslatorVisitor& v, const IR::U32& handle, Mode mode, IR::Reg src_reg, u64 mask) {
switch (mode) {
case Mode::Dimension: {
+ const bool needs_num_mips{((mask >> 3) & 1) != 0};
+ const IR::U1 skip_mips{v.ir.Imm1(!needs_num_mips)};
const IR::U32 lod{v.X(src_reg)};
- return v.ir.ImageQueryDimension(handle, lod);
+ return v.ir.ImageQueryDimension(handle, lod, skip_mips);
}
case Mode::TextureType:
case Mode::SamplePos:
@@ -46,7 +48,7 @@ void Impl(TranslatorVisitor& v, u64 insn, std::optional<u32> cbuf_offset) {
handle = v.X(src_reg);
++src_reg;
}
- const IR::Value query{Query(v, handle, txq.mode, src_reg)};
+ const IR::Value query{Query(v, handle, txq.mode, src_reg, txq.mask)};
IR::Reg dest_reg{txq.dest_reg};
for (int element = 0; element < 4; ++element) {
if (((txq.mask >> element) & 1) == 0) {
diff --git a/src/shader_recompiler/frontend/maxwell/translate_program.cpp b/src/shader_recompiler/frontend/maxwell/translate_program.cpp
index 9dd3365a8..17a6d4888 100644
--- a/src/shader_recompiler/frontend/maxwell/translate_program.cpp
+++ b/src/shader_recompiler/frontend/maxwell/translate_program.cpp
@@ -259,7 +259,7 @@ IR::Program TranslateProgram(ObjectPool<IR::Inst>& inst_pool, ObjectPool<IR::Blo
program.is_geometry_passthrough = sph.common0.geometry_passthrough != 0;
if (program.is_geometry_passthrough) {
const auto& mask{env.GpPassthroughMask()};
- for (size_t i = 0; i < program.info.passthrough.mask.size(); ++i) {
+ for (size_t i = 0; i < mask.size() * 32; ++i) {
program.info.passthrough.mask[i] = ((mask[i / 32] >> (i % 32)) & 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) {
};
}
-TextureType ReadTextureType(Environment& env, const ConstBufferAddr& cbuf) {
+u32 GetTextureHandle(Environment& env, const ConstBufferAddr& cbuf) {
const u32 secondary_index{cbuf.has_secondary ? cbuf.secondary_index : cbuf.index};
const u32 secondary_offset{cbuf.has_secondary ? cbuf.secondary_offset : cbuf.offset};
const u32 lhs_raw{env.ReadCbufValue(cbuf.index, cbuf.offset) << cbuf.shift_left};
const u32 rhs_raw{env.ReadCbufValue(secondary_index, secondary_offset)
<< cbuf.secondary_shift_left};
- return env.ReadTextureType(lhs_raw | rhs_raw);
+ return lhs_raw | rhs_raw;
+}
+
+TextureType ReadTextureType(Environment& env, const ConstBufferAddr& cbuf) {
+ return env.ReadTextureType(GetTextureHandle(env, cbuf));
}
TexturePixelFormat ReadTexturePixelFormat(Environment& env, const ConstBufferAddr& cbuf) {
- const u32 secondary_index{cbuf.has_secondary ? cbuf.secondary_index : cbuf.index};
- const u32 secondary_offset{cbuf.has_secondary ? cbuf.secondary_offset : cbuf.offset};
- const u32 lhs_raw{env.ReadCbufValue(cbuf.index, cbuf.offset)};
- const u32 rhs_raw{env.ReadCbufValue(secondary_index, secondary_offset)};
- return env.ReadTexturePixelFormat(lhs_raw | rhs_raw);
+ return env.ReadTexturePixelFormat(GetTextureHandle(env, cbuf));
}
class Descriptors {
@@ -386,8 +386,10 @@ public:
return Add(texture_buffer_descriptors, desc, [&desc](const auto& existing) {
return desc.cbuf_index == existing.cbuf_index &&
desc.cbuf_offset == existing.cbuf_offset &&
+ desc.shift_left == existing.shift_left &&
desc.secondary_cbuf_index == existing.secondary_cbuf_index &&
desc.secondary_cbuf_offset == existing.secondary_cbuf_offset &&
+ desc.secondary_shift_left == existing.secondary_shift_left &&
desc.count == existing.count && desc.size_shift == existing.size_shift &&
desc.has_secondary == existing.has_secondary;
});
@@ -405,15 +407,20 @@ public:
}
u32 Add(const TextureDescriptor& desc) {
- return Add(texture_descriptors, desc, [&desc](const auto& existing) {
+ const u32 index{Add(texture_descriptors, desc, [&desc](const auto& existing) {
return desc.type == existing.type && desc.is_depth == existing.is_depth &&
desc.has_secondary == existing.has_secondary &&
desc.cbuf_index == existing.cbuf_index &&
desc.cbuf_offset == existing.cbuf_offset &&
+ desc.shift_left == existing.shift_left &&
desc.secondary_cbuf_index == existing.secondary_cbuf_index &&
desc.secondary_cbuf_offset == existing.secondary_cbuf_offset &&
+ desc.secondary_shift_left == existing.secondary_shift_left &&
desc.count == existing.count && desc.size_shift == existing.size_shift;
- });
+ })};
+ // TODO: Read this from TIC
+ texture_descriptors[index].is_multisample |= desc.is_multisample;
+ return index;
}
u32 Add(const ImageDescriptor& desc) {
@@ -452,7 +459,8 @@ void PatchImageSampleImplicitLod(IR::Block& block, IR::Inst& inst) {
const IR::Value coord(inst.Arg(1));
const IR::Value handle(ir.Imm32(0));
const IR::U32 lod{ir.Imm32(0)};
- const IR::Value texture_size = ir.ImageQueryDimension(handle, lod, info);
+ const IR::U1 skip_mips{ir.Imm1(true)};
+ const IR::Value texture_size = ir.ImageQueryDimension(handle, lod, skip_mips, info);
inst.SetArg(
1, ir.CompositeConstruct(
ir.FPMul(IR::F32(ir.CompositeExtract(coord, 0)),
@@ -486,10 +494,10 @@ void PatchTexelFetch(IR::Block& block, IR::Inst& inst, TexturePixelFormat pixel_
const IR::F32 w(ir.CompositeExtract(new_inst, 3));
const IR::F16F32F64 max_value(ir.Imm32(get_max_value()));
const IR::Value converted =
- ir.CompositeConstruct(ir.FPMul(ir.ConvertSToF(32, 32, ir.BitCast<IR::S32>(x)), max_value),
- ir.FPMul(ir.ConvertSToF(32, 32, ir.BitCast<IR::S32>(y)), max_value),
- ir.FPMul(ir.ConvertSToF(32, 32, ir.BitCast<IR::S32>(z)), max_value),
- ir.FPMul(ir.ConvertSToF(32, 32, ir.BitCast<IR::S32>(w)), max_value));
+ ir.CompositeConstruct(ir.FPMul(ir.ConvertSToF(32, 32, ir.BitCast<IR::U32>(x)), max_value),
+ ir.FPMul(ir.ConvertSToF(32, 32, ir.BitCast<IR::U32>(y)), max_value),
+ ir.FPMul(ir.ConvertSToF(32, 32, ir.BitCast<IR::U32>(z)), max_value),
+ ir.FPMul(ir.ConvertSToF(32, 32, ir.BitCast<IR::U32>(w)), max_value));
inst.ReplaceUsesWith(converted);
}
} // Anonymous namespace
@@ -524,6 +532,7 @@ void TexturePass(Environment& env, IR::Program& program, const HostTranslateInfo
const auto& cbuf{texture_inst.cbuf};
auto flags{inst->Flags<IR::TextureInstInfo>()};
+ bool is_multisample{false};
switch (inst->GetOpcode()) {
case IR::Opcode::ImageQueryDimensions:
flags.type.Assign(ReadTextureType(env, cbuf));
@@ -538,6 +547,12 @@ void TexturePass(Environment& env, IR::Program& program, const HostTranslateInfo
}
break;
case IR::Opcode::ImageFetch:
+ if (flags.type == TextureType::Color2D || flags.type == TextureType::Color2DRect ||
+ flags.type == TextureType::ColorArray2D) {
+ is_multisample = !inst->Arg(4).IsEmpty();
+ } else {
+ inst->SetArg(4, IR::U32{});
+ }
if (flags.type != TextureType::Color1D) {
break;
}
@@ -613,6 +628,7 @@ void TexturePass(Environment& env, IR::Program& program, const HostTranslateInfo
index = descriptors.Add(TextureDescriptor{
.type = flags.type,
.is_depth = flags.is_depth != 0,
+ .is_multisample = is_multisample,
.has_secondary = cbuf.has_secondary,
.cbuf_index = cbuf.index,
.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 @@
namespace Shader {
template <typename T>
-requires std::is_destructible_v<T>
+ requires std::is_destructible_v<T>
class ObjectPool {
public:
explicit ObjectPool(size_t chunk_size = 8192) : new_chunk_size{chunk_size} {
@@ -18,7 +18,7 @@ public:
}
template <typename... Args>
- requires std::is_constructible_v<T, Args...>
+ requires std::is_constructible_v<T, Args...>
[[nodiscard]] T* Create(Args&&... args) {
return std::construct_at(Memory(), std::forward<Args>(args)...);
}
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
struct TextureDescriptor {
TextureType type;
bool is_depth;
+ bool is_multisample;
bool has_secondary;
u32 cbuf_index;
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
core/core_timing.cpp
core/internal_network/network.cpp
precompiled_headers.h
- tests.cpp
video_core/buffer_base.cpp
input_common/calibration_configuration_job.cpp
)
@@ -22,7 +21,7 @@ add_executable(tests
create_target_directory_groups(tests)
target_link_libraries(tests PRIVATE common core input_common)
-target_link_libraries(tests PRIVATE ${PLATFORM_LIBRARIES} Catch2::Catch2 Threads::Threads)
+target_link_libraries(tests PRIVATE ${PLATFORM_LIBRARIES} Catch2::Catch2WithMain Threads::Threads)
add_test(NAME tests COMMAND tests)
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 @@
#include <array>
#include <cstring>
#include <type_traits>
-#include <catch2/catch.hpp>
+#include <catch2/catch_test_macros.hpp>
#include "common/bit_field.h"
TEST_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 @@
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
-#include <catch2/catch.hpp>
+#include <catch2/catch_test_macros.hpp>
#include "common/cityhash.h"
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 @@
#include <unordered_map>
#include <vector>
-#include <catch2/catch.hpp>
+#include <catch2/catch_test_macros.hpp>
#include "common/common_types.h"
#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 @@
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
-#include <catch2/catch.hpp>
+#include <catch2/catch_test_macros.hpp>
#include "common/host_memory.h"
#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 @@
// SPDX-FileCopyrightText: 2017 Citra Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
-#include <catch2/catch.hpp>
+#include <catch2/catch_test_macros.hpp>
#include <math.h>
#include "common/logging/backend.h"
#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 @@
#include <stdexcept>
-#include <catch2/catch.hpp>
+#include <catch2/catch_test_macros.hpp>
#include "common/range_map.h"
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 @@
#include <numeric>
#include <thread>
#include <vector>
-#include <catch2/catch.hpp>
+#include <catch2/catch_test_macros.hpp>
#include "common/ring_buffer.h"
namespace 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 @@
#include <array>
#include <cstring>
#include <span>
-#include <catch2/catch.hpp>
+#include <catch2/catch_test_macros.hpp>
#include "common/common_types.h"
#include "common/scratch_buffer.h"
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 @@
#include <string>
-#include <catch2/catch.hpp>
+#include <catch2/catch_test_macros.hpp>
#include "common/unique_function.h"
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 @@
// SPDX-FileCopyrightText: 2016 Dolphin Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
-#include <catch2/catch.hpp>
+#include <catch2/catch_test_macros.hpp>
#include <array>
#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 @@
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
-#include <catch2/catch.hpp>
+#include <catch2/catch_test_macros.hpp>
#include "core/internal_network/network.h"
#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 @@
#include <thread>
#include <boost/asio.hpp>
#include <boost/crc.hpp>
-#include <catch2/catch.hpp>
+#include <catch2/catch_test_macros.hpp>
#include "input_common/drivers/udp_client.h"
#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 @@
-// SPDX-FileCopyrightText: 2016 Citra Emulator Project
-// SPDX-License-Identifier: GPL-2.0-or-later
-
-#define CATCH_CONFIG_MAIN
-#include <catch2/catch.hpp>
-
-// Catch provides the main function since we've given it the
-// 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..734dbf4b6 100644
--- a/src/tests/video_core/buffer_base.cpp
+++ b/src/tests/video_core/buffer_base.cpp
@@ -4,7 +4,7 @@
#include <stdexcept>
#include <unordered_map>
-#include <catch2/catch.hpp>
+#include <catch2/catch_test_macros.hpp>
#include "common/alignment.h"
#include "common/common_types.h"
@@ -538,7 +538,7 @@ TEST_CASE("BufferBase: Cached write downloads") {
int num = 0;
buffer.ForEachDownloadRangeAndClear(c, WORD, [&](u64 offset, u64 size) { ++num; });
buffer.ForEachUploadRange(c, WORD, [&](u64 offset, u64 size) { ++num; });
- REQUIRE(num == 1);
+ REQUIRE(num == 0);
REQUIRE(!buffer.IsRegionCpuModified(c + PAGE, PAGE));
REQUIRE(!buffer.IsRegionGpuModified(c + PAGE, PAGE));
buffer.FlushCachedWrites();
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
engines/puller.cpp
engines/puller.h
framebuffer_config.h
+ fsr.cpp
+ fsr.h
host1x/codecs/codec.cpp
host1x/codecs/codec.h
host1x/codecs/h264.cpp
@@ -100,6 +102,8 @@ add_library(video_core STATIC
renderer_null/null_rasterizer.h
renderer_null/renderer_null.cpp
renderer_null/renderer_null.h
+ renderer_opengl/blit_image.cpp
+ renderer_opengl/blit_image.h
renderer_opengl/gl_buffer_cache.cpp
renderer_opengl/gl_buffer_cache.h
renderer_opengl/gl_compute_pipeline.cpp
@@ -108,6 +112,8 @@ add_library(video_core STATIC
renderer_opengl/gl_device.h
renderer_opengl/gl_fence_manager.cpp
renderer_opengl/gl_fence_manager.h
+ renderer_opengl/gl_fsr.cpp
+ renderer_opengl/gl_fsr.h
renderer_opengl/gl_graphics_pipeline.cpp
renderer_opengl/gl_graphics_pipeline.h
renderer_opengl/gl_rasterizer.cpp
diff --git a/src/video_core/buffer_cache/buffer_base.h b/src/video_core/buffer_cache/buffer_base.h
index c47b7d866..92d77eef2 100644
--- a/src/video_core/buffer_cache/buffer_base.h
+++ b/src/video_core/buffer_cache/buffer_base.h
@@ -430,7 +430,7 @@ private:
if (query_begin >= SizeBytes() || size < 0) {
return;
}
- [[maybe_unused]] u64* const untracked_words = Array<Type::Untracked>();
+ u64* const untracked_words = Array<Type::Untracked>();
u64* const state_words = Array<type>();
const u64 query_end = query_begin + std::min(static_cast<u64>(size), SizeBytes());
u64* const words_begin = state_words + query_begin / BYTES_PER_WORD;
@@ -483,7 +483,7 @@ private:
NotifyRasterizer<true>(word_index, current_bits, ~u64{0});
}
// Exclude CPU modified pages when visiting GPU pages
- const u64 word = current_word;
+ const u64 word = current_word & ~(type == Type::GPU ? untracked_words[word_index] : 0);
u64 page = page_begin;
page_begin = 0;
@@ -531,7 +531,7 @@ private:
[[nodiscard]] bool IsRegionModified(u64 offset, u64 size) const noexcept {
static_assert(type != Type::Untracked);
- [[maybe_unused]] const u64* const untracked_words = Array<Type::Untracked>();
+ const u64* const untracked_words = Array<Type::Untracked>();
const u64* const state_words = Array<type>();
const u64 num_query_words = size / BYTES_PER_WORD + 1;
const u64 word_begin = offset / BYTES_PER_WORD;
@@ -539,7 +539,8 @@ private:
const u64 page_limit = Common::DivCeil(offset + size, BYTES_PER_PAGE);
u64 page_index = (offset / BYTES_PER_PAGE) % PAGES_PER_WORD;
for (u64 word_index = word_begin; word_index < word_end; ++word_index, page_index = 0) {
- const u64 word = state_words[word_index];
+ const u64 off_word = type == Type::GPU ? untracked_words[word_index] : 0;
+ const u64 word = state_words[word_index] & ~off_word;
if (word == 0) {
continue;
}
@@ -563,7 +564,7 @@ private:
[[nodiscard]] std::pair<u64, u64> ModifiedRegion(u64 offset, u64 size) const noexcept {
static_assert(type != Type::Untracked);
- [[maybe_unused]] const u64* const untracked_words = Array<Type::Untracked>();
+ const u64* const untracked_words = Array<Type::Untracked>();
const u64* const state_words = Array<type>();
const u64 num_query_words = size / BYTES_PER_WORD + 1;
const u64 word_begin = offset / BYTES_PER_WORD;
@@ -573,7 +574,8 @@ private:
u64 begin = std::numeric_limits<u64>::max();
u64 end = 0;
for (u64 word_index = word_begin; word_index < word_end; ++word_index) {
- const u64 word = state_words[word_index];
+ const u64 off_word = type == Type::GPU ? untracked_words[word_index] : 0;
+ const u64 word = state_words[word_index] & ~off_word;
if (word == 0) {
continue;
}
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) {
LOG_WARNING(HW_GPU, "(STUBBED) called");
break;
}
+ case MAXWELL3D_REG_INDEX(draw_texture.src_y0): {
+ DrawTexture();
+ break;
+ }
default:
break;
}
@@ -179,6 +183,33 @@ void DrawManager::DrawIndexSmall(u32 argument) {
ProcessDraw(true, 1);
}
+void DrawManager::DrawTexture() {
+ const auto& regs{maxwell3d->regs};
+ draw_texture_state.dst_x0 = static_cast<float>(regs.draw_texture.dst_x0) / 4096.f;
+ draw_texture_state.dst_y0 = static_cast<float>(regs.draw_texture.dst_y0) / 4096.f;
+ const auto dst_width = static_cast<float>(regs.draw_texture.dst_width) / 4096.f;
+ const auto dst_height = static_cast<float>(regs.draw_texture.dst_height) / 4096.f;
+ const bool lower_left{regs.window_origin.mode !=
+ Maxwell3D::Regs::WindowOrigin::Mode::UpperLeft};
+ if (lower_left) {
+ draw_texture_state.dst_y0 -= dst_height;
+ }
+ draw_texture_state.dst_x1 = draw_texture_state.dst_x0 + dst_width;
+ draw_texture_state.dst_y1 = draw_texture_state.dst_y0 + dst_height;
+ draw_texture_state.src_x0 = static_cast<float>(regs.draw_texture.src_x0) / 4096.f;
+ draw_texture_state.src_y0 = static_cast<float>(regs.draw_texture.src_y0) / 4096.f;
+ draw_texture_state.src_x1 =
+ (static_cast<float>(regs.draw_texture.dx_du) / 4294967296.f) * dst_width +
+ draw_texture_state.src_x0;
+ draw_texture_state.src_y1 =
+ (static_cast<float>(regs.draw_texture.dy_dv) / 4294967296.f) * dst_height +
+ draw_texture_state.src_y0;
+ draw_texture_state.src_sampler = regs.draw_texture.src_sampler;
+ draw_texture_state.src_texture = regs.draw_texture.src_texture;
+
+ maxwell3d->rasterizer->DrawTexture();
+}
+
void DrawManager::UpdateTopology() {
const auto& regs{maxwell3d->regs};
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:
std::vector<u8> inline_index_draw_indexes;
};
+ struct DrawTextureState {
+ f32 dst_x0;
+ f32 dst_y0;
+ f32 dst_x1;
+ f32 dst_y1;
+ f32 src_x0;
+ f32 src_y0;
+ f32 src_x1;
+ f32 src_y1;
+ u32 src_sampler;
+ u32 src_texture;
+ };
+
struct IndirectParams {
bool is_indexed;
bool include_count;
@@ -64,6 +77,10 @@ public:
return draw_state;
}
+ const DrawTextureState& GetDrawTextureState() const {
+ return draw_texture_state;
+ }
+
IndirectParams& GetIndirectParams() {
return indirect_state;
}
@@ -81,6 +98,8 @@ private:
void DrawIndexSmall(u32 argument);
+ void DrawTexture();
+
void UpdateTopology();
void ProcessDraw(bool draw_indexed, u32 instance_count);
@@ -89,6 +108,7 @@ private:
Maxwell3D* maxwell3d{};
State draw_state{};
+ DrawTextureState draw_texture_state{};
IndirectParams indirect_state{};
};
} // namespace Tegra::Engines
diff --git a/src/video_core/engines/maxwell_3d.cpp b/src/video_core/engines/maxwell_3d.cpp
index 97f547789..614d61db4 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) {
case MAXWELL3D_REG_INDEX(inline_index_4x8.index0):
case MAXWELL3D_REG_INDEX(vertex_array_instance_first):
case MAXWELL3D_REG_INDEX(vertex_array_instance_subsequent):
+ case MAXWELL3D_REG_INDEX(draw_texture.src_y0):
case MAXWELL3D_REG_INDEX(wait_for_idle):
case MAXWELL3D_REG_INDEX(shadow_ram_control):
case MAXWELL3D_REG_INDEX(load_mme.instruction_ptr):
@@ -185,6 +186,7 @@ bool Maxwell3D::IsMethodExecutable(u32 method) {
case MAXWELL3D_REG_INDEX(launch_dma):
case MAXWELL3D_REG_INDEX(inline_data):
case MAXWELL3D_REG_INDEX(fragment_barrier):
+ case MAXWELL3D_REG_INDEX(invalidate_texture_data_cache):
case MAXWELL3D_REG_INDEX(tiled_cache_barrier):
return true;
default:
@@ -257,7 +259,7 @@ u32 Maxwell3D::GetMaxCurrentVertices() {
size_t Maxwell3D::EstimateIndexBufferSize() {
GPUVAddr start_address = regs.index_buffer.StartAddress();
GPUVAddr end_address = regs.index_buffer.EndAddress();
- constexpr std::array<size_t, 4> max_sizes = {
+ static constexpr std::array<size_t, 4> max_sizes = {
std::numeric_limits<u8>::max(), std::numeric_limits<u16>::max(),
std::numeric_limits<u32>::max(), std::numeric_limits<u32>::max()};
const size_t byte_size = regs.index_buffer.FormatSizeInBytes();
@@ -374,6 +376,9 @@ void Maxwell3D::ProcessMethodCall(u32 method, u32 argument, u32 nonshadow_argume
return;
case MAXWELL3D_REG_INDEX(fragment_barrier):
return rasterizer->FragmentBarrier();
+ case MAXWELL3D_REG_INDEX(invalidate_texture_data_cache):
+ rasterizer->InvalidateGPUCache();
+ return rasterizer->WaitForIdle();
case MAXWELL3D_REG_INDEX(tiled_cache_barrier):
return rasterizer->TiledCacheBarrier();
default:
@@ -467,7 +472,7 @@ void Maxwell3D::ProcessMacroBind(u32 data) {
}
void Maxwell3D::ProcessFirmwareCall4() {
- LOG_WARNING(HW_GPU, "(STUBBED) called");
+ LOG_DEBUG(HW_GPU, "(STUBBED) called");
// Firmware call 4 is a blob that changes some registers depending on its parameters.
// These registers don't affect emulation and so are stubbed by setting 0xd00 to 1.
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:
};
static_assert(sizeof(TIRModulationCoeff) == 0x4);
+ struct DrawTexture {
+ s32 dst_x0;
+ s32 dst_y0;
+ s32 dst_width;
+ s32 dst_height;
+ s64 dx_du;
+ s64 dy_dv;
+ u32 src_sampler;
+ u32 src_texture;
+ s32 src_x0;
+ s32 src_y0;
+ };
+ static_assert(sizeof(DrawTexture) == 0x30);
+
struct ReduceColorThreshold {
union {
BitField<0, 8, u32> all_hit_once;
@@ -2751,7 +2765,7 @@ public:
u32 reserved_sw_method2; ///< 0x102C
std::array<TIRModulationCoeff, 5> tir_modulation_coeff; ///< 0x1030
std::array<u32, 15> spare_nop; ///< 0x1044
- INSERT_PADDING_BYTES_NOINIT(0x30);
+ DrawTexture draw_texture; ///< 0x1080
std::array<u32, 7> reserved_sw_method3_to_7; ///< 0x10B0
ReduceColorThreshold reduce_color_thresholds_unorm8; ///< 0x10CC
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 @@
+// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include <cmath>
+#include "video_core/fsr.h"
+
+namespace FSR {
+namespace {
+// Reimplementations of the constant generating functions in ffx_fsr1.h
+// GCC generated a lot of warnings when using the official header.
+u32 AU1_AH1_AF1(f32 f) {
+ static constexpr u32 base[512]{
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0001, 0x0002, 0x0004, 0x0008, 0x0010, 0x0020, 0x0040,
+ 0x0080, 0x0100, 0x0200, 0x0400, 0x0800, 0x0c00, 0x1000, 0x1400, 0x1800, 0x1c00, 0x2000,
+ 0x2400, 0x2800, 0x2c00, 0x3000, 0x3400, 0x3800, 0x3c00, 0x4000, 0x4400, 0x4800, 0x4c00,
+ 0x5000, 0x5400, 0x5800, 0x5c00, 0x6000, 0x6400, 0x6800, 0x6c00, 0x7000, 0x7400, 0x7800,
+ 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff,
+ 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff,
+ 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff,
+ 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff,
+ 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff,
+ 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff,
+ 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff,
+ 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff,
+ 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff,
+ 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff,
+ 0x7bff, 0x7bff, 0x7bff, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000,
+ 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000,
+ 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000,
+ 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000,
+ 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000,
+ 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000,
+ 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000,
+ 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000,
+ 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000,
+ 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8001, 0x8002, 0x8004, 0x8008,
+ 0x8010, 0x8020, 0x8040, 0x8080, 0x8100, 0x8200, 0x8400, 0x8800, 0x8c00, 0x9000, 0x9400,
+ 0x9800, 0x9c00, 0xa000, 0xa400, 0xa800, 0xac00, 0xb000, 0xb400, 0xb800, 0xbc00, 0xc000,
+ 0xc400, 0xc800, 0xcc00, 0xd000, 0xd400, 0xd800, 0xdc00, 0xe000, 0xe400, 0xe800, 0xec00,
+ 0xf000, 0xf400, 0xf800, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff,
+ 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff,
+ 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff,
+ 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff,
+ 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff,
+ 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff,
+ 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff,
+ 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff,
+ 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff,
+ 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff,
+ 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff,
+ };
+ static constexpr s8 shift[512]{
+ 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+ 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+ 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+ 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+ 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+ 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+ 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x17, 0x16,
+ 0x15, 0x14, 0x13, 0x12, 0x11, 0x10, 0x0f, 0x0e, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d,
+ 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d,
+ 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+ 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+ 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+ 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+ 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+ 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+ 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+ 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+ 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+ 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+ 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+ 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+ 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+ 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+ 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x17,
+ 0x16, 0x15, 0x14, 0x13, 0x12, 0x11, 0x10, 0x0f, 0x0e, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d,
+ 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d,
+ 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+ 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+ 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+ 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+ 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+ 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+ 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+ 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+ 0x18, 0x18,
+ };
+ const u32 u = Common::BitCast<u32>(f);
+ const u32 i = u >> 23;
+ return base[i] + ((u & 0x7fffff) >> shift[i]);
+}
+
+u32 AU1_AH2_AF2(f32 a[2]) {
+ return AU1_AH1_AF1(a[0]) + (AU1_AH1_AF1(a[1]) << 16);
+}
+
+void FsrEasuCon(u32 con0[4], u32 con1[4], u32 con2[4], u32 con3[4], f32 inputViewportInPixelsX,
+ f32 inputViewportInPixelsY, f32 inputSizeInPixelsX, f32 inputSizeInPixelsY,
+ f32 outputSizeInPixelsX, f32 outputSizeInPixelsY) {
+ con0[0] = Common::BitCast<u32>(inputViewportInPixelsX / outputSizeInPixelsX);
+ con0[1] = Common::BitCast<u32>(inputViewportInPixelsY / outputSizeInPixelsY);
+ con0[2] = Common::BitCast<u32>(0.5f * inputViewportInPixelsX / outputSizeInPixelsX - 0.5f);
+ con0[3] = Common::BitCast<u32>(0.5f * inputViewportInPixelsY / outputSizeInPixelsY - 0.5f);
+ con1[0] = Common::BitCast<u32>(1.0f / inputSizeInPixelsX);
+ con1[1] = Common::BitCast<u32>(1.0f / inputSizeInPixelsY);
+ con1[2] = Common::BitCast<u32>(1.0f / inputSizeInPixelsX);
+ con1[3] = Common::BitCast<u32>(-1.0f / inputSizeInPixelsY);
+ con2[0] = Common::BitCast<u32>(-1.0f / inputSizeInPixelsX);
+ con2[1] = Common::BitCast<u32>(2.0f / inputSizeInPixelsY);
+ con2[2] = Common::BitCast<u32>(1.0f / inputSizeInPixelsX);
+ con2[3] = Common::BitCast<u32>(2.0f / inputSizeInPixelsY);
+ con3[0] = Common::BitCast<u32>(0.0f / inputSizeInPixelsX);
+ con3[1] = Common::BitCast<u32>(4.0f / inputSizeInPixelsY);
+ con3[2] = con3[3] = 0;
+}
+} // Anonymous namespace
+
+void FsrEasuConOffset(u32 con0[4], u32 con1[4], u32 con2[4], u32 con3[4],
+ f32 inputViewportInPixelsX, f32 inputViewportInPixelsY,
+ f32 inputSizeInPixelsX, f32 inputSizeInPixelsY, f32 outputSizeInPixelsX,
+ f32 outputSizeInPixelsY, f32 inputOffsetInPixelsX, f32 inputOffsetInPixelsY) {
+ FsrEasuCon(con0, con1, con2, con3, inputViewportInPixelsX, inputViewportInPixelsY,
+ inputSizeInPixelsX, inputSizeInPixelsY, outputSizeInPixelsX, outputSizeInPixelsY);
+ con0[2] = Common::BitCast<u32>(0.5f * inputViewportInPixelsX / outputSizeInPixelsX - 0.5f +
+ inputOffsetInPixelsX);
+ con0[3] = Common::BitCast<u32>(0.5f * inputViewportInPixelsY / outputSizeInPixelsY - 0.5f +
+ inputOffsetInPixelsY);
+}
+
+void FsrRcasCon(u32* con, f32 sharpness) {
+ sharpness = std::exp2f(-sharpness);
+ f32 hSharp[2]{sharpness, sharpness};
+ con[0] = Common::BitCast<u32>(sharpness);
+ con[1] = AU1_AH2_AF2(hSharp);
+ con[2] = 0;
+ con[3] = 0;
+}
+} // 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 @@
+// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#pragma once
+
+#include "common/bit_cast.h"
+#include "common/common_types.h"
+
+namespace FSR {
+// Reimplementations of the constant generating functions in ffx_fsr1.h
+// GCC generated a lot of warnings when using the official header.
+void FsrEasuConOffset(u32 con0[4], u32 con1[4], u32 con2[4], u32 con3[4],
+ f32 inputViewportInPixelsX, f32 inputViewportInPixelsY,
+ f32 inputSizeInPixelsX, f32 inputSizeInPixelsY, f32 outputSizeInPixelsX,
+ f32 outputSizeInPixelsY, f32 inputOffsetInPixelsX, f32 inputOffsetInPixelsY);
+
+void FsrRcasCon(u32* con, f32 sharpness);
+
+} // namespace FSR
diff --git a/src/video_core/gpu.cpp b/src/video_core/gpu.cpp
index c6d54be63..7024a19cf 100644
--- a/src/video_core/gpu.cpp
+++ b/src/video_core/gpu.cpp
@@ -99,7 +99,7 @@ struct GPU::Impl {
/// Signal the ending of command list.
void OnCommandListEnd() {
- gpu_thread.OnCommandListEnd();
+ rasterizer->ReleaseFences();
}
/// Request a host GPU memory flush from the CPU.
diff --git a/src/video_core/gpu_thread.cpp b/src/video_core/gpu_thread.cpp
index 164a5252a..9c103c0d4 100644
--- a/src/video_core/gpu_thread.cpp
+++ b/src/video_core/gpu_thread.cpp
@@ -40,8 +40,6 @@ static void RunThread(std::stop_token stop_token, Core::System& system,
scheduler.Push(submit_list->channel, std::move(submit_list->entries));
} else if (const auto* data = std::get_if<SwapBuffersCommand>(&next.data)) {
renderer.SwapBuffers(data->framebuffer ? &*data->framebuffer : nullptr);
- } else if (std::holds_alternative<OnCommandListEndCommand>(next.data)) {
- rasterizer->ReleaseFences();
} else if (std::holds_alternative<GPUTickCommand>(next.data)) {
system.GPU().TickWork();
} else if (const auto* flush = std::get_if<FlushRegionCommand>(&next.data)) {
@@ -110,10 +108,6 @@ void ThreadManager::FlushAndInvalidateRegion(VAddr addr, u64 size) {
rasterizer->OnCPUWrite(addr, size);
}
-void ThreadManager::OnCommandListEnd() {
- PushCommand(OnCommandListEndCommand());
-}
-
u64 ThreadManager::PushCommand(CommandData&& command_data, bool block) {
if (!is_async) {
// In synchronous GPU mode, block the caller until the command has executed
diff --git a/src/video_core/gpu_thread.h b/src/video_core/gpu_thread.h
index c71a419c7..90bcb5958 100644
--- a/src/video_core/gpu_thread.h
+++ b/src/video_core/gpu_thread.h
@@ -77,16 +77,12 @@ struct FlushAndInvalidateRegionCommand final {
u64 size;
};
-/// Command called within the gpu, to schedule actions after a command list end
-struct OnCommandListEndCommand final {};
-
/// Command to make the gpu look into pending requests
struct GPUTickCommand final {};
using CommandData =
std::variant<std::monostate, SubmitListCommand, SwapBuffersCommand, FlushRegionCommand,
- InvalidateRegionCommand, FlushAndInvalidateRegionCommand, OnCommandListEndCommand,
- GPUTickCommand>;
+ InvalidateRegionCommand, FlushAndInvalidateRegionCommand, GPUTickCommand>;
struct CommandDataContainer {
CommandDataContainer() = default;
@@ -134,8 +130,6 @@ public:
/// Notify rasterizer that any caches of the specified region should be flushed and invalidated
void FlushAndInvalidateRegion(VAddr addr, u64 size);
- void OnCommandListEnd();
-
void TickGPU();
private:
diff --git a/src/video_core/host1x/codecs/codec.cpp b/src/video_core/host1x/codecs/codec.cpp
index 42e7d6e4f..3e9022dce 100644
--- a/src/video_core/host1x/codecs/codec.cpp
+++ b/src/video_core/host1x/codecs/codec.cpp
@@ -152,6 +152,8 @@ bool Codec::CreateGpuAvDevice() {
void Codec::InitializeAvCodecContext() {
av_codec_ctx = avcodec_alloc_context3(av_codec);
av_opt_set(av_codec_ctx->priv_data, "tune", "zerolatency", 0);
+ av_codec_ctx->thread_count = 0;
+ av_codec_ctx->thread_type &= ~FF_THREAD_FRAME;
}
void Codec::InitializeGpuDecoder() {
diff --git a/src/video_core/host1x/vic.cpp b/src/video_core/host1x/vic.cpp
index 36a04e4e0..10d7ef884 100644
--- a/src/video_core/host1x/vic.cpp
+++ b/src/video_core/host1x/vic.cpp
@@ -189,9 +189,7 @@ void Vic::WriteYUVFrame(const AVFrame* frame, const VicConfig& config) {
for (std::size_t y = 0; y < frame_height; ++y) {
const std::size_t src = y * stride;
const std::size_t dst = y * aligned_width;
- for (std::size_t x = 0; x < frame_width; ++x) {
- luma_buffer[dst + x] = luma_src[src + x];
- }
+ std::memcpy(luma_buffer.data() + dst, luma_src + src, frame_width);
}
host1x.MemoryManager().WriteBlock(output_surface_luma_address, luma_buffer.data(),
luma_buffer.size());
@@ -205,15 +203,15 @@ void Vic::WriteYUVFrame(const AVFrame* frame, const VicConfig& config) {
// Frame from FFmpeg software
// Populate chroma buffer from both channels with interleaving.
const std::size_t half_width = frame_width / 2;
+ u8* chroma_buffer_data = chroma_buffer.data();
const u8* chroma_b_src = frame->data[1];
const u8* chroma_r_src = frame->data[2];
for (std::size_t y = 0; y < half_height; ++y) {
const std::size_t src = y * half_stride;
const std::size_t dst = y * aligned_width;
-
for (std::size_t x = 0; x < half_width; ++x) {
- chroma_buffer[dst + x * 2] = chroma_b_src[src + x];
- chroma_buffer[dst + x * 2 + 1] = chroma_r_src[src + x];
+ chroma_buffer_data[dst + x * 2] = chroma_b_src[src + x];
+ chroma_buffer_data[dst + x * 2 + 1] = chroma_r_src[src + x];
}
}
break;
@@ -225,9 +223,7 @@ void Vic::WriteYUVFrame(const AVFrame* frame, const VicConfig& config) {
for (std::size_t y = 0; y < half_height; ++y) {
const std::size_t src = y * stride;
const std::size_t dst = y * aligned_width;
- for (std::size_t x = 0; x < frame_width; ++x) {
- chroma_buffer[dst + x] = chroma_src[src + x];
- }
+ std::memcpy(chroma_buffer.data() + dst, chroma_src + src, frame_width);
}
break;
}
diff --git a/src/video_core/host_shaders/CMakeLists.txt b/src/video_core/host_shaders/CMakeLists.txt
index f275b2aa9..2442c3c29 100644
--- a/src/video_core/host_shaders/CMakeLists.txt
+++ b/src/video_core/host_shaders/CMakeLists.txt
@@ -3,26 +3,36 @@
set(FIDELITYFX_INCLUDE_DIR ${CMAKE_SOURCE_DIR}/externals/FidelityFX-FSR/ffx-fsr)
-set(GLSL_INCLUDES
- fidelityfx_fsr.comp
+set(FIDELITYFX_FILES
${FIDELITYFX_INCLUDE_DIR}/ffx_a.h
${FIDELITYFX_INCLUDE_DIR}/ffx_fsr1.h
)
+set(GLSL_INCLUDES
+ fidelityfx_fsr.comp
+ ${FIDELITYFX_FILES}
+)
+
set(SHADER_FILES
astc_decoder.comp
+ blit_color_float.frag
block_linear_unswizzle_2d.comp
block_linear_unswizzle_3d.comp
convert_abgr8_to_d24s8.frag
convert_d24s8_to_abgr8.frag
convert_depth_to_float.frag
convert_float_to_depth.frag
+ convert_msaa_to_non_msaa.comp
+ convert_non_msaa_to_msaa.comp
convert_s8d24_to_abgr8.frag
full_screen_triangle.vert
fxaa.frag
fxaa.vert
opengl_convert_s8d24.comp
opengl_copy_bc4.comp
+ opengl_fidelityfx_fsr.frag
+ opengl_fidelityfx_fsr_easu.frag
+ opengl_fidelityfx_fsr_rcas.frag
opengl_present.frag
opengl_present.vert
opengl_present_scaleforce.frag
@@ -36,8 +46,9 @@ set(SHADER_FILES
smaa_blending_weight_calculation.frag
smaa_neighborhood_blending.vert
smaa_neighborhood_blending.frag
- vulkan_blit_color_float.frag
vulkan_blit_depth_stencil.frag
+ vulkan_color_clear.frag
+ vulkan_color_clear.vert
vulkan_fidelityfx_fsr_easu_fp16.comp
vulkan_fidelityfx_fsr_easu_fp32.comp
vulkan_fidelityfx_fsr_rcas_fp16.comp
@@ -118,6 +129,25 @@ foreach(FILENAME IN ITEMS ${SHADER_FILES})
endif()
endforeach()
+foreach(FILEPATH IN ITEMS ${FIDELITYFX_FILES})
+ get_filename_component(FILENAME ${FILEPATH} NAME)
+ string(REPLACE "." "_" HEADER_NAME ${FILENAME})
+ set(SOURCE_FILE ${FILEPATH})
+ set(SOURCE_HEADER_FILE ${SHADER_DIR}/${HEADER_NAME}.h)
+ add_custom_command(
+ OUTPUT
+ ${SOURCE_HEADER_FILE}
+ COMMAND
+ ${CMAKE_COMMAND} -P ${HEADER_GENERATOR} ${SOURCE_FILE} ${SOURCE_HEADER_FILE} ${INPUT_FILE}
+ MAIN_DEPENDENCY
+ ${SOURCE_FILE}
+ DEPENDS
+ ${INPUT_FILE}
+ # HEADER_GENERATOR should be included here but msbuild seems to assume it's always modified
+ )
+ set(SHADER_HEADERS ${SHADER_HEADERS} ${SOURCE_HEADER_FILE})
+endforeach()
+
set(SHADER_SOURCES ${SHADER_FILES})
list(APPEND SHADER_SOURCES ${GLSL_INCLUDES})
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/convert_msaa_to_non_msaa.comp b/src/video_core/host_shaders/convert_msaa_to_non_msaa.comp
new file mode 100644
index 000000000..fc3854d18
--- /dev/null
+++ b/src/video_core/host_shaders/convert_msaa_to_non_msaa.comp
@@ -0,0 +1,30 @@
+// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#version 450 core
+layout (local_size_x = 8, local_size_y = 8, local_size_z = 1) in;
+
+layout (binding = 0, rgba8) uniform readonly restrict image2DMSArray msaa_in;
+layout (binding = 1, rgba8) uniform writeonly restrict image2DArray output_img;
+
+void main() {
+ const ivec3 coords = ivec3(gl_GlobalInvocationID);
+ if (any(greaterThanEqual(coords, imageSize(msaa_in)))) {
+ return;
+ }
+
+ // TODO: Specialization constants for num_samples?
+ const int num_samples = imageSamples(msaa_in);
+ for (int curr_sample = 0; curr_sample < num_samples; ++curr_sample) {
+ const vec4 pixel = imageLoad(msaa_in, coords, curr_sample);
+
+ const int single_sample_x = 2 * coords.x + (curr_sample & 1);
+ const int single_sample_y = 2 * coords.y + ((curr_sample / 2) & 1);
+ const ivec3 dest_coords = ivec3(single_sample_x, single_sample_y, coords.z);
+
+ if (any(greaterThanEqual(dest_coords, imageSize(output_img)))) {
+ continue;
+ }
+ imageStore(output_img, dest_coords, pixel);
+ }
+}
diff --git a/src/video_core/host_shaders/convert_non_msaa_to_msaa.comp b/src/video_core/host_shaders/convert_non_msaa_to_msaa.comp
new file mode 100644
index 000000000..dedd962f1
--- /dev/null
+++ b/src/video_core/host_shaders/convert_non_msaa_to_msaa.comp
@@ -0,0 +1,29 @@
+// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#version 450 core
+layout (local_size_x = 8, local_size_y = 8, local_size_z = 1) in;
+
+layout (binding = 0, rgba8) uniform readonly restrict image2DArray img_in;
+layout (binding = 1, rgba8) uniform writeonly restrict image2DMSArray output_msaa;
+
+void main() {
+ const ivec3 coords = ivec3(gl_GlobalInvocationID);
+ if (any(greaterThanEqual(coords, imageSize(output_msaa)))) {
+ return;
+ }
+
+ // TODO: Specialization constants for num_samples?
+ const int num_samples = imageSamples(output_msaa);
+ for (int curr_sample = 0; curr_sample < num_samples; ++curr_sample) {
+ const int single_sample_x = 2 * coords.x + (curr_sample & 1);
+ const int single_sample_y = 2 * coords.y + ((curr_sample / 2) & 1);
+ const ivec3 single_coords = ivec3(single_sample_x, single_sample_y, coords.z);
+
+ if (any(greaterThanEqual(single_coords, imageSize(img_in)))) {
+ continue;
+ }
+ const vec4 pixel = imageLoad(img_in, single_coords);
+ imageStore(output_msaa, coords, curr_sample, pixel);
+ }
+}
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 @@
#version 450
#ifdef VULKAN
+#define VERTEX_ID gl_VertexIndex
#define BEGIN_PUSH_CONSTANTS layout(push_constant) uniform PushConstants {
#define END_PUSH_CONSTANTS };
#define UNIFORM(n)
+#define FLIPY 1
#else // ^^^ Vulkan ^^^ // vvv OpenGL vvv
+#define VERTEX_ID gl_VertexID
#define BEGIN_PUSH_CONSTANTS
#define END_PUSH_CONSTANTS
+#define FLIPY -1
#define UNIFORM(n) layout (location = n) uniform
+out gl_PerVertex {
+ vec4 gl_Position;
+};
#endif
BEGIN_PUSH_CONSTANTS
@@ -21,8 +28,8 @@ END_PUSH_CONSTANTS
layout(location = 0) out vec2 texcoord;
void main() {
- float x = float((gl_VertexIndex & 1) << 2);
- float y = float((gl_VertexIndex & 2) << 1);
- gl_Position = vec4(x - 1.0, y - 1.0, 0.0, 1.0);
+ float x = float((VERTEX_ID & 1) << 2);
+ float y = float((VERTEX_ID & 2) << 1);
+ gl_Position = vec4(x - 1.0, FLIPY * (y - 1.0), 0.0, 1.0);
texcoord = fma(vec2(x, y) / 2.0, tex_scale, tex_offset);
}
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 @@
+// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+//!#version 460 core
+#extension GL_ARB_separate_shader_objects : enable
+#extension GL_ARB_shading_language_420pack : enable
+
+#extension GL_AMD_gpu_shader_half_float : enable
+#extension GL_NV_gpu_shader5 : enable
+
+// FidelityFX Super Resolution Sample
+//
+// Copyright (c) 2021 Advanced Micro Devices, Inc. All rights reserved.
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files(the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions :
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+layout (location = 0) uniform uvec4 constants[4];
+
+#define A_GPU 1
+#define A_GLSL 1
+
+#ifdef YUZU_USE_FP16
+ #define A_HALF
+#endif
+#include "ffx_a.h"
+
+#ifndef YUZU_USE_FP16
+ layout (binding=0) uniform sampler2D InputTexture;
+ #if USE_EASU
+ #define FSR_EASU_F 1
+ AF4 FsrEasuRF(AF2 p) { AF4 res = textureGather(InputTexture, p, 0); return res; }
+ AF4 FsrEasuGF(AF2 p) { AF4 res = textureGather(InputTexture, p, 1); return res; }
+ AF4 FsrEasuBF(AF2 p) { AF4 res = textureGather(InputTexture, p, 2); return res; }
+ #endif
+ #if USE_RCAS
+ #define FSR_RCAS_F
+ AF4 FsrRcasLoadF(ASU2 p) { return texelFetch(InputTexture, ASU2(p), 0); }
+ void FsrRcasInputF(inout AF1 r, inout AF1 g, inout AF1 b) {}
+ #endif
+#else
+ layout (binding=0) uniform sampler2D InputTexture;
+ #if USE_EASU
+ #define FSR_EASU_H 1
+ AH4 FsrEasuRH(AF2 p) { AH4 res = AH4(textureGather(InputTexture, p, 0)); return res; }
+ AH4 FsrEasuGH(AF2 p) { AH4 res = AH4(textureGather(InputTexture, p, 1)); return res; }
+ AH4 FsrEasuBH(AF2 p) { AH4 res = AH4(textureGather(InputTexture, p, 2)); return res; }
+ #endif
+ #if USE_RCAS
+ #define FSR_RCAS_H
+ AH4 FsrRcasLoadH(ASW2 p) { return AH4(texelFetch(InputTexture, ASU2(p), 0)); }
+ void FsrRcasInputH(inout AH1 r,inout AH1 g,inout AH1 b){}
+ #endif
+#endif
+
+#include "ffx_fsr1.h"
+
+#if USE_RCAS
+ layout(location = 0) in vec2 frag_texcoord;
+#endif
+layout (location = 0) out vec4 frag_color;
+
+void CurrFilter(AU2 pos)
+{
+#if USE_EASU
+ #ifndef YUZU_USE_FP16
+ AF3 c;
+ FsrEasuF(c, pos, constants[0], constants[1], constants[2], constants[3]);
+ frag_color = AF4(c, 1.0);
+ #else
+ AH3 c;
+ FsrEasuH(c, pos, constants[0], constants[1], constants[2], constants[3]);
+ frag_color = AH4(c, 1.0);
+ #endif
+#endif
+#if USE_RCAS
+ #ifndef YUZU_USE_FP16
+ AF3 c;
+ FsrRcasF(c.r, c.g, c.b, pos, constants[0]);
+ frag_color = AF4(c, 1.0);
+ #else
+ AH3 c;
+ FsrRcasH(c.r, c.g, c.b, pos, constants[0]);
+ frag_color = AH4(c, 1.0);
+ #endif
+#endif
+}
+
+void main()
+{
+#if USE_RCAS
+ CurrFilter(AU2(frag_texcoord * vec2(textureSize(InputTexture, 0))));
+#else
+ CurrFilter(AU2(gl_FragCoord.xy));
+#endif
+}
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 @@
+// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#version 460 core
+#extension GL_GOOGLE_include_directive : enable
+
+#define USE_EASU 1
+
+#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 @@
+// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#version 460 core
+#extension GL_GOOGLE_include_directive : enable
+
+#define USE_RCAS 1
+
+#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 @@
+// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#version 460 core
+
+layout (push_constant) uniform PushConstants {
+ vec4 clear_color;
+};
+
+layout(location = 0) out vec4 color;
+
+void main() {
+ color = clear_color;
+}
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 @@
+// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#version 460 core
+
+void main() {
+ float x = float((gl_VertexIndex & 1) << 2);
+ float y = float((gl_VertexIndex & 2) << 1);
+ gl_Position = vec4(x - 1.0, y - 1.0, 0.0, 1.0);
+}
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 @@
#include "common/alignment.h"
#include "common/assert.h"
#include "common/logging/log.h"
-#include "common/settings.h"
#include "core/core.h"
#include "core/device_memory.h"
#include "core/hle/kernel/k_page_table.h"
@@ -46,11 +45,6 @@ MemoryManager::MemoryManager(Core::System& system_, u64 address_space_bits_, u64
big_page_table_cpu.resize(big_page_table_size);
big_page_continous.resize(big_page_table_size / continous_bits, 0);
entries.resize(page_table_size / 32, 0);
- if (!Settings::IsGPULevelExtreme() && Settings::IsFastmemEnabled()) {
- fastmem_arena = system.DeviceMemory().buffer.VirtualBasePointer();
- } else {
- fastmem_arena = nullptr;
- }
}
MemoryManager::~MemoryManager() = default;
@@ -360,7 +354,7 @@ inline void MemoryManager::MemoryOperation(GPUVAddr gpu_src_addr, std::size_t si
}
}
-template <bool is_safe, bool use_fastmem>
+template <bool is_safe>
void MemoryManager::ReadBlockImpl(GPUVAddr gpu_src_addr, void* dest_buffer, std::size_t size,
[[maybe_unused]] VideoCommon::CacheType which) const {
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:
if constexpr (is_safe) {
rasterizer->FlushRegion(cpu_addr_base, copy_amount, which);
}
- if constexpr (use_fastmem) {
- std::memcpy(dest_buffer, &fastmem_arena[cpu_addr_base], copy_amount);
- } else {
- u8* physical = memory.GetPointer(cpu_addr_base);
- std::memcpy(dest_buffer, physical, copy_amount);
- }
+ u8* physical = memory.GetPointer(cpu_addr_base);
+ std::memcpy(dest_buffer, physical, copy_amount);
dest_buffer = static_cast<u8*>(dest_buffer) + copy_amount;
};
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:
if constexpr (is_safe) {
rasterizer->FlushRegion(cpu_addr_base, copy_amount, which);
}
- if constexpr (use_fastmem) {
- std::memcpy(dest_buffer, &fastmem_arena[cpu_addr_base], copy_amount);
+ if (!IsBigPageContinous(page_index)) [[unlikely]] {
+ memory.ReadBlockUnsafe(cpu_addr_base, dest_buffer, copy_amount);
} else {
- if (!IsBigPageContinous(page_index)) [[unlikely]] {
- memory.ReadBlockUnsafe(cpu_addr_base, dest_buffer, copy_amount);
- } else {
- u8* physical = memory.GetPointer(cpu_addr_base);
- std::memcpy(dest_buffer, physical, copy_amount);
- }
+ u8* physical = memory.GetPointer(cpu_addr_base);
+ std::memcpy(dest_buffer, physical, copy_amount);
}
dest_buffer = static_cast<u8*>(dest_buffer) + copy_amount;
};
@@ -410,20 +396,12 @@ void MemoryManager::ReadBlockImpl(GPUVAddr gpu_src_addr, void* dest_buffer, std:
void MemoryManager::ReadBlock(GPUVAddr gpu_src_addr, void* dest_buffer, std::size_t size,
VideoCommon::CacheType which) const {
- if (fastmem_arena) [[likely]] {
- ReadBlockImpl<true, true>(gpu_src_addr, dest_buffer, size, which);
- return;
- }
- ReadBlockImpl<true, false>(gpu_src_addr, dest_buffer, size, which);
+ ReadBlockImpl<true>(gpu_src_addr, dest_buffer, size, which);
}
void MemoryManager::ReadBlockUnsafe(GPUVAddr gpu_src_addr, void* dest_buffer,
const std::size_t size) const {
- if (fastmem_arena) [[likely]] {
- ReadBlockImpl<false, true>(gpu_src_addr, dest_buffer, size, VideoCommon::CacheType::None);
- return;
- }
- ReadBlockImpl<false, false>(gpu_src_addr, dest_buffer, size, VideoCommon::CacheType::None);
+ ReadBlockImpl<false>(gpu_src_addr, dest_buffer, size, VideoCommon::CacheType::None);
}
template <bool is_safe>
diff --git a/src/video_core/memory_manager.h b/src/video_core/memory_manager.h
index 2936364f0..cf56392ef 100644
--- a/src/video_core/memory_manager.h
+++ b/src/video_core/memory_manager.h
@@ -141,7 +141,7 @@ private:
inline void MemoryOperation(GPUVAddr gpu_src_addr, std::size_t size, FuncMapped&& func_mapped,
FuncReserved&& func_reserved, FuncUnmapped&& func_unmapped) const;
- template <bool is_safe, bool use_fastmem>
+ template <bool is_safe>
void ReadBlockImpl(GPUVAddr gpu_src_addr, void* dest_buffer, std::size_t size,
VideoCommon::CacheType which) const;
@@ -215,9 +215,8 @@ private:
std::vector<u64> big_page_continous;
std::vector<std::pair<VAddr, std::size_t>> page_stash{};
- u8* fastmem_arena{};
- constexpr static size_t continous_bits = 64;
+ static constexpr size_t continous_bits = 64;
const size_t unique_identifier;
std::unique_ptr<VideoCommon::InvalidationAccumulator> accumulator;
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:
/// Dispatches an indirect draw invocation
virtual void DrawIndirect() {}
+ /// Dispatches an draw texture invocation
+ virtual void DrawTexture() = 0;
+
/// Clear the current framebuffer
virtual void Clear(u32 layer_count) = 0;
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
RasterizerNull::~RasterizerNull() = default;
void RasterizerNull::Draw(bool is_indexed, u32 instance_count) {}
+void RasterizerNull::DrawTexture() {}
void RasterizerNull::Clear(u32 layer_count) {}
void RasterizerNull::DispatchCompute() {}
void 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:
~RasterizerNull() override;
void Draw(bool is_indexed, u32 instance_count) override;
+ void DrawTexture() override;
void Clear(u32 layer_count) override;
void DispatchCompute() override;
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 @@
+// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include <algorithm>
+
+#include "video_core/host_shaders/blit_color_float_frag.h"
+#include "video_core/host_shaders/full_screen_triangle_vert.h"
+#include "video_core/renderer_opengl/blit_image.h"
+#include "video_core/renderer_opengl/gl_shader_manager.h"
+#include "video_core/renderer_opengl/gl_shader_util.h"
+
+namespace OpenGL {
+
+BlitImageHelper::BlitImageHelper(ProgramManager& program_manager_)
+ : program_manager(program_manager_),
+ full_screen_vert(CreateProgram(HostShaders::FULL_SCREEN_TRIANGLE_VERT, GL_VERTEX_SHADER)),
+ blit_color_to_color_frag(
+ CreateProgram(HostShaders::BLIT_COLOR_FLOAT_FRAG, GL_FRAGMENT_SHADER)) {}
+
+BlitImageHelper::~BlitImageHelper() = default;
+
+void BlitImageHelper::BlitColor(GLuint dst_framebuffer, GLuint src_image_view, GLuint src_sampler,
+ const Region2D& dst_region, const Region2D& src_region,
+ const Extent3D& src_size) {
+ glEnable(GL_CULL_FACE);
+ glDisable(GL_COLOR_LOGIC_OP);
+ glDisable(GL_DEPTH_TEST);
+ glDisable(GL_STENCIL_TEST);
+ glDisable(GL_POLYGON_OFFSET_FILL);
+ glDisable(GL_RASTERIZER_DISCARD);
+ glDisable(GL_ALPHA_TEST);
+ glDisablei(GL_BLEND, 0);
+ glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
+ glCullFace(GL_BACK);
+ glFrontFace(GL_CW);
+ glColorMaski(0, GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
+ glDepthRangeIndexed(0, 0.0, 0.0);
+
+ program_manager.BindPresentPrograms(full_screen_vert.handle, blit_color_to_color_frag.handle);
+ glProgramUniform2f(full_screen_vert.handle, 0,
+ static_cast<float>(src_region.end.x - src_region.start.x) /
+ static_cast<float>(src_size.width),
+ static_cast<float>(src_region.end.y - src_region.start.y) /
+ static_cast<float>(src_size.height));
+ glProgramUniform2f(full_screen_vert.handle, 1,
+ static_cast<float>(src_region.start.x) / static_cast<float>(src_size.width),
+ static_cast<float>(src_region.start.y) /
+ static_cast<float>(src_size.height));
+ glViewport(std::min(dst_region.start.x, dst_region.end.x),
+ std::min(dst_region.start.y, dst_region.end.y),
+ std::abs(dst_region.end.x - dst_region.start.x),
+ std::abs(dst_region.end.y - dst_region.start.y));
+ glBindFramebuffer(GL_DRAW_FRAMEBUFFER, dst_framebuffer);
+ glBindSampler(0, src_sampler);
+ glBindTextureUnit(0, src_image_view);
+ glClear(GL_COLOR_BUFFER_BIT);
+ glDrawArrays(GL_TRIANGLES, 0, 3);
+}
+} // 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 @@
+// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#pragma once
+
+#include <glad/glad.h>
+
+#include "video_core/engines/fermi_2d.h"
+#include "video_core/renderer_opengl/gl_resource_manager.h"
+#include "video_core/texture_cache/types.h"
+
+namespace OpenGL {
+
+using VideoCommon::Extent3D;
+using VideoCommon::Offset2D;
+using VideoCommon::Region2D;
+
+class ProgramManager;
+class Framebuffer;
+class ImageView;
+
+class BlitImageHelper {
+public:
+ explicit BlitImageHelper(ProgramManager& program_manager);
+ ~BlitImageHelper();
+
+ void BlitColor(GLuint dst_framebuffer, GLuint src_image_view, GLuint src_sampler,
+ const Region2D& dst_region, const Region2D& src_region,
+ const Extent3D& src_size);
+
+private:
+ ProgramManager& program_manager;
+
+ OGLProgram full_screen_vert;
+ OGLProgram blit_color_to_color_frag;
+};
+
+} // 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
ComputePipeline::ComputePipeline(const Device& device, TextureCache& texture_cache_,
BufferCache& buffer_cache_, ProgramManager& program_manager_,
const Shader::Info& info_, std::string code,
- std::vector<u32> code_v)
+ std::vector<u32> code_v, bool force_context_flush)
: texture_cache{texture_cache_}, buffer_cache{buffer_cache_},
program_manager{program_manager_}, info{info_} {
switch (device.GetShaderBackend()) {
@@ -63,6 +63,15 @@ ComputePipeline::ComputePipeline(const Device& device, TextureCache& texture_cac
writes_global_memory = !use_storage_buffers &&
std::ranges::any_of(info.storage_buffers_descriptors,
[](const auto& desc) { return desc.is_written; });
+ if (force_context_flush) {
+ std::scoped_lock lock{built_mutex};
+ built_fence.Create();
+ // Flush this context to ensure compilation commands and fence are in the GPU pipe.
+ glFlush();
+ built_condvar.notify_one();
+ } else {
+ is_built = true;
+ }
}
void ComputePipeline::Configure() {
@@ -142,6 +151,9 @@ void ComputePipeline::Configure() {
}
texture_cache.FillComputeImageViews(std::span(views.data(), views.size()));
+ if (!is_built) {
+ WaitForBuild();
+ }
if (assembly_program.handle != 0) {
program_manager.BindComputeAssemblyProgram(assembly_program.handle);
} else {
@@ -223,4 +235,13 @@ void ComputePipeline::Configure() {
}
}
+void ComputePipeline::WaitForBuild() {
+ if (built_fence.handle == 0) {
+ std::unique_lock lock{built_mutex};
+ built_condvar.wait(lock, [this] { return built_fence.handle != 0; });
+ }
+ ASSERT(glClientWaitSync(built_fence.handle, 0, GL_TIMEOUT_IGNORED) != GL_WAIT_FAILED);
+ is_built = true;
+}
+
} // 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 {
public:
explicit ComputePipeline(const Device& device, TextureCache& texture_cache_,
BufferCache& buffer_cache_, ProgramManager& program_manager_,
- const Shader::Info& info_, std::string code, std::vector<u32> code_v);
+ const Shader::Info& info_, std::string code, std::vector<u32> code_v,
+ bool force_context_flush = false);
void Configure();
@@ -65,6 +66,8 @@ public:
}
private:
+ void WaitForBuild();
+
TextureCache& texture_cache;
BufferCache& buffer_cache;
Tegra::MemoryManager* gpu_memory;
@@ -81,6 +84,11 @@ private:
bool use_storage_buffers{};
bool writes_global_memory{};
+
+ std::mutex built_mutex;
+ std::condition_variable built_condvar;
+ OGLSync built_fence{};
+ bool is_built{false};
};
} // 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) {
has_shader_int64 = HasExtension(extensions, "GL_ARB_gpu_shader_int64");
has_amd_shader_half_float = GLAD_GL_AMD_gpu_shader_half_float;
has_sparse_texture_2 = GLAD_GL_ARB_sparse_texture2;
+ has_draw_texture = GLAD_GL_NV_draw_texture;
warp_size_potentially_larger_than_guest = !is_nvidia && !is_intel;
need_fastmath_off = is_nvidia;
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 @@
#pragma once
#include <cstddef>
+#include <string>
+
#include "common/common_types.h"
#include "core/frontend/emu_window.h"
#include "shader_recompiler/stage.h"
@@ -146,6 +148,10 @@ public:
return has_sparse_texture_2;
}
+ bool HasDrawTexture() const {
+ return has_draw_texture;
+ }
+
bool IsWarpSizePotentiallyLargerThanGuest() const {
return warp_size_potentially_larger_than_guest;
}
@@ -216,6 +222,7 @@ private:
bool has_shader_int64{};
bool has_amd_shader_half_float{};
bool has_sparse_texture_2{};
+ bool has_draw_texture{};
bool warp_size_potentially_larger_than_guest{};
bool need_fastmath_off{};
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 @@
+// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include "common/settings.h"
+#include "video_core/fsr.h"
+#include "video_core/renderer_opengl/gl_fsr.h"
+#include "video_core/renderer_opengl/gl_shader_manager.h"
+#include "video_core/renderer_opengl/gl_shader_util.h"
+
+namespace OpenGL {
+using namespace FSR;
+
+using FsrConstants = std::array<u32, 4 * 4>;
+
+FSR::FSR(std::string_view fsr_vertex_source, std::string_view fsr_easu_source,
+ std::string_view fsr_rcas_source)
+ : fsr_vertex{CreateProgram(fsr_vertex_source, GL_VERTEX_SHADER)},
+ fsr_easu_frag{CreateProgram(fsr_easu_source, GL_FRAGMENT_SHADER)},
+ fsr_rcas_frag{CreateProgram(fsr_rcas_source, GL_FRAGMENT_SHADER)} {
+ glProgramUniform2f(fsr_vertex.handle, 0, 1.0f, 1.0f);
+ glProgramUniform2f(fsr_vertex.handle, 1, 0.0f, 0.0f);
+}
+
+FSR::~FSR() = default;
+
+void FSR::Draw(ProgramManager& program_manager, const Common::Rectangle<u32>& screen,
+ u32 input_image_width, u32 input_image_height,
+ const Common::Rectangle<int>& crop_rect) {
+
+ const auto output_image_width = screen.GetWidth();
+ const auto output_image_height = screen.GetHeight();
+
+ if (fsr_intermediate_tex.handle) {
+ GLint fsr_tex_width, fsr_tex_height;
+ glGetTextureLevelParameteriv(fsr_intermediate_tex.handle, 0, GL_TEXTURE_WIDTH,
+ &fsr_tex_width);
+ glGetTextureLevelParameteriv(fsr_intermediate_tex.handle, 0, GL_TEXTURE_HEIGHT,
+ &fsr_tex_height);
+ if (static_cast<u32>(fsr_tex_width) != output_image_width ||
+ static_cast<u32>(fsr_tex_height) != output_image_height) {
+ fsr_intermediate_tex.Release();
+ }
+ }
+ if (!fsr_intermediate_tex.handle) {
+ fsr_intermediate_tex.Create(GL_TEXTURE_2D);
+ glTextureStorage2D(fsr_intermediate_tex.handle, 1, GL_RGB16F, output_image_width,
+ output_image_height);
+ glNamedFramebufferTexture(fsr_framebuffer.handle, GL_COLOR_ATTACHMENT0,
+ fsr_intermediate_tex.handle, 0);
+ }
+
+ GLint old_draw_fb;
+ glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, &old_draw_fb);
+
+ glFrontFace(GL_CW);
+ glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fsr_framebuffer.handle);
+ glViewportIndexedf(0, 0.0f, 0.0f, static_cast<GLfloat>(output_image_width),
+ static_cast<GLfloat>(output_image_height));
+
+ FsrConstants constants;
+ FsrEasuConOffset(
+ constants.data() + 0, constants.data() + 4, constants.data() + 8, constants.data() + 12,
+
+ static_cast<f32>(crop_rect.GetWidth()), static_cast<f32>(crop_rect.GetHeight()),
+ static_cast<f32>(input_image_width), static_cast<f32>(input_image_height),
+ static_cast<f32>(output_image_width), static_cast<f32>(output_image_height),
+ static_cast<f32>(crop_rect.left), static_cast<f32>(crop_rect.top));
+
+ glProgramUniform4uiv(fsr_easu_frag.handle, 0, sizeof(constants), std::data(constants));
+
+ program_manager.BindPresentPrograms(fsr_vertex.handle, fsr_easu_frag.handle);
+ glDrawArrays(GL_TRIANGLES, 0, 3);
+
+ glBindFramebuffer(GL_DRAW_FRAMEBUFFER, old_draw_fb);
+ glBindTextureUnit(0, fsr_intermediate_tex.handle);
+
+ const float sharpening =
+ static_cast<float>(Settings::values.fsr_sharpening_slider.GetValue()) / 100.0f;
+
+ FsrRcasCon(constants.data(), sharpening);
+ glProgramUniform4uiv(fsr_rcas_frag.handle, 0, sizeof(constants), std::data(constants));
+}
+
+void FSR::InitBuffers() {
+ fsr_framebuffer.Create();
+}
+
+void FSR::ReleaseBuffers() {
+ fsr_framebuffer.Release();
+ fsr_intermediate_tex.Release();
+}
+
+const OGLProgram& FSR::GetPresentFragmentProgram() const noexcept {
+ return fsr_rcas_frag;
+}
+
+bool FSR::AreBuffersInitialized() const noexcept {
+ return fsr_framebuffer.handle;
+}
+
+} // 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 @@
+// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#pragma once
+
+#include <string_view>
+
+#include "common/common_types.h"
+#include "common/math_util.h"
+#include "video_core/fsr.h"
+#include "video_core/renderer_opengl/gl_resource_manager.h"
+
+namespace OpenGL {
+
+class ProgramManager;
+
+class FSR {
+public:
+ explicit FSR(std::string_view fsr_vertex_source, std::string_view fsr_easu_source,
+ std::string_view fsr_rcas_source);
+ ~FSR();
+
+ void Draw(ProgramManager& program_manager, const Common::Rectangle<u32>& screen,
+ u32 input_image_width, u32 input_image_height,
+ const Common::Rectangle<int>& crop_rect);
+
+ void InitBuffers();
+
+ void ReleaseBuffers();
+
+ [[nodiscard]] const OGLProgram& GetPresentFragmentProgram() const noexcept;
+
+ [[nodiscard]] bool AreBuffersInitialized() const noexcept;
+
+private:
+ OGLFramebuffer fsr_framebuffer;
+ OGLProgram fsr_vertex;
+ OGLProgram fsr_easu_frag;
+ OGLProgram fsr_rcas_frag;
+ OGLTexture fsr_intermediate_tex;
+};
+
+} // 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
std::array<std::string, 5> sources,
std::array<std::vector<u32>, 5> sources_spirv,
const std::array<const Shader::Info*, 5>& infos,
- const GraphicsPipelineKey& key_)
+ const GraphicsPipelineKey& key_, bool force_context_flush)
: texture_cache{texture_cache_}, buffer_cache{buffer_cache_}, program_manager{program_manager_},
state_tracker{state_tracker_}, key{key_} {
if (shader_notify) {
@@ -231,7 +231,8 @@ GraphicsPipeline::GraphicsPipeline(const Device& device, TextureCache& texture_c
const bool in_parallel = thread_worker != nullptr;
const auto backend = device.GetShaderBackend();
auto func{[this, sources = std::move(sources), sources_spirv = std::move(sources_spirv),
- shader_notify, backend, in_parallel](ShaderContext::Context*) mutable {
+ shader_notify, backend, in_parallel,
+ force_context_flush](ShaderContext::Context*) mutable {
for (size_t stage = 0; stage < 5; ++stage) {
switch (backend) {
case Settings::ShaderBackend::GLSL:
@@ -251,7 +252,7 @@ GraphicsPipeline::GraphicsPipeline(const Device& device, TextureCache& texture_c
break;
}
}
- if (in_parallel) {
+ if (force_context_flush || in_parallel) {
std::scoped_lock lock{built_mutex};
built_fence.Create();
// 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:
std::array<std::string, 5> sources,
std::array<std::vector<u32>, 5> sources_spirv,
const std::array<const Shader::Info*, 5>& infos,
- const GraphicsPipelineKey& key_);
+ const GraphicsPipelineKey& key_, bool force_context_flush = false);
void Configure(bool is_indexed) {
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 7d48af8e1..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
shader_cache(*this, emu_window_, device, texture_cache, buffer_cache, program_manager,
state_tracker, gpu.ShaderNotify()),
query_cache(*this), accelerate_dma(buffer_cache),
- fence_manager(*this, gpu, texture_cache, buffer_cache, query_cache) {}
+ fence_manager(*this, gpu, texture_cache, buffer_cache, query_cache),
+ blit_image(program_manager_) {}
RasterizerOpenGL::~RasterizerOpenGL() = default;
@@ -139,6 +140,7 @@ void RasterizerOpenGL::LoadDiskResources(u64 title_id, std::stop_token stop_load
void RasterizerOpenGL::Clear(u32 layer_count) {
MICROPROFILE_SCOPE(OpenGL_Clears);
+ gpu_memory->FlushCaching();
const auto& regs = maxwell3d->regs;
bool use_color{};
bool use_depth{};
@@ -207,6 +209,7 @@ void RasterizerOpenGL::PrepareDraw(bool is_indexed, Func&& draw_func) {
MICROPROFILE_SCOPE(OpenGL_Drawing);
SCOPE_EXIT({ gpu.TickWork(); });
+ gpu_memory->FlushCaching();
query_cache.UpdateCounters();
GraphicsPipeline* const pipeline{shader_cache.CurrentGraphicsPipeline()};
@@ -318,7 +321,49 @@ void RasterizerOpenGL::DrawIndirect() {
buffer_cache.SetDrawIndirect(nullptr);
}
+void RasterizerOpenGL::DrawTexture() {
+ MICROPROFILE_SCOPE(OpenGL_Drawing);
+
+ SCOPE_EXIT({ gpu.TickWork(); });
+ query_cache.UpdateCounters();
+
+ texture_cache.SynchronizeGraphicsDescriptors();
+ texture_cache.UpdateRenderTargets(false);
+
+ SyncState();
+
+ const auto& draw_texture_state = maxwell3d->draw_manager->GetDrawTextureState();
+ const auto& sampler = texture_cache.GetGraphicsSampler(draw_texture_state.src_sampler);
+ const auto& texture = texture_cache.GetImageView(draw_texture_state.src_texture);
+
+ if (device.HasDrawTexture()) {
+ state_tracker.BindFramebuffer(texture_cache.GetFramebuffer()->Handle());
+
+ glDrawTextureNV(texture.DefaultHandle(), sampler->Handle(), draw_texture_state.dst_x0,
+ draw_texture_state.dst_y0, draw_texture_state.dst_x1,
+ draw_texture_state.dst_y1, 0,
+ draw_texture_state.src_x0 / static_cast<float>(texture.size.width),
+ draw_texture_state.src_y0 / static_cast<float>(texture.size.height),
+ draw_texture_state.src_x1 / static_cast<float>(texture.size.width),
+ draw_texture_state.src_y1 / static_cast<float>(texture.size.height));
+ } else {
+ Region2D dst_region = {Offset2D{.x = static_cast<s32>(draw_texture_state.dst_x0),
+ .y = static_cast<s32>(draw_texture_state.dst_y0)},
+ Offset2D{.x = static_cast<s32>(draw_texture_state.dst_x1),
+ .y = static_cast<s32>(draw_texture_state.dst_y1)}};
+ Region2D src_region = {Offset2D{.x = static_cast<s32>(draw_texture_state.src_x0),
+ .y = static_cast<s32>(draw_texture_state.src_y0)},
+ Offset2D{.x = static_cast<s32>(draw_texture_state.src_x1),
+ .y = static_cast<s32>(draw_texture_state.src_y1)}};
+ blit_image.BlitColor(texture_cache.GetFramebuffer()->Handle(), texture.DefaultHandle(),
+ sampler->Handle(), dst_region, src_region, texture.size);
+ }
+
+ ++num_queued_commands;
+}
+
void RasterizerOpenGL::DispatchCompute() {
+ gpu_memory->FlushCaching();
ComputePipeline* const pipeline{shader_cache.CurrentComputePipeline()};
if (!pipeline) {
return;
@@ -526,6 +571,7 @@ void RasterizerOpenGL::TickFrame() {
}
bool RasterizerOpenGL::AccelerateConditionalRendering() {
+ gpu_memory->FlushCaching();
if (Settings::IsGPULevelHigh()) {
// Reimplement Host conditional rendering.
return false;
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 @@
#include "video_core/engines/maxwell_dma.h"
#include "video_core/rasterizer_accelerated.h"
#include "video_core/rasterizer_interface.h"
+#include "video_core/renderer_opengl/blit_image.h"
#include "video_core/renderer_opengl/gl_buffer_cache.h"
#include "video_core/renderer_opengl/gl_device.h"
#include "video_core/renderer_opengl/gl_fence_manager.h"
@@ -70,6 +71,7 @@ public:
void Draw(bool is_indexed, u32 instance_count) override;
void DrawIndirect() override;
+ void DrawTexture() override;
void Clear(u32 layer_count) override;
void DispatchCompute() override;
void ResetCounter(VideoCore::QueryType type) override;
@@ -224,6 +226,8 @@ private:
AccelerateDMA accelerate_dma;
FenceManagerOpenGL fence_manager;
+ BlitImageHelper blit_image;
+
boost::container::static_vector<u32, MAX_IMAGE_VIEWS> image_view_indices;
std::array<ImageViewId, MAX_IMAGE_VIEWS> image_view_ids;
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 9442193de..479bb8ba3 100644
--- a/src/video_core/renderer_opengl/gl_shader_cache.cpp
+++ b/src/video_core/renderer_opengl/gl_shader_cache.cpp
@@ -285,7 +285,7 @@ void ShaderCache::LoadDiskResources(u64 title_id, std::stop_token stop_loading,
file.read(reinterpret_cast<char*>(&key), sizeof(key));
queue_work([this, key, env = std::move(env), &state, &callback](Context* ctx) mutable {
ctx->pools.ReleaseContents();
- auto pipeline{CreateComputePipeline(ctx->pools, key, env)};
+ auto pipeline{CreateComputePipeline(ctx->pools, key, env, true)};
std::scoped_lock lock{state.mutex};
if (pipeline) {
compute_cache.emplace(key, std::move(pipeline));
@@ -306,7 +306,7 @@ void ShaderCache::LoadDiskResources(u64 title_id, std::stop_token stop_loading,
env_ptrs.push_back(&env);
}
ctx->pools.ReleaseContents();
- auto pipeline{CreateGraphicsPipeline(ctx->pools, key, MakeSpan(env_ptrs), false)};
+ auto pipeline{CreateGraphicsPipeline(ctx->pools, key, MakeSpan(env_ptrs), false, true)};
std::scoped_lock lock{state.mutex};
if (pipeline) {
graphics_cache.emplace(key, std::move(pipeline));
@@ -438,7 +438,8 @@ std::unique_ptr<GraphicsPipeline> ShaderCache::CreateGraphicsPipeline() {
std::unique_ptr<GraphicsPipeline> ShaderCache::CreateGraphicsPipeline(
ShaderContext::ShaderPools& pools, const GraphicsPipelineKey& key,
- std::span<Shader::Environment* const> envs, bool build_in_parallel) try {
+ std::span<Shader::Environment* const> envs, bool use_shader_workers,
+ bool force_context_flush) try {
LOG_INFO(Render_OpenGL, "0x{:016x}", key.Hash());
size_t env_index{};
u32 total_storage_buffers{};
@@ -530,10 +531,10 @@ std::unique_ptr<GraphicsPipeline> ShaderCache::CreateGraphicsPipeline(
}
previous_program = &program;
}
- auto* const thread_worker{build_in_parallel ? workers.get() : nullptr};
+ auto* const thread_worker{use_shader_workers ? workers.get() : nullptr};
return std::make_unique<GraphicsPipeline>(device, texture_cache, buffer_cache, program_manager,
state_tracker, thread_worker, &shader_notify, sources,
- sources_spirv, infos, key);
+ sources_spirv, infos, key, force_context_flush);
} catch (Shader::Exception& exception) {
LOG_ERROR(Render_OpenGL, "{}", exception.what());
@@ -558,8 +559,8 @@ std::unique_ptr<ComputePipeline> ShaderCache::CreateComputePipeline(
}
std::unique_ptr<ComputePipeline> ShaderCache::CreateComputePipeline(
- ShaderContext::ShaderPools& pools, const ComputePipelineKey& key,
- Shader::Environment& env) try {
+ ShaderContext::ShaderPools& pools, const ComputePipelineKey& key, Shader::Environment& env,
+ bool force_context_flush) try {
LOG_INFO(Render_OpenGL, "0x{:016x}", key.Hash());
Shader::Maxwell::Flow::CFG cfg{env, pools.flow_block, env.StartAddress()};
@@ -588,7 +589,7 @@ std::unique_ptr<ComputePipeline> ShaderCache::CreateComputePipeline(
}
return std::make_unique<ComputePipeline>(device, texture_cache, buffer_cache, program_manager,
- program.info, code, code_spirv);
+ program.info, code, code_spirv, force_context_flush);
} catch (Shader::Exception& exception) {
LOG_ERROR(Render_OpenGL, "{}", exception.what());
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:
std::unique_ptr<GraphicsPipeline> CreateGraphicsPipeline(
ShaderContext::ShaderPools& pools, const GraphicsPipelineKey& key,
- std::span<Shader::Environment* const> envs, bool build_in_parallel);
+ std::span<Shader::Environment* const> envs, bool use_shader_workers,
+ bool force_context_flush = false);
std::unique_ptr<ComputePipeline> CreateComputePipeline(const ComputePipelineKey& key,
const VideoCommon::ShaderInfo* shader);
std::unique_ptr<ComputePipeline> CreateComputePipeline(ShaderContext::ShaderPools& pools,
const ComputePipelineKey& key,
- Shader::Environment& env);
+ Shader::Environment& env,
+ bool force_context_flush = false);
std::unique_ptr<ShaderWorker> CreateWorkers() const;
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 @@
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include <glad/glad.h>
+
+#include "video_core/renderer_opengl/gl_shader_manager.h"
+
+namespace OpenGL {
+
+static constexpr std::array ASSEMBLY_PROGRAM_ENUMS{
+ GL_VERTEX_PROGRAM_NV, GL_TESS_CONTROL_PROGRAM_NV, GL_TESS_EVALUATION_PROGRAM_NV,
+ GL_GEOMETRY_PROGRAM_NV, GL_FRAGMENT_PROGRAM_NV,
+};
+
+ProgramManager::ProgramManager(const Device& device) {
+ glCreateProgramPipelines(1, &pipeline.handle);
+ if (device.UseAssemblyShaders()) {
+ glEnable(GL_COMPUTE_PROGRAM_NV);
+ }
+}
+
+void ProgramManager::BindComputeProgram(GLuint program) {
+ glUseProgram(program);
+ is_compute_bound = true;
+}
+
+void ProgramManager::BindComputeAssemblyProgram(GLuint program) {
+ if (current_assembly_compute_program != program) {
+ current_assembly_compute_program = program;
+ glBindProgramARB(GL_COMPUTE_PROGRAM_NV, program);
+ }
+ UnbindPipeline();
+}
+
+void ProgramManager::BindSourcePrograms(std::span<const OGLProgram, NUM_STAGES> programs) {
+ static constexpr std::array<GLenum, 5> stage_enums{
+ GL_VERTEX_SHADER_BIT, GL_TESS_CONTROL_SHADER_BIT, GL_TESS_EVALUATION_SHADER_BIT,
+ GL_GEOMETRY_SHADER_BIT, GL_FRAGMENT_SHADER_BIT,
+ };
+ for (size_t stage = 0; stage < NUM_STAGES; ++stage) {
+ if (current_programs[stage] != programs[stage].handle) {
+ current_programs[stage] = programs[stage].handle;
+ glUseProgramStages(pipeline.handle, stage_enums[stage], programs[stage].handle);
+ }
+ }
+ BindPipeline();
+}
+
+void ProgramManager::BindPresentPrograms(GLuint vertex, GLuint fragment) {
+ if (current_programs[0] != vertex) {
+ current_programs[0] = vertex;
+ glUseProgramStages(pipeline.handle, GL_VERTEX_SHADER_BIT, vertex);
+ }
+ if (current_programs[4] != fragment) {
+ current_programs[4] = fragment;
+ glUseProgramStages(pipeline.handle, GL_FRAGMENT_SHADER_BIT, fragment);
+ }
+ glUseProgramStages(
+ pipeline.handle,
+ GL_TESS_CONTROL_SHADER_BIT | GL_TESS_EVALUATION_SHADER_BIT | GL_GEOMETRY_SHADER_BIT, 0);
+ current_programs[1] = 0;
+ current_programs[2] = 0;
+ current_programs[3] = 0;
+
+ if (current_stage_mask != 0) {
+ current_stage_mask = 0;
+ for (const GLenum program_type : ASSEMBLY_PROGRAM_ENUMS) {
+ glDisable(program_type);
+ }
+ }
+ BindPipeline();
+}
+
+void ProgramManager::BindAssemblyPrograms(std::span<const OGLAssemblyProgram, NUM_STAGES> programs,
+ u32 stage_mask) {
+ const u32 changed_mask = current_stage_mask ^ stage_mask;
+ current_stage_mask = stage_mask;
+
+ if (changed_mask != 0) {
+ for (size_t stage = 0; stage < NUM_STAGES; ++stage) {
+ if (((changed_mask >> stage) & 1) != 0) {
+ if (((stage_mask >> stage) & 1) != 0) {
+ glEnable(ASSEMBLY_PROGRAM_ENUMS[stage]);
+ } else {
+ glDisable(ASSEMBLY_PROGRAM_ENUMS[stage]);
+ }
+ }
+ }
+ }
+ for (size_t stage = 0; stage < NUM_STAGES; ++stage) {
+ if (current_programs[stage] != programs[stage].handle) {
+ current_programs[stage] = programs[stage].handle;
+ glBindProgramARB(ASSEMBLY_PROGRAM_ENUMS[stage], programs[stage].handle);
+ }
+ }
+ UnbindPipeline();
+}
+
+void ProgramManager::RestoreGuestCompute() {}
+
+void ProgramManager::BindPipeline() {
+ if (!is_pipeline_bound) {
+ is_pipeline_bound = true;
+ glBindProgramPipeline(pipeline.handle);
+ }
+ UnbindCompute();
+}
+
+void ProgramManager::UnbindPipeline() {
+ if (is_pipeline_bound) {
+ is_pipeline_bound = false;
+ glBindProgramPipeline(0);
+ }
+ UnbindCompute();
+}
+
+void ProgramManager::UnbindCompute() {
+ if (is_compute_bound) {
+ is_compute_bound = false;
+ glUseProgram(0);
+ }
+}
+} // 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 @@
#include <array>
#include <span>
-#include <glad/glad.h>
-
#include "video_core/renderer_opengl/gl_device.h"
#include "video_core/renderer_opengl/gl_resource_manager.h"
@@ -16,121 +14,28 @@ namespace OpenGL {
class ProgramManager {
static constexpr size_t NUM_STAGES = 5;
- static constexpr std::array ASSEMBLY_PROGRAM_ENUMS{
- GL_VERTEX_PROGRAM_NV, GL_TESS_CONTROL_PROGRAM_NV, GL_TESS_EVALUATION_PROGRAM_NV,
- GL_GEOMETRY_PROGRAM_NV, GL_FRAGMENT_PROGRAM_NV,
- };
-
public:
- explicit ProgramManager(const Device& device) {
- glCreateProgramPipelines(1, &pipeline.handle);
- if (device.UseAssemblyShaders()) {
- glEnable(GL_COMPUTE_PROGRAM_NV);
- }
- }
-
- void BindComputeProgram(GLuint program) {
- glUseProgram(program);
- is_compute_bound = true;
- }
-
- void BindComputeAssemblyProgram(GLuint program) {
- if (current_assembly_compute_program != program) {
- current_assembly_compute_program = program;
- glBindProgramARB(GL_COMPUTE_PROGRAM_NV, program);
- }
- UnbindPipeline();
- }
-
- void BindSourcePrograms(std::span<const OGLProgram, NUM_STAGES> programs) {
- static constexpr std::array<GLenum, 5> stage_enums{
- GL_VERTEX_SHADER_BIT, GL_TESS_CONTROL_SHADER_BIT, GL_TESS_EVALUATION_SHADER_BIT,
- GL_GEOMETRY_SHADER_BIT, GL_FRAGMENT_SHADER_BIT,
- };
- for (size_t stage = 0; stage < NUM_STAGES; ++stage) {
- if (current_programs[stage] != programs[stage].handle) {
- current_programs[stage] = programs[stage].handle;
- glUseProgramStages(pipeline.handle, stage_enums[stage], programs[stage].handle);
- }
- }
- BindPipeline();
- }
-
- void BindPresentPrograms(GLuint vertex, GLuint fragment) {
- if (current_programs[0] != vertex) {
- current_programs[0] = vertex;
- glUseProgramStages(pipeline.handle, GL_VERTEX_SHADER_BIT, vertex);
- }
- if (current_programs[4] != fragment) {
- current_programs[4] = fragment;
- glUseProgramStages(pipeline.handle, GL_FRAGMENT_SHADER_BIT, fragment);
- }
- glUseProgramStages(
- pipeline.handle,
- GL_TESS_CONTROL_SHADER_BIT | GL_TESS_EVALUATION_SHADER_BIT | GL_GEOMETRY_SHADER_BIT, 0);
- current_programs[1] = 0;
- current_programs[2] = 0;
- current_programs[3] = 0;
-
- if (current_stage_mask != 0) {
- current_stage_mask = 0;
- for (const GLenum program_type : ASSEMBLY_PROGRAM_ENUMS) {
- glDisable(program_type);
- }
- }
- BindPipeline();
- }
+ explicit ProgramManager(const Device& device);
+
+ void BindComputeProgram(GLuint program);
+
+ void BindComputeAssemblyProgram(GLuint program);
+
+ void BindSourcePrograms(std::span<const OGLProgram, NUM_STAGES> programs);
+
+ void BindPresentPrograms(GLuint vertex, GLuint fragment);
void BindAssemblyPrograms(std::span<const OGLAssemblyProgram, NUM_STAGES> programs,
- u32 stage_mask) {
- const u32 changed_mask = current_stage_mask ^ stage_mask;
- current_stage_mask = stage_mask;
-
- if (changed_mask != 0) {
- for (size_t stage = 0; stage < NUM_STAGES; ++stage) {
- if (((changed_mask >> stage) & 1) != 0) {
- if (((stage_mask >> stage) & 1) != 0) {
- glEnable(ASSEMBLY_PROGRAM_ENUMS[stage]);
- } else {
- glDisable(ASSEMBLY_PROGRAM_ENUMS[stage]);
- }
- }
- }
- }
- for (size_t stage = 0; stage < NUM_STAGES; ++stage) {
- if (current_programs[stage] != programs[stage].handle) {
- current_programs[stage] = programs[stage].handle;
- glBindProgramARB(ASSEMBLY_PROGRAM_ENUMS[stage], programs[stage].handle);
- }
- }
- UnbindPipeline();
- }
-
- void RestoreGuestCompute() {}
+ u32 stage_mask);
+
+ void RestoreGuestCompute();
private:
- void BindPipeline() {
- if (!is_pipeline_bound) {
- is_pipeline_bound = true;
- glBindProgramPipeline(pipeline.handle);
- }
- UnbindCompute();
- }
-
- void UnbindPipeline() {
- if (is_pipeline_bound) {
- is_pipeline_bound = false;
- glBindProgramPipeline(0);
- }
- UnbindCompute();
- }
-
- void UnbindCompute() {
- if (is_compute_bound) {
- is_compute_bound = false;
- glUseProgram(0);
- }
- }
+ void BindPipeline();
+
+ void UnbindPipeline();
+
+ void UnbindCompute();
OGLPipeline pipeline;
bool is_pipeline_bound{};
diff --git a/src/video_core/renderer_opengl/gl_texture_cache.cpp b/src/video_core/renderer_opengl/gl_texture_cache.cpp
index 9f7ce7414..eb6e43a08 100644
--- a/src/video_core/renderer_opengl/gl_texture_cache.cpp
+++ b/src/video_core/renderer_opengl/gl_texture_cache.cpp
@@ -557,6 +557,14 @@ void TextureCacheRuntime::CopyImage(Image& dst_image, Image& src_image,
}
}
+void TextureCacheRuntime::CopyImageMSAA(Image& dst_image, Image& src_image,
+ std::span<const VideoCommon::ImageCopy> copies) {
+ LOG_DEBUG(Render_OpenGL, "Copying from {} samples to {} samples", src_image.info.num_samples,
+ dst_image.info.num_samples);
+ // TODO: Leverage the format conversion pass if possible/accurate.
+ util_shaders.CopyMSAA(dst_image, src_image, copies);
+}
+
void TextureCacheRuntime::ReinterpretImage(Image& dst, Image& src,
std::span<const VideoCommon::ImageCopy> copies) {
LOG_DEBUG(Render_OpenGL, "Converting {} to {}", src.info.format, dst.info.format);
diff --git a/src/video_core/renderer_opengl/gl_texture_cache.h b/src/video_core/renderer_opengl/gl_texture_cache.h
index 5d9d370f2..e30875496 100644
--- a/src/video_core/renderer_opengl/gl_texture_cache.h
+++ b/src/video_core/renderer_opengl/gl_texture_cache.h
@@ -93,12 +93,19 @@ public:
return device.CanReportMemoryUsage();
}
- bool ShouldReinterpret([[maybe_unused]] Image& dst, [[maybe_unused]] Image& src) {
+ bool ShouldReinterpret([[maybe_unused]] Image& dst,
+ [[maybe_unused]] Image& src) const noexcept {
+ return true;
+ }
+
+ bool CanUploadMSAA() const noexcept {
return true;
}
void CopyImage(Image& dst, Image& src, std::span<const VideoCommon::ImageCopy> copies);
+ void CopyImageMSAA(Image& dst, Image& src, std::span<const VideoCommon::ImageCopy> copies);
+
void ReinterpretImage(Image& dst, Image& src, std::span<const VideoCommon::ImageCopy> copies);
void ConvertImage(Framebuffer* dst, ImageView& dst_view, ImageView& src_view) {
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 @@
#include "core/frontend/emu_window.h"
#include "core/memory.h"
#include "core/telemetry_session.h"
+#include "video_core/host_shaders/ffx_a_h.h"
+#include "video_core/host_shaders/ffx_fsr1_h.h"
+#include "video_core/host_shaders/full_screen_triangle_vert.h"
#include "video_core/host_shaders/fxaa_frag.h"
#include "video_core/host_shaders/fxaa_vert.h"
+#include "video_core/host_shaders/opengl_fidelityfx_fsr_easu_frag.h"
+#include "video_core/host_shaders/opengl_fidelityfx_fsr_frag.h"
+#include "video_core/host_shaders/opengl_fidelityfx_fsr_rcas_frag.h"
#include "video_core/host_shaders/opengl_present_frag.h"
#include "video_core/host_shaders/opengl_present_scaleforce_frag.h"
#include "video_core/host_shaders/opengl_present_vert.h"
@@ -31,6 +37,7 @@
#include "video_core/host_shaders/smaa_edge_detection_vert.h"
#include "video_core/host_shaders/smaa_neighborhood_blending_frag.h"
#include "video_core/host_shaders/smaa_neighborhood_blending_vert.h"
+#include "video_core/renderer_opengl/gl_fsr.h"
#include "video_core/renderer_opengl/gl_rasterizer.h"
#include "video_core/renderer_opengl/gl_shader_manager.h"
#include "video_core/renderer_opengl/gl_shader_util.h"
@@ -268,12 +275,17 @@ void RendererOpenGL::InitOpenGLObjects() {
fxaa_vertex = CreateProgram(HostShaders::FXAA_VERT, GL_VERTEX_SHADER);
fxaa_fragment = CreateProgram(HostShaders::FXAA_FRAG, GL_FRAGMENT_SHADER);
- const auto SmaaShader = [](std::string_view specialized_source, GLenum stage) {
- std::string shader_source{specialized_source};
- constexpr std::string_view include_string = "#include \"opengl_smaa.glsl\"";
+ const auto replace_include = [](std::string& shader_source, std::string_view include_name,
+ std::string_view include_content) {
+ const std::string include_string = fmt::format("#include \"{}\"", include_name);
const std::size_t pos = shader_source.find(include_string);
ASSERT(pos != std::string::npos);
- shader_source.replace(pos, include_string.size(), HostShaders::OPENGL_SMAA_GLSL);
+ shader_source.replace(pos, include_string.size(), include_content);
+ };
+
+ const auto SmaaShader = [&](std::string_view specialized_source, GLenum stage) {
+ std::string shader_source{specialized_source};
+ replace_include(shader_source, "opengl_smaa.glsl", HostShaders::OPENGL_SMAA_GLSL);
return CreateProgram(shader_source, stage);
};
@@ -298,14 +310,32 @@ void RendererOpenGL::InitOpenGLObjects() {
CreateProgram(fmt::format("#version 460\n{}", HostShaders::OPENGL_PRESENT_SCALEFORCE_FRAG),
GL_FRAGMENT_SHADER);
+ std::string fsr_source{HostShaders::OPENGL_FIDELITYFX_FSR_FRAG};
+ replace_include(fsr_source, "ffx_a.h", HostShaders::FFX_A_H);
+ replace_include(fsr_source, "ffx_fsr1.h", HostShaders::FFX_FSR1_H);
+
+ std::string fsr_easu_frag_source{HostShaders::OPENGL_FIDELITYFX_FSR_EASU_FRAG};
+ std::string fsr_rcas_frag_source{HostShaders::OPENGL_FIDELITYFX_FSR_RCAS_FRAG};
+ replace_include(fsr_easu_frag_source, "opengl_fidelityfx_fsr.frag", fsr_source);
+ replace_include(fsr_rcas_frag_source, "opengl_fidelityfx_fsr.frag", fsr_source);
+
+ fsr = std::make_unique<FSR>(HostShaders::FULL_SCREEN_TRIANGLE_VERT, fsr_easu_frag_source,
+ fsr_rcas_frag_source);
+
// Generate presentation sampler
present_sampler.Create();
glSamplerParameteri(present_sampler.handle, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glSamplerParameteri(present_sampler.handle, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+ glSamplerParameteri(present_sampler.handle, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+ glSamplerParameteri(present_sampler.handle, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+ glSamplerParameteri(present_sampler.handle, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
present_sampler_nn.Create();
glSamplerParameteri(present_sampler_nn.handle, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glSamplerParameteri(present_sampler_nn.handle, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+ glSamplerParameteri(present_sampler_nn.handle, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+ glSamplerParameteri(present_sampler_nn.handle, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+ glSamplerParameteri(present_sampler_nn.handle, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
// Generate VBO handle for drawing
vertex_buffer.Create();
@@ -525,6 +555,31 @@ void RendererOpenGL::DrawScreen(const Layout::FramebufferLayout& layout) {
glBindTextureUnit(0, aa_texture.handle);
}
+ glDisablei(GL_SCISSOR_TEST, 0);
+
+ if (Settings::values.scaling_filter.GetValue() == Settings::ScalingFilter::Fsr) {
+ if (!fsr->AreBuffersInitialized()) {
+ fsr->InitBuffers();
+ }
+
+ auto crop_rect = framebuffer_crop_rect;
+ if (crop_rect.GetWidth() == 0) {
+ crop_rect.right = framebuffer_width;
+ }
+ if (crop_rect.GetHeight() == 0) {
+ crop_rect.bottom = framebuffer_height;
+ }
+ crop_rect = crop_rect.Scale(Settings::values.resolution_info.up_factor);
+ const auto fsr_input_width = Settings::values.resolution_info.ScaleUp(framebuffer_width);
+ const auto fsr_input_height = Settings::values.resolution_info.ScaleUp(framebuffer_height);
+ glBindSampler(0, present_sampler.handle);
+ fsr->Draw(program_manager, layout.screen, fsr_input_width, fsr_input_height, crop_rect);
+ } else {
+ if (fsr->AreBuffersInitialized()) {
+ fsr->ReleaseBuffers();
+ }
+ }
+
const std::array ortho_matrix =
MakeOrthographicMatrix(static_cast<float>(layout.width), static_cast<float>(layout.height));
@@ -540,10 +595,7 @@ void RendererOpenGL::DrawScreen(const Layout::FramebufferLayout& layout) {
case Settings::ScalingFilter::ScaleForce:
return present_scaleforce_fragment.handle;
case Settings::ScalingFilter::Fsr:
- LOG_WARNING(
- Render_OpenGL,
- "FidelityFX Super Resolution is not supported in OpenGL, changing to ScaleForce");
- return present_scaleforce_fragment.handle;
+ return fsr->GetPresentFragmentProgram().handle;
default:
return present_bilinear_fragment.handle;
}
@@ -578,15 +630,18 @@ void RendererOpenGL::DrawScreen(const Layout::FramebufferLayout& layout) {
f32 scale_u = static_cast<f32>(framebuffer_width) / static_cast<f32>(screen_info.texture.width);
f32 scale_v =
static_cast<f32>(framebuffer_height) / static_cast<f32>(screen_info.texture.height);
- // Scale the output by the crop width/height. This is commonly used with 1280x720 rendering
- // (e.g. handheld mode) on a 1920x1080 framebuffer.
- if (framebuffer_crop_rect.GetWidth() > 0) {
- scale_u = static_cast<f32>(framebuffer_crop_rect.GetWidth()) /
- static_cast<f32>(screen_info.texture.width);
- }
- if (framebuffer_crop_rect.GetHeight() > 0) {
- scale_v = static_cast<f32>(framebuffer_crop_rect.GetHeight()) /
- static_cast<f32>(screen_info.texture.height);
+
+ if (Settings::values.scaling_filter.GetValue() != Settings::ScalingFilter::Fsr) {
+ // Scale the output by the crop width/height. This is commonly used with 1280x720 rendering
+ // (e.g. handheld mode) on a 1920x1080 framebuffer.
+ if (framebuffer_crop_rect.GetWidth() > 0) {
+ scale_u = static_cast<f32>(framebuffer_crop_rect.GetWidth()) /
+ static_cast<f32>(screen_info.texture.width);
+ }
+ if (framebuffer_crop_rect.GetHeight() > 0) {
+ scale_v = static_cast<f32>(framebuffer_crop_rect.GetHeight()) /
+ static_cast<f32>(screen_info.texture.height);
+ }
}
if (Settings::values.anti_aliasing.GetValue() == Settings::AntiAliasing::Fxaa &&
!screen_info.was_accelerated) {
@@ -612,7 +667,6 @@ void RendererOpenGL::DrawScreen(const Layout::FramebufferLayout& layout) {
} else {
glDisable(GL_FRAMEBUFFER_SRGB);
}
- glDisablei(GL_SCISSOR_TEST, 0);
glViewportIndexedf(0, 0.0f, 0.0f, static_cast<GLfloat>(layout.width),
static_cast<GLfloat>(layout.height));
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 @@
#include "video_core/renderer_base.h"
#include "video_core/renderer_opengl/gl_device.h"
+#include "video_core/renderer_opengl/gl_fsr.h"
#include "video_core/renderer_opengl/gl_rasterizer.h"
#include "video_core/renderer_opengl/gl_resource_manager.h"
#include "video_core/renderer_opengl/gl_shader_manager.h"
@@ -141,6 +142,8 @@ private:
OGLTexture smaa_edges_tex;
OGLTexture smaa_blend_tex;
+ std::unique_ptr<FSR> fsr;
+
/// OpenGL framebuffer data
std::vector<u8> gl_framebuffer_data;
diff --git a/src/video_core/renderer_opengl/util_shaders.cpp b/src/video_core/renderer_opengl/util_shaders.cpp
index 404def62e..2c7ac210b 100644
--- a/src/video_core/renderer_opengl/util_shaders.cpp
+++ b/src/video_core/renderer_opengl/util_shaders.cpp
@@ -12,6 +12,8 @@
#include "video_core/host_shaders/astc_decoder_comp.h"
#include "video_core/host_shaders/block_linear_unswizzle_2d_comp.h"
#include "video_core/host_shaders/block_linear_unswizzle_3d_comp.h"
+#include "video_core/host_shaders/convert_msaa_to_non_msaa_comp.h"
+#include "video_core/host_shaders/convert_non_msaa_to_msaa_comp.h"
#include "video_core/host_shaders/opengl_convert_s8d24_comp.h"
#include "video_core/host_shaders/opengl_copy_bc4_comp.h"
#include "video_core/host_shaders/pitch_unswizzle_comp.h"
@@ -51,7 +53,9 @@ UtilShaders::UtilShaders(ProgramManager& program_manager_)
block_linear_unswizzle_3d_program(MakeProgram(BLOCK_LINEAR_UNSWIZZLE_3D_COMP)),
pitch_unswizzle_program(MakeProgram(PITCH_UNSWIZZLE_COMP)),
copy_bc4_program(MakeProgram(OPENGL_COPY_BC4_COMP)),
- convert_s8d24_program(MakeProgram(OPENGL_CONVERT_S8D24_COMP)) {
+ convert_s8d24_program(MakeProgram(OPENGL_CONVERT_S8D24_COMP)),
+ convert_ms_to_nonms_program(MakeProgram(CONVERT_MSAA_TO_NON_MSAA_COMP)),
+ convert_nonms_to_ms_program(MakeProgram(CONVERT_NON_MSAA_TO_MSAA_COMP)) {
const auto swizzle_table = Tegra::Texture::MakeSwizzleTable();
swizzle_table_buffer.Create();
glNamedBufferStorage(swizzle_table_buffer.handle, sizeof(swizzle_table), &swizzle_table, 0);
@@ -269,6 +273,33 @@ void UtilShaders::ConvertS8D24(Image& dst_image, std::span<const ImageCopy> copi
program_manager.RestoreGuestCompute();
}
+void UtilShaders::CopyMSAA(Image& dst_image, Image& src_image,
+ std::span<const VideoCommon::ImageCopy> copies) {
+ const bool is_ms_to_non_ms = src_image.info.num_samples > 1 && dst_image.info.num_samples == 1;
+ const auto program_handle =
+ is_ms_to_non_ms ? convert_ms_to_nonms_program.handle : convert_nonms_to_ms_program.handle;
+ program_manager.BindComputeProgram(program_handle);
+
+ for (const ImageCopy& copy : copies) {
+ ASSERT(copy.src_subresource.base_layer == 0);
+ ASSERT(copy.src_subresource.num_layers == 1);
+ ASSERT(copy.dst_subresource.base_layer == 0);
+ ASSERT(copy.dst_subresource.num_layers == 1);
+
+ glBindImageTexture(0, src_image.StorageHandle(), copy.src_subresource.base_level, GL_TRUE,
+ 0, GL_READ_ONLY, GL_RGBA8);
+ glBindImageTexture(1, dst_image.StorageHandle(), copy.dst_subresource.base_level, GL_TRUE,
+ 0, GL_WRITE_ONLY, GL_RGBA8);
+
+ const u32 num_dispatches_x = Common::DivCeil(copy.extent.width, 8U);
+ const u32 num_dispatches_y = Common::DivCeil(copy.extent.height, 8U);
+ const u32 num_dispatches_z = copy.extent.depth;
+
+ glDispatchCompute(num_dispatches_x, num_dispatches_y, num_dispatches_z);
+ }
+ program_manager.RestoreGuestCompute();
+}
+
GLenum StoreFormat(u32 bytes_per_block) {
switch (bytes_per_block) {
case 1:
diff --git a/src/video_core/renderer_opengl/util_shaders.h b/src/video_core/renderer_opengl/util_shaders.h
index 44efb6ecf..9013808e7 100644
--- a/src/video_core/renderer_opengl/util_shaders.h
+++ b/src/video_core/renderer_opengl/util_shaders.h
@@ -40,6 +40,9 @@ public:
void ConvertS8D24(Image& dst_image, std::span<const VideoCommon::ImageCopy> copies);
+ void CopyMSAA(Image& dst_image, Image& src_image,
+ std::span<const VideoCommon::ImageCopy> copies);
+
private:
ProgramManager& program_manager;
@@ -51,6 +54,8 @@ private:
OGLProgram pitch_unswizzle_program;
OGLProgram copy_bc4_program;
OGLProgram convert_s8d24_program;
+ OGLProgram convert_ms_to_nonms_program;
+ OGLProgram convert_nonms_to_ms_program;
};
GLenum StoreFormat(u32 bytes_per_block);
diff --git a/src/video_core/renderer_vulkan/blit_image.cpp b/src/video_core/renderer_vulkan/blit_image.cpp
index 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 @@
#include <algorithm>
#include "common/settings.h"
+#include "video_core/host_shaders/blit_color_float_frag_spv.h"
#include "video_core/host_shaders/convert_abgr8_to_d24s8_frag_spv.h"
#include "video_core/host_shaders/convert_d24s8_to_abgr8_frag_spv.h"
#include "video_core/host_shaders/convert_depth_to_float_frag_spv.h"
#include "video_core/host_shaders/convert_float_to_depth_frag_spv.h"
#include "video_core/host_shaders/convert_s8d24_to_abgr8_frag_spv.h"
#include "video_core/host_shaders/full_screen_triangle_vert_spv.h"
-#include "video_core/host_shaders/vulkan_blit_color_float_frag_spv.h"
#include "video_core/host_shaders/vulkan_blit_depth_stencil_frag_spv.h"
+#include "video_core/host_shaders/vulkan_color_clear_frag_spv.h"
+#include "video_core/host_shaders/vulkan_color_clear_vert_spv.h"
#include "video_core/renderer_vulkan/blit_image.h"
#include "video_core/renderer_vulkan/maxwell_to_vk.h"
#include "video_core/renderer_vulkan/vk_scheduler.h"
@@ -69,10 +71,11 @@ constexpr VkDescriptorSetLayoutCreateInfo TWO_TEXTURES_DESCRIPTOR_SET_LAYOUT_CRE
.bindingCount = static_cast<u32>(TWO_TEXTURES_DESCRIPTOR_SET_LAYOUT_BINDINGS.size()),
.pBindings = TWO_TEXTURES_DESCRIPTOR_SET_LAYOUT_BINDINGS.data(),
};
-constexpr VkPushConstantRange PUSH_CONSTANT_RANGE{
- .stageFlags = VK_SHADER_STAGE_VERTEX_BIT,
+template <VkShaderStageFlags stageFlags, size_t size>
+inline constexpr VkPushConstantRange PUSH_CONSTANT_RANGE{
+ .stageFlags = stageFlags,
.offset = 0,
- .size = sizeof(PushConstants),
+ .size = static_cast<u32>(size),
};
constexpr VkPipelineVertexInputStateCreateInfo PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO{
.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO,
@@ -125,10 +128,8 @@ constexpr VkPipelineMultisampleStateCreateInfo PIPELINE_MULTISAMPLE_STATE_CREATE
.alphaToCoverageEnable = VK_FALSE,
.alphaToOneEnable = VK_FALSE,
};
-constexpr std::array DYNAMIC_STATES{
- VK_DYNAMIC_STATE_VIEWPORT,
- VK_DYNAMIC_STATE_SCISSOR,
-};
+constexpr std::array DYNAMIC_STATES{VK_DYNAMIC_STATE_VIEWPORT, VK_DYNAMIC_STATE_SCISSOR,
+ VK_DYNAMIC_STATE_BLEND_CONSTANTS};
constexpr VkPipelineDynamicStateCreateInfo PIPELINE_DYNAMIC_STATE_CREATE_INFO{
.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO,
.pNext = nullptr,
@@ -205,15 +206,15 @@ inline constexpr VkSamplerCreateInfo SAMPLER_CREATE_INFO{
};
constexpr VkPipelineLayoutCreateInfo PipelineLayoutCreateInfo(
- const VkDescriptorSetLayout* set_layout) {
+ const VkDescriptorSetLayout* set_layout, vk::Span<VkPushConstantRange> push_constants) {
return VkPipelineLayoutCreateInfo{
.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO,
.pNext = nullptr,
.flags = 0,
- .setLayoutCount = 1,
+ .setLayoutCount = (set_layout != nullptr ? 1u : 0u),
.pSetLayouts = set_layout,
- .pushConstantRangeCount = 1,
- .pPushConstantRanges = &PUSH_CONSTANT_RANGE,
+ .pushConstantRangeCount = push_constants.size(),
+ .pPushConstantRanges = push_constants.data(),
};
}
@@ -302,8 +303,7 @@ void UpdateTwoTexturesDescriptorSet(const Device& device, VkDescriptorSet descri
device.GetLogical().UpdateDescriptorSets(write_descriptor_sets, nullptr);
}
-void BindBlitState(vk::CommandBuffer cmdbuf, VkPipelineLayout layout, const Region2D& dst_region,
- const Region2D& src_region) {
+void BindBlitState(vk::CommandBuffer cmdbuf, const Region2D& dst_region) {
const VkOffset2D offset{
.x = std::min(dst_region.start.x, dst_region.end.x),
.y = std::min(dst_region.start.y, dst_region.end.y),
@@ -325,15 +325,23 @@ void BindBlitState(vk::CommandBuffer cmdbuf, VkPipelineLayout layout, const Regi
.offset = offset,
.extent = extent,
};
- const float scale_x = static_cast<float>(src_region.end.x - src_region.start.x);
- const float scale_y = static_cast<float>(src_region.end.y - src_region.start.y);
+ cmdbuf.SetViewport(0, viewport);
+ cmdbuf.SetScissor(0, scissor);
+}
+
+void BindBlitState(vk::CommandBuffer cmdbuf, VkPipelineLayout layout, const Region2D& dst_region,
+ const Region2D& src_region, const Extent3D& src_size = {1, 1, 1}) {
+ BindBlitState(cmdbuf, dst_region);
+ const float scale_x = static_cast<float>(src_region.end.x - src_region.start.x) /
+ static_cast<float>(src_size.width);
+ const float scale_y = static_cast<float>(src_region.end.y - src_region.start.y) /
+ static_cast<float>(src_size.height);
const PushConstants push_constants{
.tex_scale = {scale_x, scale_y},
- .tex_offset = {static_cast<float>(src_region.start.x),
- static_cast<float>(src_region.start.y)},
+ .tex_offset = {static_cast<float>(src_region.start.x) / static_cast<float>(src_size.width),
+ static_cast<float>(src_region.start.y) /
+ static_cast<float>(src_size.height)},
};
- cmdbuf.SetViewport(0, viewport);
- cmdbuf.SetScissor(0, scissor);
cmdbuf.PushConstants(layout, VK_SHADER_STAGE_VERTEX_BIT, push_constants);
}
@@ -347,6 +355,51 @@ VkExtent2D GetConversionExtent(const ImageView& src_image_view) {
.height = is_rescaled ? resolution.ScaleUp(height) : height,
};
}
+
+void TransitionImageLayout(vk::CommandBuffer& cmdbuf, VkImage image, VkImageLayout target_layout,
+ VkImageLayout source_layout = VK_IMAGE_LAYOUT_GENERAL) {
+ constexpr VkFlags flags{VK_ACCESS_COLOR_ATTACHMENT_READ_BIT |
+ VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT | VK_ACCESS_SHADER_READ_BIT};
+ const VkImageMemoryBarrier barrier{
+ .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
+ .pNext = nullptr,
+ .srcAccessMask = flags,
+ .dstAccessMask = flags,
+ .oldLayout = source_layout,
+ .newLayout = target_layout,
+ .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
+ .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
+ .image = image,
+ .subresourceRange{
+ .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
+ .baseMipLevel = 0,
+ .levelCount = 1,
+ .baseArrayLayer = 0,
+ .layerCount = 1,
+ },
+ };
+ cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT,
+ 0, barrier);
+}
+
+void BeginRenderPass(vk::CommandBuffer& cmdbuf, const Framebuffer* framebuffer) {
+ const VkRenderPass render_pass = framebuffer->RenderPass();
+ const VkFramebuffer framebuffer_handle = framebuffer->Handle();
+ const VkExtent2D render_area = framebuffer->RenderArea();
+ const VkRenderPassBeginInfo renderpass_bi{
+ .sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO,
+ .pNext = nullptr,
+ .renderPass = render_pass,
+ .framebuffer = framebuffer_handle,
+ .renderArea{
+ .offset{},
+ .extent = render_area,
+ },
+ .clearValueCount = 0,
+ .pClearValues = nullptr,
+ };
+ cmdbuf.BeginRenderPass(renderpass_bi, VK_SUBPASS_CONTENTS_INLINE);
+}
} // Anonymous namespace
BlitImageHelper::BlitImageHelper(const Device& device_, Scheduler& scheduler_,
@@ -360,13 +413,20 @@ BlitImageHelper::BlitImageHelper(const Device& device_, Scheduler& scheduler_,
descriptor_pool.Allocator(*one_texture_set_layout, TEXTURE_DESCRIPTOR_BANK_INFO<1>)},
two_textures_descriptor_allocator{
descriptor_pool.Allocator(*two_textures_set_layout, TEXTURE_DESCRIPTOR_BANK_INFO<2>)},
- one_texture_pipeline_layout(device.GetLogical().CreatePipelineLayout(
- PipelineLayoutCreateInfo(one_texture_set_layout.address()))),
- two_textures_pipeline_layout(device.GetLogical().CreatePipelineLayout(
- PipelineLayoutCreateInfo(two_textures_set_layout.address()))),
+ one_texture_pipeline_layout(device.GetLogical().CreatePipelineLayout(PipelineLayoutCreateInfo(
+ one_texture_set_layout.address(),
+ PUSH_CONSTANT_RANGE<VK_SHADER_STAGE_VERTEX_BIT, sizeof(PushConstants)>))),
+ two_textures_pipeline_layout(
+ device.GetLogical().CreatePipelineLayout(PipelineLayoutCreateInfo(
+ two_textures_set_layout.address(),
+ PUSH_CONSTANT_RANGE<VK_SHADER_STAGE_VERTEX_BIT, sizeof(PushConstants)>))),
+ clear_color_pipeline_layout(device.GetLogical().CreatePipelineLayout(PipelineLayoutCreateInfo(
+ nullptr, PUSH_CONSTANT_RANGE<VK_SHADER_STAGE_FRAGMENT_BIT, sizeof(float) * 4>))),
full_screen_vert(BuildShader(device, FULL_SCREEN_TRIANGLE_VERT_SPV)),
- blit_color_to_color_frag(BuildShader(device, VULKAN_BLIT_COLOR_FLOAT_FRAG_SPV)),
+ blit_color_to_color_frag(BuildShader(device, BLIT_COLOR_FLOAT_FRAG_SPV)),
blit_depth_stencil_frag(BuildShader(device, VULKAN_BLIT_DEPTH_STENCIL_FRAG_SPV)),
+ clear_color_vert(BuildShader(device, VULKAN_COLOR_CLEAR_VERT_SPV)),
+ clear_color_frag(BuildShader(device, VULKAN_COLOR_CLEAR_FRAG_SPV)),
convert_depth_to_float_frag(BuildShader(device, CONVERT_DEPTH_TO_FLOAT_FRAG_SPV)),
convert_float_to_depth_frag(BuildShader(device, CONVERT_FLOAT_TO_DEPTH_FRAG_SPV)),
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
scheduler.InvalidateState();
}
+void BlitImageHelper::BlitColor(const Framebuffer* dst_framebuffer, VkImageView src_image_view,
+ VkImage src_image, VkSampler src_sampler,
+ const Region2D& dst_region, const Region2D& src_region,
+ const Extent3D& src_size) {
+ const BlitImagePipelineKey key{
+ .renderpass = dst_framebuffer->RenderPass(),
+ .operation = Tegra::Engines::Fermi2D::Operation::SrcCopy,
+ };
+ const VkPipelineLayout layout = *one_texture_pipeline_layout;
+ const VkPipeline pipeline = FindOrEmplaceColorPipeline(key);
+ scheduler.RequestOutsideRenderPassOperationContext();
+ scheduler.Record([this, dst_framebuffer, src_image_view, src_image, src_sampler, dst_region,
+ src_region, src_size, pipeline, layout](vk::CommandBuffer cmdbuf) {
+ TransitionImageLayout(cmdbuf, src_image, VK_IMAGE_LAYOUT_READ_ONLY_OPTIMAL);
+ BeginRenderPass(cmdbuf, dst_framebuffer);
+ const VkDescriptorSet descriptor_set = one_texture_descriptor_allocator.Commit();
+ UpdateOneTextureDescriptorSet(device, descriptor_set, src_sampler, src_image_view);
+ cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline);
+ cmdbuf.BindDescriptorSets(VK_PIPELINE_BIND_POINT_GRAPHICS, layout, 0, descriptor_set,
+ nullptr);
+ BindBlitState(cmdbuf, layout, dst_region, src_region, src_size);
+ cmdbuf.Draw(3, 1, 0, 0);
+ cmdbuf.EndRenderPass();
+ });
+}
+
void BlitImageHelper::BlitDepthStencil(const Framebuffer* dst_framebuffer,
VkImageView src_depth_view, VkImageView src_stencil_view,
const Region2D& dst_region, const Region2D& src_region,
@@ -479,6 +565,30 @@ void BlitImageHelper::ConvertS8D24ToABGR8(const Framebuffer* dst_framebuffer,
ConvertDepthStencil(*convert_s8d24_to_abgr8_pipeline, dst_framebuffer, src_image_view);
}
+void BlitImageHelper::ClearColor(const Framebuffer* dst_framebuffer, u8 color_mask,
+ const std::array<f32, 4>& clear_color,
+ const Region2D& dst_region) {
+ const BlitImagePipelineKey key{
+ .renderpass = dst_framebuffer->RenderPass(),
+ .operation = Tegra::Engines::Fermi2D::Operation::BlendPremult,
+ };
+ const VkPipeline pipeline = FindOrEmplaceClearColorPipeline(key);
+ const VkPipelineLayout layout = *clear_color_pipeline_layout;
+ scheduler.RequestRenderpass(dst_framebuffer);
+ scheduler.Record(
+ [pipeline, layout, color_mask, clear_color, dst_region](vk::CommandBuffer cmdbuf) {
+ cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline);
+ const std::array blend_color = {
+ (color_mask & 0x1) ? 1.0f : 0.0f, (color_mask & 0x2) ? 1.0f : 0.0f,
+ (color_mask & 0x4) ? 1.0f : 0.0f, (color_mask & 0x8) ? 1.0f : 0.0f};
+ cmdbuf.SetBlendConstants(blend_color.data());
+ BindBlitState(cmdbuf, dst_region);
+ cmdbuf.PushConstants(layout, VK_SHADER_STAGE_FRAGMENT_BIT, clear_color);
+ cmdbuf.Draw(3, 1, 0, 0);
+ });
+ scheduler.InvalidateState();
+}
+
void BlitImageHelper::Convert(VkPipeline pipeline, const Framebuffer* dst_framebuffer,
const ImageView& src_image_view) {
const VkPipelineLayout layout = *one_texture_pipeline_layout;
@@ -654,6 +764,58 @@ VkPipeline BlitImageHelper::FindOrEmplaceDepthStencilPipeline(const BlitImagePip
return *blit_depth_stencil_pipelines.back();
}
+VkPipeline BlitImageHelper::FindOrEmplaceClearColorPipeline(const BlitImagePipelineKey& key) {
+ const auto it = std::ranges::find(clear_color_keys, key);
+ if (it != clear_color_keys.end()) {
+ return *clear_color_pipelines[std::distance(clear_color_keys.begin(), it)];
+ }
+ clear_color_keys.push_back(key);
+ const std::array stages = MakeStages(*clear_color_vert, *clear_color_frag);
+ const VkPipelineColorBlendAttachmentState color_blend_attachment_state{
+ .blendEnable = VK_TRUE,
+ .srcColorBlendFactor = VK_BLEND_FACTOR_CONSTANT_COLOR,
+ .dstColorBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_CONSTANT_COLOR,
+ .colorBlendOp = VK_BLEND_OP_ADD,
+ .srcAlphaBlendFactor = VK_BLEND_FACTOR_CONSTANT_ALPHA,
+ .dstAlphaBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_CONSTANT_ALPHA,
+ .alphaBlendOp = VK_BLEND_OP_ADD,
+ .colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT |
+ VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT,
+ };
+ const VkPipelineColorBlendStateCreateInfo color_blend_state_generic_create_info{
+ .sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO,
+ .pNext = nullptr,
+ .flags = 0,
+ .logicOpEnable = VK_FALSE,
+ .logicOp = VK_LOGIC_OP_CLEAR,
+ .attachmentCount = 1,
+ .pAttachments = &color_blend_attachment_state,
+ .blendConstants = {0.0f, 0.0f, 0.0f, 0.0f},
+ };
+ clear_color_pipelines.push_back(device.GetLogical().CreateGraphicsPipeline({
+ .sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO,
+ .pNext = nullptr,
+ .flags = 0,
+ .stageCount = static_cast<u32>(stages.size()),
+ .pStages = stages.data(),
+ .pVertexInputState = &PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO,
+ .pInputAssemblyState = &PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO,
+ .pTessellationState = nullptr,
+ .pViewportState = &PIPELINE_VIEWPORT_STATE_CREATE_INFO,
+ .pRasterizationState = &PIPELINE_RASTERIZATION_STATE_CREATE_INFO,
+ .pMultisampleState = &PIPELINE_MULTISAMPLE_STATE_CREATE_INFO,
+ .pDepthStencilState = &PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO,
+ .pColorBlendState = &color_blend_state_generic_create_info,
+ .pDynamicState = &PIPELINE_DYNAMIC_STATE_CREATE_INFO,
+ .layout = *clear_color_pipeline_layout,
+ .renderPass = key.renderpass,
+ .subpass = 0,
+ .basePipelineHandle = VK_NULL_HANDLE,
+ .basePipelineIndex = 0,
+ }));
+ return *clear_color_pipelines.back();
+}
+
void BlitImageHelper::ConvertPipeline(vk::Pipeline& pipeline, VkRenderPass renderpass,
bool is_target_depth) {
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 @@
namespace Vulkan {
+using VideoCommon::Extent3D;
+using VideoCommon::Offset2D;
using VideoCommon::Region2D;
class Device;
@@ -36,6 +38,10 @@ public:
Tegra::Engines::Fermi2D::Filter filter,
Tegra::Engines::Fermi2D::Operation operation);
+ void BlitColor(const Framebuffer* dst_framebuffer, VkImageView src_image_view,
+ VkImage src_image, VkSampler src_sampler, const Region2D& dst_region,
+ const Region2D& src_region, const Extent3D& src_size);
+
void BlitDepthStencil(const Framebuffer* dst_framebuffer, VkImageView src_depth_view,
VkImageView src_stencil_view, const Region2D& dst_region,
const Region2D& src_region, Tegra::Engines::Fermi2D::Filter filter,
@@ -55,6 +61,9 @@ public:
void ConvertS8D24ToABGR8(const Framebuffer* dst_framebuffer, ImageView& src_image_view);
+ void ClearColor(const Framebuffer* dst_framebuffer, u8 color_mask,
+ const std::array<f32, 4>& clear_color, const Region2D& dst_region);
+
private:
void Convert(VkPipeline pipeline, const Framebuffer* dst_framebuffer,
const ImageView& src_image_view);
@@ -66,6 +75,8 @@ private:
[[nodiscard]] VkPipeline FindOrEmplaceDepthStencilPipeline(const BlitImagePipelineKey& key);
+ [[nodiscard]] VkPipeline FindOrEmplaceClearColorPipeline(const BlitImagePipelineKey& key);
+
void ConvertPipeline(vk::Pipeline& pipeline, VkRenderPass renderpass, bool is_target_depth);
void ConvertDepthToColorPipeline(vk::Pipeline& pipeline, VkRenderPass renderpass);
@@ -91,9 +102,12 @@ private:
DescriptorAllocator two_textures_descriptor_allocator;
vk::PipelineLayout one_texture_pipeline_layout;
vk::PipelineLayout two_textures_pipeline_layout;
+ vk::PipelineLayout clear_color_pipeline_layout;
vk::ShaderModule full_screen_vert;
vk::ShaderModule blit_color_to_color_frag;
vk::ShaderModule blit_depth_stencil_frag;
+ vk::ShaderModule clear_color_vert;
+ vk::ShaderModule clear_color_frag;
vk::ShaderModule convert_depth_to_float_frag;
vk::ShaderModule convert_float_to_depth_frag;
vk::ShaderModule convert_abgr8_to_d24s8_frag;
@@ -106,6 +120,8 @@ private:
std::vector<vk::Pipeline> blit_color_pipelines;
std::vector<BlitImagePipelineKey> blit_depth_stencil_keys;
std::vector<vk::Pipeline> blit_depth_stencil_pipelines;
+ std::vector<BlitImagePipelineKey> clear_color_keys;
+ std::vector<vk::Pipeline> clear_color_pipelines;
vk::Pipeline convert_d32_to_r32_pipeline;
vk::Pipeline convert_r32_to_d32_pipeline;
vk::Pipeline convert_d16_to_r16_pipeline;
diff --git a/src/video_core/renderer_vulkan/renderer_vulkan.cpp b/src/video_core/renderer_vulkan/renderer_vulkan.cpp
index 52855120c..2a8d9e377 100644
--- a/src/video_core/renderer_vulkan/renderer_vulkan.cpp
+++ b/src/video_core/renderer_vulkan/renderer_vulkan.cpp
@@ -60,22 +60,9 @@ std::string GetDriverVersion(const Device& device) {
return GetReadableVersion(version);
}
-std::string BuildCommaSeparatedExtensions(std::vector<std::string> available_extensions) {
- std::sort(std::begin(available_extensions), std::end(available_extensions));
-
- static constexpr std::size_t AverageExtensionSize = 64;
- std::string separated_extensions;
- separated_extensions.reserve(available_extensions.size() * AverageExtensionSize);
-
- const auto end = std::end(available_extensions);
- for (auto extension = std::begin(available_extensions); extension != end; ++extension) {
- if (const bool is_last = extension + 1 == end; is_last) {
- separated_extensions += *extension;
- } else {
- separated_extensions += fmt::format("{},", *extension);
- }
- }
- return separated_extensions;
+std::string BuildCommaSeparatedExtensions(
+ const std::set<std::string, std::less<>>& available_extensions) {
+ return fmt::format("{}", fmt::join(available_extensions, ","));
}
} // Anonymous namespace
@@ -112,6 +99,7 @@ RendererVulkan::RendererVulkan(Core::TelemetrySession& telemetry_session_,
state_tracker, scheduler) {
if (Settings::values.renderer_force_max_clock.GetValue() && device.ShouldBoostClocks()) {
turbo_mode.emplace(instance, dld);
+ scheduler.RegisterOnSubmit([this] { turbo_mode->QueueSubmitted(); });
}
Report();
} catch (const vk::Exception& exception) {
@@ -120,6 +108,7 @@ RendererVulkan::RendererVulkan(Core::TelemetrySession& telemetry_session_,
}
RendererVulkan::~RendererVulkan() {
+ scheduler.RegisterOnSubmit([] {});
void(device.GetLogical().WaitIdle());
}
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 @@
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
-#include <cmath>
-#include "common/bit_cast.h"
#include "common/common_types.h"
#include "common/div_ceil.h"
#include "common/settings.h"
+#include "video_core/fsr.h"
#include "video_core/host_shaders/vulkan_fidelityfx_fsr_easu_fp16_comp_spv.h"
#include "video_core/host_shaders/vulkan_fidelityfx_fsr_easu_fp32_comp_spv.h"
#include "video_core/host_shaders/vulkan_fidelityfx_fsr_rcas_fp16_comp_spv.h"
@@ -17,146 +16,7 @@
#include "video_core/vulkan_common/vulkan_device.h"
namespace Vulkan {
-namespace {
-// Reimplementations of the constant generating functions in ffx_fsr1.h
-// GCC generated a lot of warnings when using the official header.
-u32 AU1_AH1_AF1(f32 f) {
- static constexpr u32 base[512]{
- 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
- 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
- 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
- 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
- 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
- 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
- 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
- 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
- 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
- 0x0000, 0x0000, 0x0000, 0x0000, 0x0001, 0x0002, 0x0004, 0x0008, 0x0010, 0x0020, 0x0040,
- 0x0080, 0x0100, 0x0200, 0x0400, 0x0800, 0x0c00, 0x1000, 0x1400, 0x1800, 0x1c00, 0x2000,
- 0x2400, 0x2800, 0x2c00, 0x3000, 0x3400, 0x3800, 0x3c00, 0x4000, 0x4400, 0x4800, 0x4c00,
- 0x5000, 0x5400, 0x5800, 0x5c00, 0x6000, 0x6400, 0x6800, 0x6c00, 0x7000, 0x7400, 0x7800,
- 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff,
- 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff,
- 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff,
- 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff,
- 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff,
- 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff,
- 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff,
- 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff,
- 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff,
- 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff, 0x7bff,
- 0x7bff, 0x7bff, 0x7bff, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000,
- 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000,
- 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000,
- 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000,
- 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000,
- 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000,
- 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000,
- 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000,
- 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000,
- 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8001, 0x8002, 0x8004, 0x8008,
- 0x8010, 0x8020, 0x8040, 0x8080, 0x8100, 0x8200, 0x8400, 0x8800, 0x8c00, 0x9000, 0x9400,
- 0x9800, 0x9c00, 0xa000, 0xa400, 0xa800, 0xac00, 0xb000, 0xb400, 0xb800, 0xbc00, 0xc000,
- 0xc400, 0xc800, 0xcc00, 0xd000, 0xd400, 0xd800, 0xdc00, 0xe000, 0xe400, 0xe800, 0xec00,
- 0xf000, 0xf400, 0xf800, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff,
- 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff,
- 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff,
- 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff,
- 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff,
- 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff,
- 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff,
- 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff,
- 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff,
- 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff,
- 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff, 0xfbff,
- };
- static constexpr s8 shift[512]{
- 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
- 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
- 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
- 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
- 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
- 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
- 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x17, 0x16,
- 0x15, 0x14, 0x13, 0x12, 0x11, 0x10, 0x0f, 0x0e, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d,
- 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d,
- 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
- 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
- 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
- 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
- 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
- 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
- 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
- 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
- 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
- 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
- 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
- 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
- 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
- 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
- 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x17,
- 0x16, 0x15, 0x14, 0x13, 0x12, 0x11, 0x10, 0x0f, 0x0e, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d,
- 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d,
- 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
- 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
- 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
- 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
- 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
- 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
- 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
- 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
- 0x18, 0x18,
- };
- const u32 u = Common::BitCast<u32>(f);
- const u32 i = u >> 23;
- return base[i] + ((u & 0x7fffff) >> shift[i]);
-}
-
-u32 AU1_AH2_AF2(f32 a[2]) {
- return AU1_AH1_AF1(a[0]) + (AU1_AH1_AF1(a[1]) << 16);
-}
-
-void FsrEasuCon(u32 con0[4], u32 con1[4], u32 con2[4], u32 con3[4], f32 inputViewportInPixelsX,
- f32 inputViewportInPixelsY, f32 inputSizeInPixelsX, f32 inputSizeInPixelsY,
- f32 outputSizeInPixelsX, f32 outputSizeInPixelsY) {
- con0[0] = Common::BitCast<u32>(inputViewportInPixelsX / outputSizeInPixelsX);
- con0[1] = Common::BitCast<u32>(inputViewportInPixelsY / outputSizeInPixelsY);
- con0[2] = Common::BitCast<u32>(0.5f * inputViewportInPixelsX / outputSizeInPixelsX - 0.5f);
- con0[3] = Common::BitCast<u32>(0.5f * inputViewportInPixelsY / outputSizeInPixelsY - 0.5f);
- con1[0] = Common::BitCast<u32>(1.0f / inputSizeInPixelsX);
- con1[1] = Common::BitCast<u32>(1.0f / inputSizeInPixelsY);
- con1[2] = Common::BitCast<u32>(1.0f / inputSizeInPixelsX);
- con1[3] = Common::BitCast<u32>(-1.0f / inputSizeInPixelsY);
- con2[0] = Common::BitCast<u32>(-1.0f / inputSizeInPixelsX);
- con2[1] = Common::BitCast<u32>(2.0f / inputSizeInPixelsY);
- con2[2] = Common::BitCast<u32>(1.0f / inputSizeInPixelsX);
- con2[3] = Common::BitCast<u32>(2.0f / inputSizeInPixelsY);
- con3[0] = Common::BitCast<u32>(0.0f / inputSizeInPixelsX);
- con3[1] = Common::BitCast<u32>(4.0f / inputSizeInPixelsY);
- con3[2] = con3[3] = 0;
-}
-
-void FsrEasuConOffset(u32 con0[4], u32 con1[4], u32 con2[4], u32 con3[4],
- f32 inputViewportInPixelsX, f32 inputViewportInPixelsY,
- f32 inputSizeInPixelsX, f32 inputSizeInPixelsY, f32 outputSizeInPixelsX,
- f32 outputSizeInPixelsY, f32 inputOffsetInPixelsX, f32 inputOffsetInPixelsY) {
- FsrEasuCon(con0, con1, con2, con3, inputViewportInPixelsX, inputViewportInPixelsY,
- inputSizeInPixelsX, inputSizeInPixelsY, outputSizeInPixelsX, outputSizeInPixelsY);
- con0[2] = Common::BitCast<u32>(0.5f * inputViewportInPixelsX / outputSizeInPixelsX - 0.5f +
- inputOffsetInPixelsX);
- con0[3] = Common::BitCast<u32>(0.5f * inputViewportInPixelsY / outputSizeInPixelsY - 0.5f +
- inputOffsetInPixelsY);
-}
-
-void FsrRcasCon(u32* con, f32 sharpness) {
- sharpness = std::exp2f(-sharpness);
- f32 hSharp[2]{sharpness, sharpness};
- con[0] = Common::BitCast<u32>(sharpness);
- con[1] = AU1_AH2_AF2(hSharp);
- con[2] = 0;
- con[3] = 0;
-}
-} // Anonymous namespace
+using namespace FSR;
FSR::FSR(const Device& device_, MemoryAllocator& memory_allocator_, size_t image_count_,
VkExtent2D output_size_)
diff --git a/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp b/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp
index f91bb5a1d..baedc4424 100644
--- a/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp
+++ b/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp
@@ -548,31 +548,7 @@ void GraphicsPipeline::MakePipeline(VkRenderPass render_pass) {
static_vector<VkVertexInputBindingDescription, 32> vertex_bindings;
static_vector<VkVertexInputBindingDivisorDescriptionEXT, 32> vertex_binding_divisors;
static_vector<VkVertexInputAttributeDescription, 32> vertex_attributes;
- if (key.state.dynamic_vertex_input) {
- const size_t num_vertex_arrays = std::min(
- key.state.attributes.size(), static_cast<size_t>(device.GetMaxVertexInputBindings()));
- for (size_t index = 0; index < num_vertex_arrays; ++index) {
- const u32 type = key.state.DynamicAttributeType(index);
- if (!stage_infos[0].loads.Generic(index) || type == 0) {
- continue;
- }
- vertex_attributes.push_back({
- .location = static_cast<u32>(index),
- .binding = 0,
- .format = type == 1 ? VK_FORMAT_R32_SFLOAT
- : type == 2 ? VK_FORMAT_R32_SINT
- : VK_FORMAT_R32_UINT,
- .offset = 0,
- });
- }
- if (!vertex_attributes.empty()) {
- vertex_bindings.push_back({
- .binding = 0,
- .stride = 4,
- .inputRate = VK_VERTEX_INPUT_RATE_VERTEX,
- });
- }
- } else {
+ if (!key.state.dynamic_vertex_input) {
const size_t num_vertex_arrays = std::min(
Maxwell::NumVertexArrays, static_cast<size_t>(device.GetMaxVertexInputBindings()));
for (size_t index = 0; index < num_vertex_arrays; ++index) {
diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.cpp b/src/video_core/renderer_vulkan/vk_rasterizer.cpp
index ed4a72166..719edbcfb 100644
--- a/src/video_core/renderer_vulkan/vk_rasterizer.cpp
+++ b/src/video_core/renderer_vulkan/vk_rasterizer.cpp
@@ -266,10 +266,40 @@ void RasterizerVulkan::DrawIndirect() {
buffer_cache.SetDrawIndirect(nullptr);
}
+void RasterizerVulkan::DrawTexture() {
+ MICROPROFILE_SCOPE(Vulkan_Drawing);
+
+ SCOPE_EXIT({ gpu.TickWork(); });
+ FlushWork();
+
+ query_cache.UpdateCounters();
+
+ texture_cache.SynchronizeGraphicsDescriptors();
+ texture_cache.UpdateRenderTargets(false);
+
+ UpdateDynamicStates();
+
+ const auto& draw_texture_state = maxwell3d->draw_manager->GetDrawTextureState();
+ const auto& sampler = texture_cache.GetGraphicsSampler(draw_texture_state.src_sampler);
+ const auto& texture = texture_cache.GetImageView(draw_texture_state.src_texture);
+ Region2D dst_region = {Offset2D{.x = static_cast<s32>(draw_texture_state.dst_x0),
+ .y = static_cast<s32>(draw_texture_state.dst_y0)},
+ Offset2D{.x = static_cast<s32>(draw_texture_state.dst_x1),
+ .y = static_cast<s32>(draw_texture_state.dst_y1)}};
+ Region2D src_region = {Offset2D{.x = static_cast<s32>(draw_texture_state.src_x0),
+ .y = static_cast<s32>(draw_texture_state.src_y0)},
+ Offset2D{.x = static_cast<s32>(draw_texture_state.src_x1),
+ .y = static_cast<s32>(draw_texture_state.src_y1)}};
+ blit_image.BlitColor(texture_cache.GetFramebuffer(), texture.RenderTarget(),
+ texture.ImageHandle(), sampler->Handle(), dst_region, src_region,
+ texture.size);
+}
+
void RasterizerVulkan::Clear(u32 layer_count) {
MICROPROFILE_SCOPE(Vulkan_Clearing);
FlushWork();
+ gpu_memory->FlushCaching();
query_cache.UpdateCounters();
@@ -364,7 +394,15 @@ void RasterizerVulkan::Clear(u32 layer_count) {
cmdbuf.ClearAttachments(attachment, clear_rect);
});
} else {
- UNIMPLEMENTED_MSG("Unimplemented Clear only the specified channel");
+ u8 color_mask = static_cast<u8>(regs.clear_surface.R | regs.clear_surface.G << 1 |
+ regs.clear_surface.B << 2 | regs.clear_surface.A << 3);
+ Region2D dst_region = {
+ Offset2D{.x = clear_rect.rect.offset.x, .y = clear_rect.rect.offset.y},
+ Offset2D{.x = clear_rect.rect.offset.x +
+ static_cast<s32>(clear_rect.rect.extent.width),
+ .y = clear_rect.rect.offset.y +
+ static_cast<s32>(clear_rect.rect.extent.height)}};
+ blit_image.ClearColor(framebuffer, color_mask, regs.clear_color, dst_region);
}
}
@@ -628,6 +666,7 @@ void RasterizerVulkan::TickFrame() {
}
bool RasterizerVulkan::AccelerateConditionalRendering() {
+ gpu_memory->FlushCaching();
if (Settings::IsGPULevelHigh()) {
// TODO(Blinkhawk): Reimplement Host conditional rendering.
return false;
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:
void Draw(bool is_indexed, u32 instance_count) override;
void DrawIndirect() override;
+ void DrawTexture() override;
void Clear(u32 layer_count) override;
void DispatchCompute() override;
void ResetCounter(VideoCore::QueryType type) override;
diff --git a/src/video_core/renderer_vulkan/vk_scheduler.cpp b/src/video_core/renderer_vulkan/vk_scheduler.cpp
index c2e53a5d5..e03685af1 100644
--- a/src/video_core/renderer_vulkan/vk_scheduler.cpp
+++ b/src/video_core/renderer_vulkan/vk_scheduler.cpp
@@ -213,6 +213,11 @@ void Scheduler::SubmitExecution(VkSemaphore signal_semaphore, VkSemaphore wait_s
.signalSemaphoreCount = num_signal_semaphores,
.pSignalSemaphores = signal_semaphores.data(),
};
+
+ if (on_submit) {
+ on_submit();
+ }
+
switch (const VkResult result = device.GetGraphicsQueue().Submit(submit_info)) {
case VK_SUCCESS:
break;
diff --git a/src/video_core/renderer_vulkan/vk_scheduler.h b/src/video_core/renderer_vulkan/vk_scheduler.h
index 3858c506c..bd4cb0f7e 100644
--- a/src/video_core/renderer_vulkan/vk_scheduler.h
+++ b/src/video_core/renderer_vulkan/vk_scheduler.h
@@ -5,6 +5,7 @@
#include <condition_variable>
#include <cstddef>
+#include <functional>
#include <memory>
#include <thread>
#include <utility>
@@ -66,6 +67,11 @@ public:
query_cache = &query_cache_;
}
+ // Registers a callback to perform on queue submission.
+ void RegisterOnSubmit(std::function<void()>&& func) {
+ on_submit = std::move(func);
+ }
+
/// Send work to a separate thread.
template <typename T>
void Record(T&& command) {
@@ -216,6 +222,7 @@ private:
vk::CommandBuffer current_cmdbuf;
std::unique_ptr<CommandChunk> chunk;
+ std::function<void()> on_submit;
State state;
diff --git a/src/video_core/renderer_vulkan/vk_smaa.cpp b/src/video_core/renderer_vulkan/vk_smaa.cpp
index 8eb735489..f8735189d 100644
--- a/src/video_core/renderer_vulkan/vk_smaa.cpp
+++ b/src/video_core/renderer_vulkan/vk_smaa.cpp
@@ -468,7 +468,7 @@ VkWriteDescriptorSet CreateWriteDescriptorSet(std::vector<VkDescriptorImageInfo>
}
void ClearColorImage(vk::CommandBuffer& cmdbuf, VkImage image) {
- constexpr std::array<VkImageSubresourceRange, 1> subresources{{{
+ static constexpr std::array<VkImageSubresourceRange, 1> subresources{{{
.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
.baseMipLevel = 0,
.levelCount = 1,
@@ -528,8 +528,8 @@ SMAA::SMAA(const Device& device, MemoryAllocator& allocator, size_t image_count,
}
void SMAA::CreateImages() {
- constexpr VkExtent2D area_extent{AREATEX_WIDTH, AREATEX_HEIGHT};
- constexpr VkExtent2D search_extent{SEARCHTEX_WIDTH, SEARCHTEX_HEIGHT};
+ static constexpr VkExtent2D area_extent{AREATEX_WIDTH, AREATEX_HEIGHT};
+ static constexpr VkExtent2D search_extent{SEARCHTEX_WIDTH, SEARCHTEX_HEIGHT};
std::tie(m_static_images[Area], m_static_buffer_commits[Area]) =
CreateWrappedImage(m_device, m_allocator, area_extent, VK_FORMAT_R8G8_UNORM);
@@ -586,12 +586,12 @@ void SMAA::CreateSampler() {
void SMAA::CreateShaders() {
// These match the order of the SMAAStage enum
- constexpr std::array vert_shader_sources{
+ static constexpr std::array vert_shader_sources{
ARRAY_TO_SPAN(SMAA_EDGE_DETECTION_VERT_SPV),
ARRAY_TO_SPAN(SMAA_BLENDING_WEIGHT_CALCULATION_VERT_SPV),
ARRAY_TO_SPAN(SMAA_NEIGHBORHOOD_BLENDING_VERT_SPV),
};
- constexpr std::array frag_shader_sources{
+ static constexpr std::array frag_shader_sources{
ARRAY_TO_SPAN(SMAA_EDGE_DETECTION_FRAG_SPV),
ARRAY_TO_SPAN(SMAA_BLENDING_WEIGHT_CALCULATION_FRAG_SPV),
ARRAY_TO_SPAN(SMAA_NEIGHBORHOOD_BLENDING_FRAG_SPV),
@@ -675,8 +675,8 @@ void SMAA::UploadImages(Scheduler& scheduler) {
return;
}
- constexpr VkExtent2D area_extent{AREATEX_WIDTH, AREATEX_HEIGHT};
- constexpr VkExtent2D search_extent{SEARCHTEX_WIDTH, SEARCHTEX_HEIGHT};
+ static constexpr VkExtent2D area_extent{AREATEX_WIDTH, AREATEX_HEIGHT};
+ static constexpr VkExtent2D search_extent{SEARCHTEX_WIDTH, SEARCHTEX_HEIGHT};
UploadImage(m_device, m_allocator, scheduler, m_static_images[Area], area_extent,
VK_FORMAT_R8G8_UNORM, ARRAY_TO_SPAN(areaTexBytes));
diff --git a/src/video_core/renderer_vulkan/vk_texture_cache.cpp b/src/video_core/renderer_vulkan/vk_texture_cache.cpp
index d39372ec4..9b85dfb5e 100644
--- a/src/video_core/renderer_vulkan/vk_texture_cache.cpp
+++ b/src/video_core/renderer_vulkan/vk_texture_cache.cpp
@@ -1230,6 +1230,11 @@ void TextureCacheRuntime::CopyImage(Image& dst, Image& src,
});
}
+void TextureCacheRuntime::CopyImageMSAA(Image& dst, Image& src,
+ std::span<const VideoCommon::ImageCopy> copies) {
+ UNIMPLEMENTED_MSG("Copying images with different samples is not implemented in Vulkan.");
+}
+
u64 TextureCacheRuntime::GetDeviceLocalMemory() const {
return device.GetDeviceLocalMemory();
}
diff --git a/src/video_core/renderer_vulkan/vk_texture_cache.h b/src/video_core/renderer_vulkan/vk_texture_cache.h
index 1f27a3589..0ce39616f 100644
--- a/src/video_core/renderer_vulkan/vk_texture_cache.h
+++ b/src/video_core/renderer_vulkan/vk_texture_cache.h
@@ -70,6 +70,8 @@ public:
void CopyImage(Image& dst, Image& src, std::span<const VideoCommon::ImageCopy> copies);
+ void CopyImageMSAA(Image& dst, Image& src, std::span<const VideoCommon::ImageCopy> copies);
+
bool ShouldReinterpret(Image& dst, Image& src);
void ReinterpretImage(Image& dst, Image& src, std::span<const VideoCommon::ImageCopy> copies);
@@ -80,6 +82,11 @@ public:
return false;
}
+ bool CanUploadMSAA() const noexcept {
+ // TODO: Implement buffer to MSAA uploads
+ return false;
+ }
+
void AccelerateImageUpload(Image&, const StagingBufferRef&,
std::span<const VideoCommon::SwizzleParameters>);
@@ -106,7 +113,7 @@ public:
std::optional<ASTCDecoderPass> astc_decoder_pass;
const Settings::ResolutionScalingInfo& resolution;
- constexpr static size_t indexing_slots = 8 * sizeof(size_t);
+ static constexpr size_t indexing_slots = 8 * sizeof(size_t);
std::array<vk::Buffer, indexing_slots> buffers{};
std::array<std::unique_ptr<MemoryCommit>, indexing_slots> buffer_commits{};
};
diff --git a/src/video_core/renderer_vulkan/vk_turbo_mode.cpp b/src/video_core/renderer_vulkan/vk_turbo_mode.cpp
index 852b86f84..db04943eb 100644
--- a/src/video_core/renderer_vulkan/vk_turbo_mode.cpp
+++ b/src/video_core/renderer_vulkan/vk_turbo_mode.cpp
@@ -14,11 +14,21 @@ using namespace Common::Literals;
TurboMode::TurboMode(const vk::Instance& instance, const vk::InstanceDispatch& dld)
: m_device{CreateDevice(instance, dld, VK_NULL_HANDLE)}, m_allocator{m_device, false} {
+ {
+ std::scoped_lock lk{m_submission_lock};
+ m_submission_time = std::chrono::steady_clock::now();
+ }
m_thread = std::jthread([&](auto stop_token) { Run(stop_token); });
}
TurboMode::~TurboMode() = default;
+void TurboMode::QueueSubmitted() {
+ std::scoped_lock lk{m_submission_lock};
+ m_submission_time = std::chrono::steady_clock::now();
+ m_submission_cv.notify_one();
+}
+
void TurboMode::Run(std::stop_token stop_token) {
auto& dld = m_device.GetLogical();
@@ -38,7 +48,7 @@ void TurboMode::Run(std::stop_token stop_token) {
auto commit = m_allocator.Commit(buffer, MemoryUsage::DeviceLocal);
// Create the descriptor pool to contain our descriptor.
- constexpr VkDescriptorPoolSize pool_size{
+ static constexpr VkDescriptorPoolSize pool_size{
.type = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,
.descriptorCount = 1,
};
@@ -53,7 +63,7 @@ void TurboMode::Run(std::stop_token stop_token) {
});
// Create the descriptor set layout from the pool.
- constexpr VkDescriptorSetLayoutBinding layout_binding{
+ static constexpr VkDescriptorSetLayoutBinding layout_binding{
.binding = 0,
.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,
.descriptorCount = 1,
@@ -199,6 +209,13 @@ void TurboMode::Run(std::stop_token stop_token) {
// Wait for completion.
fence.Wait();
+
+ // Wait for the next graphics queue submission if necessary.
+ std::unique_lock lk{m_submission_lock};
+ Common::CondvarWait(m_submission_cv, lk, stop_token, [this] {
+ return (std::chrono::steady_clock::now() - m_submission_time) <=
+ std::chrono::milliseconds{100};
+ });
}
}
diff --git a/src/video_core/renderer_vulkan/vk_turbo_mode.h b/src/video_core/renderer_vulkan/vk_turbo_mode.h
index 2060e2395..99b5ac50b 100644
--- a/src/video_core/renderer_vulkan/vk_turbo_mode.h
+++ b/src/video_core/renderer_vulkan/vk_turbo_mode.h
@@ -3,6 +3,9 @@
#pragma once
+#include <chrono>
+#include <mutex>
+
#include "common/polyfill_thread.h"
#include "video_core/vulkan_common/vulkan_device.h"
#include "video_core/vulkan_common/vulkan_memory_allocator.h"
@@ -15,11 +18,17 @@ public:
explicit TurboMode(const vk::Instance& instance, const vk::InstanceDispatch& dld);
~TurboMode();
+ void QueueSubmitted();
+
private:
void Run(std::stop_token stop_token);
Device m_device;
MemoryAllocator m_allocator;
+ std::mutex m_submission_lock;
+ std::condition_variable_any m_submission_cv;
+ std::chrono::time_point<std::chrono::steady_clock> m_submission_time{};
+
std::jthread m_thread;
};
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:
explicit DescriptorTable(Tegra::MemoryManager& gpu_memory_) : gpu_memory{gpu_memory_} {}
[[nodiscard]] bool Synchronize(GPUVAddr gpu_addr, u32 limit) {
- [[likely]] if (current_gpu_addr == gpu_addr && current_limit == limit) {
- return false;
- }
+ [[likely]] if (current_gpu_addr == gpu_addr && current_limit == limit) { return false; }
Refresh(gpu_addr, limit);
return true;
}
diff --git a/src/video_core/texture_cache/formatter.cpp b/src/video_core/texture_cache/formatter.cpp
index 418890126..30f72361d 100644
--- a/src/video_core/texture_cache/formatter.cpp
+++ b/src/video_core/texture_cache/formatter.cpp
@@ -22,6 +22,9 @@ std::string Name(const ImageBase& image) {
const u32 num_layers = image.info.resources.layers;
const u32 num_levels = image.info.resources.levels;
std::string resource;
+ if (image.info.num_samples > 1) {
+ resource += fmt::format(":{}xMSAA", image.info.num_samples);
+ }
if (num_layers > 1) {
resource += fmt::format(":L{}", num_layers);
}
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 {
ASSERT_MSG(false, "Invalid texture_type={}", static_cast<int>(config.texture_type.Value()));
break;
}
+ if (num_samples > 1) {
+ size.width *= NumSamplesX(config.msaa_mode);
+ size.height *= NumSamplesY(config.msaa_mode);
+ }
if (type != ImageType::Linear) {
// FIXME: Call this without passing *this
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 {
return 1;
}
+[[nodiscard]] inline int NumSamplesX(Tegra::Texture::MsaaMode msaa_mode) {
+ using Tegra::Texture::MsaaMode;
+ switch (msaa_mode) {
+ case MsaaMode::Msaa1x1:
+ return 1;
+ case MsaaMode::Msaa2x1:
+ case MsaaMode::Msaa2x1_D3D:
+ case MsaaMode::Msaa2x2:
+ case MsaaMode::Msaa2x2_VC4:
+ case MsaaMode::Msaa2x2_VC12:
+ return 2;
+ case MsaaMode::Msaa4x2:
+ case MsaaMode::Msaa4x2_D3D:
+ case MsaaMode::Msaa4x2_VC8:
+ case MsaaMode::Msaa4x2_VC24:
+ case MsaaMode::Msaa4x4:
+ return 4;
+ }
+ ASSERT_MSG(false, "Invalid MSAA mode={}", static_cast<int>(msaa_mode));
+ return 1;
+}
+
+[[nodiscard]] inline int NumSamplesY(Tegra::Texture::MsaaMode msaa_mode) {
+ using Tegra::Texture::MsaaMode;
+ switch (msaa_mode) {
+ case MsaaMode::Msaa1x1:
+ case MsaaMode::Msaa2x1:
+ case MsaaMode::Msaa2x1_D3D:
+ return 1;
+ case MsaaMode::Msaa2x2:
+ case MsaaMode::Msaa2x2_VC4:
+ case MsaaMode::Msaa2x2_VC12:
+ case MsaaMode::Msaa4x2:
+ case MsaaMode::Msaa4x2_D3D:
+ case MsaaMode::Msaa4x2_VC8:
+ case MsaaMode::Msaa4x2_VC24:
+ return 2;
+ case MsaaMode::Msaa4x4:
+ return 4;
+ }
+ ASSERT_MSG(false, "Invalid MSAA mode={}", static_cast<int>(msaa_mode));
+ return 1;
+}
+
} // 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 {
};
template <class T>
-requires std::is_nothrow_move_assignable_v<T> && std::is_nothrow_move_constructible_v<T>
+ requires std::is_nothrow_move_assignable_v<T> && std::is_nothrow_move_constructible_v<T>
class SlotVector {
public:
class Iterator {
diff --git a/src/video_core/texture_cache/texture_cache.h b/src/video_core/texture_cache/texture_cache.h
index 87152c8e9..3e2cbb0b0 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 {
}
template <class P>
+typename P::ImageView& TextureCache<P>::GetImageView(u32 index) noexcept {
+ const auto image_view_id = VisitImageView(channel_state->graphics_image_table,
+ channel_state->graphics_image_view_ids, index);
+ return slot_image_views[image_view_id];
+}
+
+template <class P>
void TextureCache<P>::MarkModification(ImageId id) noexcept {
MarkModification(slot_images[id]);
}
@@ -766,7 +773,7 @@ void TextureCache<P>::RefreshContents(Image& image, ImageId image_id) {
image.flags &= ~ImageFlagBits::CpuModified;
TrackImage(image, image_id);
- if (image.info.num_samples > 1) {
+ if (image.info.num_samples > 1 && !runtime.CanUploadMSAA()) {
LOG_WARNING(HW_GPU, "MSAA image uploads are not implemented");
return;
}
@@ -1160,14 +1167,14 @@ ImageId TextureCache<P>::JoinImages(const ImageInfo& info, GPUVAddr gpu_addr, VA
if (True(overlap.flags & ImageFlagBits::GpuModified)) {
new_image.flags |= ImageFlagBits::GpuModified;
}
+ const auto& resolution = Settings::values.resolution_info;
+ const SubresourceBase base = new_image.TryFindBase(overlap.gpu_addr).value();
+ const u32 up_scale = can_rescale ? resolution.up_scale : 1;
+ const u32 down_shift = can_rescale ? resolution.down_shift : 0;
+ auto copies = MakeShrinkImageCopies(new_info, overlap.info, base, up_scale, down_shift);
if (overlap.info.num_samples != new_image.info.num_samples) {
- LOG_WARNING(HW_GPU, "Copying between images with different samples is not implemented");
+ runtime.CopyImageMSAA(new_image, overlap, std::move(copies));
} else {
- const auto& resolution = Settings::values.resolution_info;
- const SubresourceBase base = new_image.TryFindBase(overlap.gpu_addr).value();
- const u32 up_scale = can_rescale ? resolution.up_scale : 1;
- const u32 down_shift = can_rescale ? resolution.down_shift : 0;
- auto copies = MakeShrinkImageCopies(new_info, overlap.info, base, up_scale, down_shift);
runtime.CopyImage(new_image, overlap, std::move(copies));
}
if (True(overlap.flags & ImageFlagBits::Tracked)) {
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:
/// Return a reference to the given image view id
[[nodiscard]] ImageView& GetImageView(ImageViewId id) noexcept;
+ /// Get the imageview from the graphics descriptor table in the specified index
+ [[nodiscard]] ImageView& GetImageView(u32 index) noexcept;
+
/// Mark an image as modified from the GPU
void MarkModification(ImageId id) noexcept;
diff --git a/src/video_core/texture_cache/util.cpp b/src/video_core/texture_cache/util.cpp
index 03acc68d9..697f86641 100644
--- a/src/video_core/texture_cache/util.cpp
+++ b/src/video_core/texture_cache/util.cpp
@@ -573,10 +573,6 @@ u32 CalculateUnswizzledSizeBytes(const ImageInfo& info) noexcept {
if (info.type == ImageType::Buffer) {
return info.size.width * BytesPerBlock(info.format);
}
- if (info.num_samples > 1) {
- // Multisample images can't be uploaded or downloaded to the host
- return 0;
- }
if (info.type == ImageType::Linear) {
return info.pitch * Common::DivCeil(info.size.height, DefaultBlockHeight(info.format));
}
@@ -703,7 +699,6 @@ ImageViewType RenderTargetImageViewType(const ImageInfo& info) noexcept {
std::vector<ImageCopy> MakeShrinkImageCopies(const ImageInfo& dst, const ImageInfo& src,
SubresourceBase base, u32 up_scale, u32 down_shift) {
ASSERT(dst.resources.levels >= src.resources.levels);
- ASSERT(dst.num_samples == src.num_samples);
const bool is_dst_3d = dst.type == ImageType::e3D;
if (is_dst_3d) {
diff --git a/src/video_core/textures/decoders.cpp b/src/video_core/textures/decoders.cpp
index 59120cd09..95bcdd37b 100644
--- a/src/video_core/textures/decoders.cpp
+++ b/src/video_core/textures/decoders.cpp
@@ -29,7 +29,7 @@ constexpr u32 pdep(u32 value) {
template <u32 mask, u32 incr_amount>
void incrpdep(u32& value) {
- constexpr u32 swizzled_incr = pdep<mask>(incr_amount);
+ static constexpr u32 swizzled_incr = pdep<mask>(incr_amount);
value = ((value | ~mask) + swizzled_incr) & mask;
}
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() {
if (!GFSDK_Aftermath_SUCCEED(GFSDK_Aftermath_EnableGpuCrashDumps(
GFSDK_Aftermath_Version_API, GFSDK_Aftermath_GpuCrashDumpWatchedApiFlags_Vulkan,
GFSDK_Aftermath_GpuCrashDumpFeatureFlags_Default, GpuCrashDumpCallback,
- ShaderDebugInfoCallback, CrashDumpDescriptionCallback, this))) {
+ ShaderDebugInfoCallback, CrashDumpDescriptionCallback, nullptr, this))) {
LOG_ERROR(Render_Vulkan, "GFSDK_Aftermath_EnableGpuCrashDumps failed");
return;
}
@@ -83,7 +83,7 @@ void NsightAftermathTracker::SaveShader(std::span<const u32> spirv) const {
std::scoped_lock lock{mutex};
- GFSDK_Aftermath_ShaderHash hash;
+ GFSDK_Aftermath_ShaderBinaryHash hash;
if (!GFSDK_Aftermath_SUCCEED(
GFSDK_Aftermath_GetShaderHashSpirv(GFSDK_Aftermath_Version_API, &shader, &hash))) {
LOG_ERROR(Render_Vulkan, "Failed to hash SPIR-V module");
@@ -121,8 +121,8 @@ void NsightAftermathTracker::OnGpuCrashDumpCallback(const void* gpu_crash_dump,
u32 json_size = 0;
if (!GFSDK_Aftermath_SUCCEED(GFSDK_Aftermath_GpuCrashDump_GenerateJSON(
decoder, GFSDK_Aftermath_GpuCrashDumpDecoderFlags_ALL_INFO,
- GFSDK_Aftermath_GpuCrashDumpFormatterFlags_NONE, nullptr, nullptr, nullptr, nullptr,
- this, &json_size))) {
+ GFSDK_Aftermath_GpuCrashDumpFormatterFlags_NONE, nullptr, nullptr, nullptr, this,
+ &json_size))) {
LOG_ERROR(Render_Vulkan, "Failed to generate JSON");
return;
}
diff --git a/src/video_core/vulkan_common/vulkan_device.cpp b/src/video_core/vulkan_common/vulkan_device.cpp
index fd1c5a683..23d922e5d 100644
--- a/src/video_core/vulkan_common/vulkan_device.cpp
+++ b/src/video_core/vulkan_common/vulkan_device.cpp
@@ -74,30 +74,6 @@ enum class NvidiaArchitecture {
VoltaOrOlder,
};
-constexpr std::array REQUIRED_EXTENSIONS{
- VK_EXT_VERTEX_ATTRIBUTE_DIVISOR_EXTENSION_NAME,
- VK_EXT_ROBUSTNESS_2_EXTENSION_NAME,
-#ifdef _WIN32
- VK_KHR_EXTERNAL_MEMORY_WIN32_EXTENSION_NAME,
-#endif
-#ifdef __unix__
- VK_KHR_EXTERNAL_MEMORY_FD_EXTENSION_NAME,
-#endif
-};
-
-constexpr std::array REQUIRED_EXTENSIONS_BEFORE_1_2{
- VK_KHR_TIMELINE_SEMAPHORE_EXTENSION_NAME,
- VK_EXT_HOST_QUERY_RESET_EXTENSION_NAME,
- VK_KHR_8BIT_STORAGE_EXTENSION_NAME,
- VK_KHR_SHADER_FLOAT_CONTROLS_EXTENSION_NAME,
- VK_KHR_SAMPLER_MIRROR_CLAMP_TO_EDGE_EXTENSION_NAME,
- VK_KHR_DRIVER_PROPERTIES_EXTENSION_NAME,
-};
-
-constexpr std::array REQUIRED_EXTENSIONS_BEFORE_1_3{
- VK_EXT_SHADER_DEMOTE_TO_HELPER_INVOCATION_EXTENSION_NAME,
-};
-
template <typename T>
void SetNext(void**& next, T& data) {
*next = &data;
@@ -286,24 +262,9 @@ std::unordered_map<VkFormat, VkFormatProperties> GetFormatProperties(vk::Physica
return format_properties;
}
-std::vector<std::string> GetSupportedExtensions(vk::PhysicalDevice physical) {
- const std::vector extensions = physical.EnumerateDeviceExtensionProperties();
- std::vector<std::string> supported_extensions;
- supported_extensions.reserve(extensions.size());
- for (const auto& extension : extensions) {
- supported_extensions.emplace_back(extension.extensionName);
- }
- return supported_extensions;
-}
-
-bool IsExtensionSupported(std::span<const std::string> supported_extensions,
- std::string_view extension) {
- return std::ranges::find(supported_extensions, extension) != supported_extensions.end();
-}
-
NvidiaArchitecture GetNvidiaArchitecture(vk::PhysicalDevice physical,
- std::span<const std::string> exts) {
- if (IsExtensionSupported(exts, VK_KHR_FRAGMENT_SHADING_RATE_EXTENSION_NAME)) {
+ const std::set<std::string, std::less<>>& exts) {
+ if (exts.contains(VK_KHR_FRAGMENT_SHADING_RATE_EXTENSION_NAME)) {
VkPhysicalDeviceFragmentShadingRatePropertiesKHR shading_rate_props{};
shading_rate_props.sType =
VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FRAGMENT_SHADING_RATE_PROPERTIES_KHR;
@@ -316,423 +277,55 @@ NvidiaArchitecture GetNvidiaArchitecture(vk::PhysicalDevice physical,
return NvidiaArchitecture::AmpereOrNewer;
}
}
- if (IsExtensionSupported(exts, VK_NV_SHADING_RATE_IMAGE_EXTENSION_NAME)) {
+ if (exts.contains(VK_NV_SHADING_RATE_IMAGE_EXTENSION_NAME)) {
return NvidiaArchitecture::Turing;
}
return NvidiaArchitecture::VoltaOrOlder;
}
+
+std::vector<const char*> ExtensionListForVulkan(
+ const std::set<std::string, std::less<>>& extensions) {
+ std::vector<const char*> output;
+ for (const auto& extension : extensions) {
+ output.push_back(extension.c_str());
+ }
+ return output;
+}
+
} // Anonymous namespace
Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR surface,
const vk::InstanceDispatch& dld_)
- : instance{instance_}, dld{dld_}, physical{physical_}, properties{physical.GetProperties()},
- instance_version{properties.apiVersion}, supported_extensions{GetSupportedExtensions(
- physical)},
+ : instance{instance_}, dld{dld_}, physical{physical_},
format_properties(GetFormatProperties(physical)) {
- CheckSuitability(surface != nullptr);
- SetupFamilies(surface);
- SetupFeatures();
- SetupProperties();
-
- const auto queue_cis = GetDeviceQueueCreateInfos();
- const std::vector extensions = LoadExtensions(surface != nullptr);
-
- VkPhysicalDeviceFeatures2 features2{
- .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2,
- .pNext = nullptr,
- .features{
- .robustBufferAccess = true,
- .fullDrawIndexUint32 = false,
- .imageCubeArray = true,
- .independentBlend = true,
- .geometryShader = true,
- .tessellationShader = true,
- .sampleRateShading = true,
- .dualSrcBlend = true,
- .logicOp = true,
- .multiDrawIndirect = true,
- .drawIndirectFirstInstance = true,
- .depthClamp = true,
- .depthBiasClamp = true,
- .fillModeNonSolid = true,
- .depthBounds = is_depth_bounds_supported,
- .wideLines = true,
- .largePoints = true,
- .alphaToOne = false,
- .multiViewport = true,
- .samplerAnisotropy = true,
- .textureCompressionETC2 = false,
- .textureCompressionASTC_LDR = is_optimal_astc_supported,
- .textureCompressionBC = false,
- .occlusionQueryPrecise = true,
- .pipelineStatisticsQuery = false,
- .vertexPipelineStoresAndAtomics = true,
- .fragmentStoresAndAtomics = true,
- .shaderTessellationAndGeometryPointSize = false,
- .shaderImageGatherExtended = true,
- .shaderStorageImageExtendedFormats = false,
- .shaderStorageImageMultisample = is_shader_storage_image_multisample,
- .shaderStorageImageReadWithoutFormat = is_formatless_image_load_supported,
- .shaderStorageImageWriteWithoutFormat = true,
- .shaderUniformBufferArrayDynamicIndexing = false,
- .shaderSampledImageArrayDynamicIndexing = false,
- .shaderStorageBufferArrayDynamicIndexing = false,
- .shaderStorageImageArrayDynamicIndexing = false,
- .shaderClipDistance = true,
- .shaderCullDistance = true,
- .shaderFloat64 = is_shader_float64_supported,
- .shaderInt64 = is_shader_int64_supported,
- .shaderInt16 = is_shader_int16_supported,
- .shaderResourceResidency = false,
- .shaderResourceMinLod = false,
- .sparseBinding = false,
- .sparseResidencyBuffer = false,
- .sparseResidencyImage2D = false,
- .sparseResidencyImage3D = false,
- .sparseResidency2Samples = false,
- .sparseResidency4Samples = false,
- .sparseResidency8Samples = false,
- .sparseResidency16Samples = false,
- .sparseResidencyAliased = false,
- .variableMultisampleRate = false,
- .inheritedQueries = false,
- },
- };
- const void* first_next = &features2;
- void** next = &features2.pNext;
-
- VkPhysicalDeviceTimelineSemaphoreFeatures timeline_semaphore{
- .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_TIMELINE_SEMAPHORE_FEATURES,
- .pNext = nullptr,
- .timelineSemaphore = true,
- };
- SetNext(next, timeline_semaphore);
-
- VkPhysicalDevice16BitStorageFeatures bit16_storage{
- .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_16BIT_STORAGE_FEATURES,
- .pNext = nullptr,
- .storageBuffer16BitAccess = true,
- .uniformAndStorageBuffer16BitAccess = true,
- .storagePushConstant16 = false,
- .storageInputOutput16 = false,
- };
- SetNext(next, bit16_storage);
-
- VkPhysicalDevice8BitStorageFeatures bit8_storage{
- .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_8BIT_STORAGE_FEATURES,
- .pNext = nullptr,
- .storageBuffer8BitAccess = true,
- .uniformAndStorageBuffer8BitAccess = true,
- .storagePushConstant8 = false,
- };
- SetNext(next, bit8_storage);
-
- VkPhysicalDeviceRobustness2FeaturesEXT robustness2{
- .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_ROBUSTNESS_2_FEATURES_EXT,
- .pNext = nullptr,
- .robustBufferAccess2 = true,
- .robustImageAccess2 = true,
- .nullDescriptor = true,
- };
- SetNext(next, robustness2);
-
- VkPhysicalDeviceHostQueryResetFeatures host_query_reset{
- .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_HOST_QUERY_RESET_FEATURES,
- .pNext = nullptr,
- .hostQueryReset = true,
- };
- SetNext(next, host_query_reset);
-
- VkPhysicalDeviceVariablePointerFeatures variable_pointers{
- .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VARIABLE_POINTERS_FEATURES,
- .pNext = nullptr,
- .variablePointersStorageBuffer = VK_TRUE,
- .variablePointers = VK_TRUE,
- };
- SetNext(next, variable_pointers);
-
- VkPhysicalDeviceShaderDemoteToHelperInvocationFeatures demote{
- .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_DEMOTE_TO_HELPER_INVOCATION_FEATURES,
- .pNext = nullptr,
- .shaderDemoteToHelperInvocation = true,
- };
- SetNext(next, demote);
-
- VkPhysicalDeviceShaderDrawParametersFeatures draw_parameters{
- .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_DRAW_PARAMETER_FEATURES,
- .pNext = nullptr,
- .shaderDrawParameters = true,
- };
- SetNext(next, draw_parameters);
-
- VkPhysicalDeviceShaderFloat16Int8Features float16_int8;
- if (is_int8_supported || is_float16_supported) {
- float16_int8 = {
- .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_FLOAT16_INT8_FEATURES,
- .pNext = nullptr,
- .shaderFloat16 = is_float16_supported,
- .shaderInt8 = is_int8_supported,
- };
- SetNext(next, float16_int8);
- }
- if (!is_float16_supported) {
- LOG_INFO(Render_Vulkan, "Device doesn't support float16 natively");
- }
- if (!is_int8_supported) {
- LOG_INFO(Render_Vulkan, "Device doesn't support int8 natively");
- }
-
- if (!nv_viewport_swizzle) {
- LOG_INFO(Render_Vulkan, "Device doesn't support viewport swizzles");
- }
-
- if (!nv_viewport_array2) {
- LOG_INFO(Render_Vulkan, "Device doesn't support viewport masks");
- }
-
- if (!nv_geometry_shader_passthrough) {
- LOG_INFO(Render_Vulkan, "Device doesn't support passthrough geometry shaders");
- }
+ // Get suitability and device properties.
+ const bool is_suitable = GetSuitability(surface != nullptr);
- VkPhysicalDeviceUniformBufferStandardLayoutFeatures std430_layout;
- if (khr_uniform_buffer_standard_layout) {
- std430_layout = {
- .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_UNIFORM_BUFFER_STANDARD_LAYOUT_FEATURES,
- .pNext = nullptr,
- .uniformBufferStandardLayout = true,
- };
- SetNext(next, std430_layout);
- } else {
- LOG_INFO(Render_Vulkan, "Device doesn't support packed UBOs");
- }
-
- VkPhysicalDeviceIndexTypeUint8FeaturesEXT index_type_uint8;
- if (ext_index_type_uint8) {
- index_type_uint8 = {
- .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_INDEX_TYPE_UINT8_FEATURES_EXT,
- .pNext = nullptr,
- .indexTypeUint8 = true,
- };
- SetNext(next, index_type_uint8);
- } else {
- LOG_INFO(Render_Vulkan, "Device doesn't support uint8 indexes");
- }
-
- VkPhysicalDevicePrimitiveTopologyListRestartFeaturesEXT primitive_topology_list_restart;
- if (is_topology_list_restart_supported || is_patch_list_restart_supported) {
- primitive_topology_list_restart = {
- .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PRIMITIVE_TOPOLOGY_LIST_RESTART_FEATURES_EXT,
- .pNext = nullptr,
- .primitiveTopologyListRestart = is_topology_list_restart_supported,
- .primitiveTopologyPatchListRestart = is_patch_list_restart_supported,
- };
- SetNext(next, primitive_topology_list_restart);
- } else {
- LOG_INFO(Render_Vulkan, "Device doesn't support list topology primitive restart");
- }
-
- VkPhysicalDeviceTransformFeedbackFeaturesEXT transform_feedback;
- if (ext_transform_feedback) {
- transform_feedback = {
- .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_TRANSFORM_FEEDBACK_FEATURES_EXT,
- .pNext = nullptr,
- .transformFeedback = true,
- .geometryStreams = true,
- };
- SetNext(next, transform_feedback);
- } else {
- LOG_INFO(Render_Vulkan, "Device doesn't support transform feedbacks");
- }
-
- VkPhysicalDeviceCustomBorderColorFeaturesEXT custom_border;
- if (ext_custom_border_color) {
- custom_border = {
- .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_CUSTOM_BORDER_COLOR_FEATURES_EXT,
- .pNext = nullptr,
- .customBorderColors = VK_TRUE,
- .customBorderColorWithoutFormat = VK_TRUE,
- };
- SetNext(next, custom_border);
- } else {
- LOG_INFO(Render_Vulkan, "Device doesn't support custom border colors");
- }
-
- VkPhysicalDeviceExtendedDynamicStateFeaturesEXT dynamic_state;
- if (ext_extended_dynamic_state) {
- dynamic_state = {
- .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTENDED_DYNAMIC_STATE_FEATURES_EXT,
- .pNext = nullptr,
- .extendedDynamicState = VK_TRUE,
- };
- SetNext(next, dynamic_state);
- } else {
- LOG_INFO(Render_Vulkan, "Device doesn't support extended dynamic state");
- }
-
- VkPhysicalDeviceExtendedDynamicState2FeaturesEXT dynamic_state_2;
- if (ext_extended_dynamic_state_2) {
- dynamic_state_2 = {
- .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTENDED_DYNAMIC_STATE_2_FEATURES_EXT,
- .pNext = nullptr,
- .extendedDynamicState2 = VK_TRUE,
- .extendedDynamicState2LogicOp = ext_extended_dynamic_state_2_extra ? VK_TRUE : VK_FALSE,
- .extendedDynamicState2PatchControlPoints = VK_FALSE,
- };
- SetNext(next, dynamic_state_2);
- } else {
- LOG_INFO(Render_Vulkan, "Device doesn't support extended dynamic state 2");
- }
-
- VkPhysicalDeviceExtendedDynamicState3FeaturesEXT dynamic_state_3;
- if (ext_extended_dynamic_state_3) {
- dynamic_state_3 = {
- .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTENDED_DYNAMIC_STATE_3_FEATURES_EXT,
- .pNext = nullptr,
- .extendedDynamicState3TessellationDomainOrigin = VK_FALSE,
- .extendedDynamicState3DepthClampEnable =
- ext_extended_dynamic_state_3_enables ? VK_TRUE : VK_FALSE,
- .extendedDynamicState3PolygonMode = VK_FALSE,
- .extendedDynamicState3RasterizationSamples = VK_FALSE,
- .extendedDynamicState3SampleMask = VK_FALSE,
- .extendedDynamicState3AlphaToCoverageEnable = VK_FALSE,
- .extendedDynamicState3AlphaToOneEnable = VK_FALSE,
- .extendedDynamicState3LogicOpEnable =
- ext_extended_dynamic_state_3_enables ? VK_TRUE : VK_FALSE,
- .extendedDynamicState3ColorBlendEnable =
- ext_extended_dynamic_state_3_blend ? VK_TRUE : VK_FALSE,
- .extendedDynamicState3ColorBlendEquation =
- ext_extended_dynamic_state_3_blend ? VK_TRUE : VK_FALSE,
- .extendedDynamicState3ColorWriteMask =
- ext_extended_dynamic_state_3_blend ? VK_TRUE : VK_FALSE,
- .extendedDynamicState3RasterizationStream = VK_FALSE,
- .extendedDynamicState3ConservativeRasterizationMode = VK_FALSE,
- .extendedDynamicState3ExtraPrimitiveOverestimationSize = VK_FALSE,
- .extendedDynamicState3DepthClipEnable = VK_FALSE,
- .extendedDynamicState3SampleLocationsEnable = VK_FALSE,
- .extendedDynamicState3ColorBlendAdvanced = VK_FALSE,
- .extendedDynamicState3ProvokingVertexMode = VK_FALSE,
- .extendedDynamicState3LineRasterizationMode = VK_FALSE,
- .extendedDynamicState3LineStippleEnable = VK_FALSE,
- .extendedDynamicState3DepthClipNegativeOneToOne = VK_FALSE,
- .extendedDynamicState3ViewportWScalingEnable = VK_FALSE,
- .extendedDynamicState3ViewportSwizzle = VK_FALSE,
- .extendedDynamicState3CoverageToColorEnable = VK_FALSE,
- .extendedDynamicState3CoverageToColorLocation = VK_FALSE,
- .extendedDynamicState3CoverageModulationMode = VK_FALSE,
- .extendedDynamicState3CoverageModulationTableEnable = VK_FALSE,
- .extendedDynamicState3CoverageModulationTable = VK_FALSE,
- .extendedDynamicState3CoverageReductionMode = VK_FALSE,
- .extendedDynamicState3RepresentativeFragmentTestEnable = VK_FALSE,
- .extendedDynamicState3ShadingRateImageEnable = VK_FALSE,
- };
- SetNext(next, dynamic_state_3);
- } else {
- LOG_INFO(Render_Vulkan, "Device doesn't support extended dynamic state 3");
- }
-
- VkPhysicalDeviceLineRasterizationFeaturesEXT line_raster;
- if (ext_line_rasterization) {
- line_raster = {
- .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_LINE_RASTERIZATION_FEATURES_EXT,
- .pNext = nullptr,
- .rectangularLines = VK_TRUE,
- .bresenhamLines = VK_FALSE,
- .smoothLines = VK_TRUE,
- .stippledRectangularLines = VK_FALSE,
- .stippledBresenhamLines = VK_FALSE,
- .stippledSmoothLines = VK_FALSE,
- };
- SetNext(next, line_raster);
- } else {
- LOG_INFO(Render_Vulkan, "Device doesn't support smooth lines");
- }
-
- if (!ext_conservative_rasterization) {
- LOG_INFO(Render_Vulkan, "Device doesn't support conservative rasterization");
- }
-
- VkPhysicalDeviceProvokingVertexFeaturesEXT provoking_vertex;
- if (ext_provoking_vertex) {
- provoking_vertex = {
- .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROVOKING_VERTEX_FEATURES_EXT,
- .pNext = nullptr,
- .provokingVertexLast = VK_TRUE,
- .transformFeedbackPreservesProvokingVertex = VK_TRUE,
- };
- SetNext(next, provoking_vertex);
- } else {
- LOG_INFO(Render_Vulkan, "Device doesn't support provoking vertex last");
- }
-
- VkPhysicalDeviceVertexInputDynamicStateFeaturesEXT vertex_input_dynamic;
- if (ext_vertex_input_dynamic_state) {
- vertex_input_dynamic = {
- .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VERTEX_INPUT_DYNAMIC_STATE_FEATURES_EXT,
- .pNext = nullptr,
- .vertexInputDynamicState = VK_TRUE,
- };
- SetNext(next, vertex_input_dynamic);
- } else {
- LOG_INFO(Render_Vulkan, "Device doesn't support vertex input dynamic state");
- }
-
- VkPhysicalDeviceShaderAtomicInt64Features atomic_int64;
- if (ext_shader_atomic_int64) {
- atomic_int64 = {
- .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_ATOMIC_INT64_FEATURES,
- .pNext = nullptr,
- .shaderBufferInt64Atomics = VK_TRUE,
- .shaderSharedInt64Atomics = VK_TRUE,
- };
- SetNext(next, atomic_int64);
- }
+ const VkDriverId driver_id = properties.driver.driverID;
+ const bool is_radv = driver_id == VK_DRIVER_ID_MESA_RADV;
+ const bool is_amd_driver =
+ driver_id == VK_DRIVER_ID_AMD_PROPRIETARY || driver_id == VK_DRIVER_ID_AMD_OPEN_SOURCE;
+ const bool is_amd = is_amd_driver || is_radv;
+ const bool is_intel_windows = driver_id == VK_DRIVER_ID_INTEL_PROPRIETARY_WINDOWS;
+ const bool is_intel_anv = driver_id == VK_DRIVER_ID_INTEL_OPEN_SOURCE_MESA;
+ const bool is_nvidia = driver_id == VK_DRIVER_ID_NVIDIA_PROPRIETARY;
+ const bool is_mvk = driver_id == VK_DRIVER_ID_MOLTENVK;
- VkPhysicalDeviceWorkgroupMemoryExplicitLayoutFeaturesKHR workgroup_layout;
- if (khr_workgroup_memory_explicit_layout && is_shader_int16_supported) {
- workgroup_layout = {
- .sType =
- VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_WORKGROUP_MEMORY_EXPLICIT_LAYOUT_FEATURES_KHR,
- .pNext = nullptr,
- .workgroupMemoryExplicitLayout = VK_TRUE,
- .workgroupMemoryExplicitLayoutScalarBlockLayout = VK_TRUE,
- .workgroupMemoryExplicitLayout8BitAccess = VK_TRUE,
- .workgroupMemoryExplicitLayout16BitAccess = VK_TRUE,
- };
- SetNext(next, workgroup_layout);
- } else if (khr_workgroup_memory_explicit_layout) {
- // TODO(lat9nq): Find a proper fix for this
- LOG_WARNING(Render_Vulkan, "Disabling VK_KHR_workgroup_memory_explicit_layout due to a "
- "yuzu bug when host driver does not support 16-bit integers");
- khr_workgroup_memory_explicit_layout = false;
+ if (is_mvk && !is_suitable) {
+ LOG_WARNING(Render_Vulkan, "Unsuitable driver is MoltenVK, continuing anyway");
+ } else if (!is_suitable) {
+ throw vk::Exception(VK_ERROR_INCOMPATIBLE_DRIVER);
}
- VkPhysicalDevicePipelineExecutablePropertiesFeaturesKHR executable_properties;
- if (khr_pipeline_executable_properties) {
- LOG_INFO(Render_Vulkan, "Enabling shader feedback, expect slower shader build times");
- executable_properties = {
- .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PIPELINE_EXECUTABLE_PROPERTIES_FEATURES_KHR,
- .pNext = nullptr,
- .pipelineExecutableInfo = VK_TRUE,
- };
- SetNext(next, executable_properties);
- }
-
- if (!ext_depth_range_unrestricted) {
- LOG_INFO(Render_Vulkan, "Device doesn't support depth range unrestricted");
- }
+ SetupFamilies(surface);
+ const auto queue_cis = GetDeviceQueueCreateInfos();
- VkPhysicalDeviceDepthClipControlFeaturesEXT depth_clip_control_features;
- if (ext_depth_clip_control) {
- depth_clip_control_features = {
- .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DEPTH_CLIP_CONTROL_FEATURES_EXT,
- .pNext = nullptr,
- .depthClipControl = VK_TRUE,
- };
- SetNext(next, depth_clip_control_features);
- }
+ // GetSuitability has already configured the linked list of features for us.
+ // Reuse it here.
+ const void* first_next = &features2;
- VkDeviceDiagnosticsConfigCreateInfoNV diagnostics_nv;
- if (Settings::values.enable_nsight_aftermath && nv_device_diagnostics_config) {
+ VkDeviceDiagnosticsConfigCreateInfoNV diagnostics_nv{};
+ if (Settings::values.enable_nsight_aftermath && extensions.device_diagnostics_config) {
nsight_aftermath_tracker = std::make_unique<NsightAftermathTracker>();
diagnostics_nv = {
@@ -744,33 +337,39 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR
};
first_next = &diagnostics_nv;
}
- logical = vk::Device::Create(physical, queue_cis, extensions, first_next, dld);
- is_integrated = properties.deviceType == VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU;
- is_virtual = properties.deviceType == VK_PHYSICAL_DEVICE_TYPE_VIRTUAL_GPU;
- is_non_gpu = properties.deviceType == VK_PHYSICAL_DEVICE_TYPE_OTHER ||
- properties.deviceType == VK_PHYSICAL_DEVICE_TYPE_CPU;
+ is_blit_depth_stencil_supported = TestDepthStencilBlits();
+ is_optimal_astc_supported = ComputeIsOptimalAstcSupported();
+ is_warp_potentially_bigger = !extensions.subgroup_size_control ||
+ properties.subgroup_size_control.maxSubgroupSize > GuestWarpSize;
+
+ is_integrated = properties.properties.deviceType == VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU;
+ is_virtual = properties.properties.deviceType == VK_PHYSICAL_DEVICE_TYPE_VIRTUAL_GPU;
+ is_non_gpu = properties.properties.deviceType == VK_PHYSICAL_DEVICE_TYPE_OTHER ||
+ properties.properties.deviceType == VK_PHYSICAL_DEVICE_TYPE_CPU;
+
+ supports_d24_depth =
+ IsFormatSupported(VK_FORMAT_D24_UNORM_S8_UINT,
+ VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT, FormatType::Optimal);
CollectPhysicalMemoryInfo();
- CollectTelemetryParameters();
CollectToolingInfo();
- if (driver_id == VK_DRIVER_ID_NVIDIA_PROPRIETARY_KHR) {
- const u32 nv_major_version = (properties.driverVersion >> 22) & 0x3ff;
-
+ if (is_nvidia) {
+ const u32 nv_major_version = (properties.properties.driverVersion >> 22) & 0x3ff;
const auto arch = GetNvidiaArchitecture(physical, supported_extensions);
switch (arch) {
case NvidiaArchitecture::AmpereOrNewer:
- LOG_WARNING(Render_Vulkan, "Blacklisting Ampere devices from float16 math");
- is_float16_supported = false;
+ LOG_WARNING(Render_Vulkan, "Ampere and newer have broken float16 math");
+ features.shader_float16_int8.shaderFloat16 = false;
break;
case NvidiaArchitecture::Turing:
break;
case NvidiaArchitecture::VoltaOrOlder:
if (nv_major_version < 527) {
- LOG_WARNING(Render_Vulkan,
- "Blacklisting Volta and older from VK_KHR_push_descriptor");
- khr_push_descriptor = false;
+ LOG_WARNING(Render_Vulkan, "Volta and older have broken VK_KHR_push_descriptor");
+ extensions.push_descriptor = false;
+ loaded_extensions.erase(VK_KHR_PUSH_DESCRIPTOR_EXTENSION_NAME);
}
break;
}
@@ -779,75 +378,75 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR
cant_blit_msaa = true;
}
}
- const bool is_radv = driver_id == VK_DRIVER_ID_MESA_RADV;
- if (ext_extended_dynamic_state && is_radv) {
+ if (extensions.extended_dynamic_state && is_radv) {
// Mask driver version variant
- const u32 version = (properties.driverVersion << 3) >> 3;
+ const u32 version = (properties.properties.driverVersion << 3) >> 3;
if (version < VK_MAKE_API_VERSION(0, 21, 2, 0)) {
LOG_WARNING(Render_Vulkan,
"RADV versions older than 21.2 have broken VK_EXT_extended_dynamic_state");
- ext_extended_dynamic_state = false;
+ extensions.extended_dynamic_state = false;
+ loaded_extensions.erase(VK_EXT_EXTENDED_DYNAMIC_STATE_EXTENSION_NAME);
}
}
- if (ext_vertex_input_dynamic_state && is_radv) {
+ if (extensions.extended_dynamic_state2 && is_radv) {
+ const u32 version = (properties.properties.driverVersion << 3) >> 3;
+ if (version < VK_MAKE_API_VERSION(0, 22, 3, 1)) {
+ LOG_WARNING(
+ Render_Vulkan,
+ "RADV versions older than 22.3.1 have broken VK_EXT_extended_dynamic_state2");
+ features.extended_dynamic_state2.extendedDynamicState2 = false;
+ features.extended_dynamic_state2.extendedDynamicState2LogicOp = false;
+ features.extended_dynamic_state2.extendedDynamicState2PatchControlPoints = false;
+ extensions.extended_dynamic_state2 = false;
+ loaded_extensions.erase(VK_EXT_EXTENDED_DYNAMIC_STATE_2_EXTENSION_NAME);
+ }
+ }
+ if (extensions.vertex_input_dynamic_state && is_radv) {
// TODO(ameerj): Blacklist only offending driver versions
// TODO(ameerj): Confirm if RDNA1 is affected
const bool is_rdna2 =
- IsExtensionSupported(supported_extensions, VK_KHR_FRAGMENT_SHADING_RATE_EXTENSION_NAME);
+ supported_extensions.contains(VK_KHR_FRAGMENT_SHADING_RATE_EXTENSION_NAME);
if (is_rdna2) {
LOG_WARNING(Render_Vulkan,
"RADV has broken VK_EXT_vertex_input_dynamic_state on RDNA2 hardware");
- ext_vertex_input_dynamic_state = false;
+ extensions.vertex_input_dynamic_state = false;
+ loaded_extensions.erase(VK_EXT_VERTEX_INPUT_DYNAMIC_STATE_EXTENSION_NAME);
}
}
- if (ext_extended_dynamic_state_2 && is_radv) {
- const u32 version = (properties.driverVersion << 3) >> 3;
- if (version < VK_MAKE_API_VERSION(0, 22, 3, 1)) {
- LOG_WARNING(
- Render_Vulkan,
- "RADV versions older than 22.3.1 have broken VK_EXT_extended_dynamic_state2");
- ext_extended_dynamic_state_2 = false;
- ext_extended_dynamic_state_2_extra = false;
- }
- }
- sets_per_pool = 64;
- const bool is_amd =
- driver_id == VK_DRIVER_ID_AMD_PROPRIETARY || driver_id == VK_DRIVER_ID_AMD_OPEN_SOURCE;
- if (is_amd) {
+ sets_per_pool = 64;
+ if (is_amd_driver) {
// AMD drivers need a higher amount of Sets per Pool in certain circunstances like in XC2.
sets_per_pool = 96;
// Disable VK_IMAGE_CREATE_CUBE_COMPATIBLE_BIT on AMD GCN4 and lower as it is broken.
- if (!is_float16_supported) {
- LOG_WARNING(
- Render_Vulkan,
- "AMD GCN4 and earlier do not properly support VK_IMAGE_CREATE_CUBE_COMPATIBLE_BIT");
+ if (!features.shader_float16_int8.shaderFloat16) {
+ LOG_WARNING(Render_Vulkan,
+ "AMD GCN4 and earlier have broken VK_IMAGE_CREATE_CUBE_COMPATIBLE_BIT");
has_broken_cube_compatibility = true;
}
}
- const bool is_amd_or_radv = is_amd || is_radv;
- if (ext_sampler_filter_minmax && is_amd_or_radv) {
+ if (extensions.sampler_filter_minmax && is_amd) {
// Disable ext_sampler_filter_minmax on AMD GCN4 and lower as it is broken.
- if (!is_float16_supported) {
+ if (!features.shader_float16_int8.shaderFloat16) {
LOG_WARNING(Render_Vulkan,
- "Blacklisting AMD GCN4 and earlier for VK_EXT_sampler_filter_minmax");
- ext_sampler_filter_minmax = false;
+ "AMD GCN4 and earlier have broken VK_EXT_sampler_filter_minmax");
+ extensions.sampler_filter_minmax = false;
+ loaded_extensions.erase(VK_EXT_SAMPLER_FILTER_MINMAX_EXTENSION_NAME);
}
}
- const bool is_intel_windows = driver_id == VK_DRIVER_ID_INTEL_PROPRIETARY_WINDOWS;
- const bool is_intel_anv = driver_id == VK_DRIVER_ID_INTEL_OPEN_SOURCE_MESA;
- if (ext_vertex_input_dynamic_state && is_intel_windows) {
- const u32 version = (properties.driverVersion << 3) >> 3;
+ if (extensions.vertex_input_dynamic_state && is_intel_windows) {
+ const u32 version = (properties.properties.driverVersion << 3) >> 3;
if (version < VK_MAKE_API_VERSION(27, 20, 100, 0)) {
- LOG_WARNING(Render_Vulkan, "Blacklisting Intel for VK_EXT_vertex_input_dynamic_state");
- ext_vertex_input_dynamic_state = false;
+ LOG_WARNING(Render_Vulkan, "Intel has broken VK_EXT_vertex_input_dynamic_state");
+ extensions.vertex_input_dynamic_state = false;
+ loaded_extensions.erase(VK_EXT_VERTEX_INPUT_DYNAMIC_STATE_EXTENSION_NAME);
}
}
- if (is_float16_supported && is_intel_windows) {
+ if (features.shader_float16_int8.shaderFloat16 && is_intel_windows) {
// Intel's compiler crashes when using fp16 on Astral Chain, disable it for the time being.
- LOG_WARNING(Render_Vulkan, "Blacklisting Intel proprietary from float16 math");
- is_float16_supported = false;
+ LOG_WARNING(Render_Vulkan, "Intel has broken float16 math");
+ features.shader_float16_int8.shaderFloat16 = false;
}
if (is_intel_windows) {
LOG_WARNING(Render_Vulkan, "Intel proprietary drivers do not support MSAA image blits");
@@ -857,10 +456,17 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR
LOG_WARNING(Render_Vulkan, "ANV driver does not support native BGR format");
must_emulate_bgr565 = true;
}
+ if (is_mvk) {
+ LOG_WARNING(Render_Vulkan,
+ "MVK driver breaks when using more than 16 vertex attributes/bindings");
+ properties.properties.limits.maxVertexInputAttributes =
+ std::min(properties.properties.limits.maxVertexInputAttributes, 16U);
+ properties.properties.limits.maxVertexInputBindings =
+ std::min(properties.properties.limits.maxVertexInputBindings, 16U);
+ }
- supports_d24_depth =
- IsFormatSupported(VK_FORMAT_D24_UNORM_S8_UINT,
- VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT, FormatType::Optimal);
+ logical = vk::Device::Create(physical, queue_cis, ExtensionListForVulkan(loaded_extensions),
+ first_next, dld);
graphics_queue = logical.GetQueue(graphics_family);
present_queue = logical.GetQueue(present_family);
@@ -915,7 +521,7 @@ void Device::SaveShader(std::span<const u32> spirv) const {
}
}
-bool Device::IsOptimalAstcSupported(const VkPhysicalDeviceFeatures& features) const {
+bool Device::ComputeIsOptimalAstcSupported() const {
// Disable for now to avoid converting ASTC twice.
static constexpr std::array astc_formats = {
VK_FORMAT_ASTC_4x4_UNORM_BLOCK, VK_FORMAT_ASTC_4x4_SRGB_BLOCK,
@@ -933,7 +539,7 @@ bool Device::IsOptimalAstcSupported(const VkPhysicalDeviceFeatures& features) co
VK_FORMAT_ASTC_12x10_UNORM_BLOCK, VK_FORMAT_ASTC_12x10_SRGB_BLOCK,
VK_FORMAT_ASTC_12x12_UNORM_BLOCK, VK_FORMAT_ASTC_12x12_SRGB_BLOCK,
};
- if (!features.textureCompressionASTC_LDR) {
+ if (!features.features.textureCompressionASTC_LDR) {
return false;
}
const auto format_feature_usage{
@@ -971,7 +577,7 @@ bool Device::IsFormatSupported(VkFormat wanted_format, VkFormatFeatureFlags want
}
std::string Device::GetDriverName() const {
- switch (driver_id) {
+ switch (properties.driver.driverID) {
case VK_DRIVER_ID_AMD_PROPRIETARY:
return "AMD";
case VK_DRIVER_ID_AMD_OPEN_SOURCE:
@@ -987,522 +593,336 @@ std::string Device::GetDriverName() const {
case VK_DRIVER_ID_MESA_LLVMPIPE:
return "LAVAPIPE";
default:
- return vendor_name;
+ return properties.driver.driverName;
}
}
bool Device::ShouldBoostClocks() const {
+ const auto driver_id = properties.driver.driverID;
+ const auto vendor_id = properties.properties.vendorID;
+ const auto device_id = properties.properties.deviceID;
+
const bool validated_driver =
driver_id == VK_DRIVER_ID_AMD_PROPRIETARY || driver_id == VK_DRIVER_ID_AMD_OPEN_SOURCE ||
driver_id == VK_DRIVER_ID_MESA_RADV || driver_id == VK_DRIVER_ID_NVIDIA_PROPRIETARY ||
driver_id == VK_DRIVER_ID_INTEL_PROPRIETARY_WINDOWS ||
driver_id == VK_DRIVER_ID_INTEL_OPEN_SOURCE_MESA;
- const bool is_steam_deck = properties.vendorID == 0x1002 && properties.deviceID == 0x163F;
+ const bool is_steam_deck = vendor_id == 0x1002 && device_id == 0x163F;
return validated_driver && !is_steam_deck;
}
-static std::vector<const char*> ExtensionsRequiredForInstanceVersion(u32 available_version) {
- std::vector<const char*> extensions{REQUIRED_EXTENSIONS.begin(), REQUIRED_EXTENSIONS.end()};
+bool Device::GetSuitability(bool requires_swapchain) {
+ // Assume we will be suitable.
+ bool suitable = true;
- if (available_version < VK_API_VERSION_1_2) {
- extensions.insert(extensions.end(), REQUIRED_EXTENSIONS_BEFORE_1_2.begin(),
- REQUIRED_EXTENSIONS_BEFORE_1_2.end());
- }
-
- if (available_version < VK_API_VERSION_1_3) {
- extensions.insert(extensions.end(), REQUIRED_EXTENSIONS_BEFORE_1_3.begin(),
- REQUIRED_EXTENSIONS_BEFORE_1_3.end());
- }
-
- return extensions;
-}
+ // Configure properties.
+ properties.properties = physical.GetProperties();
-void Device::CheckSuitability(bool requires_swapchain) const {
- std::vector<const char*> required_extensions =
- ExtensionsRequiredForInstanceVersion(instance_version);
- std::vector<const char*> available_extensions;
+ // Set instance version.
+ instance_version = properties.properties.apiVersion;
- if (requires_swapchain) {
- required_extensions.push_back(VK_KHR_SWAPCHAIN_EXTENSION_NAME);
- }
+ // Minimum of API version 1.1 is required. (This is well-supported.)
+ ASSERT(instance_version >= VK_API_VERSION_1_1);
+ // Get available extensions.
auto extension_properties = physical.EnumerateDeviceExtensionProperties();
+ // Get the set of supported extensions.
+ supported_extensions.clear();
for (const VkExtensionProperties& property : extension_properties) {
- available_extensions.push_back(property.extensionName);
+ supported_extensions.insert(property.extensionName);
}
- bool has_all_required_extensions = true;
- for (const char* requirement_name : required_extensions) {
- const bool found =
- std::ranges::any_of(available_extensions, [&](const char* extension_name) {
- return std::strcmp(requirement_name, extension_name) == 0;
- });
+ // Generate list of extensions to load.
+ loaded_extensions.clear();
- if (!found) {
- LOG_ERROR(Render_Vulkan, "Missing required extension: {}", requirement_name);
- has_all_required_extensions = false;
- }
+#define EXTENSION(prefix, macro_name, var_name) \
+ if (supported_extensions.contains(VK_##prefix##_##macro_name##_EXTENSION_NAME)) { \
+ loaded_extensions.insert(VK_##prefix##_##macro_name##_EXTENSION_NAME); \
+ extensions.var_name = true; \
}
-
- if (!has_all_required_extensions) {
- throw vk::Exception(VK_ERROR_EXTENSION_NOT_PRESENT);
+#define FEATURE_EXTENSION(prefix, struct_name, macro_name, var_name) \
+ if (supported_extensions.contains(VK_##prefix##_##macro_name##_EXTENSION_NAME)) { \
+ loaded_extensions.insert(VK_##prefix##_##macro_name##_EXTENSION_NAME); \
+ extensions.var_name = true; \
}
- struct LimitTuple {
- u32 minimum;
- u32 value;
- const char* name;
- };
- const VkPhysicalDeviceLimits& limits{properties.limits};
- const std::array limits_report{
- LimitTuple{65536, limits.maxUniformBufferRange, "maxUniformBufferRange"},
- LimitTuple{16, limits.maxViewports, "maxViewports"},
- LimitTuple{8, limits.maxColorAttachments, "maxColorAttachments"},
- LimitTuple{8, limits.maxClipDistances, "maxClipDistances"},
- };
- for (const auto& tuple : limits_report) {
- if (tuple.value < tuple.minimum) {
- LOG_ERROR(Render_Vulkan, "{} has to be {} or greater but it is {}", tuple.name,
- tuple.minimum, tuple.value);
- throw vk::Exception(VK_ERROR_FEATURE_NOT_PRESENT);
- }
+ if (instance_version < VK_API_VERSION_1_2) {
+ FOR_EACH_VK_FEATURE_1_2(FEATURE_EXTENSION);
+ }
+ if (instance_version < VK_API_VERSION_1_3) {
+ FOR_EACH_VK_FEATURE_1_3(FEATURE_EXTENSION);
}
- VkPhysicalDeviceShaderDemoteToHelperInvocationFeatures demote{};
- demote.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_DEMOTE_TO_HELPER_INVOCATION_FEATURES;
- demote.pNext = nullptr;
- VkPhysicalDeviceVariablePointerFeatures variable_pointers{};
- variable_pointers.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VARIABLE_POINTERS_FEATURES;
- variable_pointers.pNext = &demote;
+ FOR_EACH_VK_FEATURE_EXT(FEATURE_EXTENSION);
+ FOR_EACH_VK_EXTENSION(EXTENSION);
+#ifdef _WIN32
+ FOR_EACH_VK_EXTENSION_WIN32(EXTENSION);
+#endif
- VkPhysicalDeviceRobustness2FeaturesEXT robustness2{};
- robustness2.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_ROBUSTNESS_2_FEATURES_EXT;
- robustness2.pNext = &variable_pointers;
+#undef FEATURE_EXTENSION
+#undef EXTENSION
- VkPhysicalDeviceTimelineSemaphoreFeatures timeline_semaphore{};
- timeline_semaphore.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_TIMELINE_SEMAPHORE_FEATURES;
- timeline_semaphore.pNext = &robustness2;
+ // Some extensions are mandatory. Check those.
+#define CHECK_EXTENSION(extension_name) \
+ if (!loaded_extensions.contains(extension_name)) { \
+ LOG_ERROR(Render_Vulkan, "Missing required extension {}", extension_name); \
+ suitable = false; \
+ }
- VkPhysicalDevice16BitStorageFeatures bit16_storage{};
- bit16_storage.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_16BIT_STORAGE_FEATURES;
- bit16_storage.pNext = &timeline_semaphore;
+#define LOG_EXTENSION(extension_name) \
+ if (!loaded_extensions.contains(extension_name)) { \
+ LOG_INFO(Render_Vulkan, "Device doesn't support extension {}", extension_name); \
+ }
- VkPhysicalDevice8BitStorageFeatures bit8_storage{};
- bit8_storage.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_8BIT_STORAGE_FEATURES;
- bit8_storage.pNext = &bit16_storage;
+ FOR_EACH_VK_RECOMMENDED_EXTENSION(LOG_EXTENSION);
+ FOR_EACH_VK_MANDATORY_EXTENSION(CHECK_EXTENSION);
+#ifdef _WIN32
+ FOR_EACH_VK_MANDATORY_EXTENSION_WIN32(CHECK_EXTENSION);
+#else
+ FOR_EACH_VK_MANDATORY_EXTENSION_GENERIC(CHECK_EXTENSION);
+#endif
- VkPhysicalDeviceHostQueryResetFeatures host_query_reset{};
- host_query_reset.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_HOST_QUERY_RESET_FEATURES;
- host_query_reset.pNext = &bit8_storage;
+ if (requires_swapchain) {
+ CHECK_EXTENSION(VK_KHR_SWAPCHAIN_EXTENSION_NAME);
+ }
- VkPhysicalDeviceShaderDrawParametersFeatures draw_parameters{};
- draw_parameters.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_DRAW_PARAMETER_FEATURES;
- draw_parameters.pNext = &host_query_reset;
+#undef LOG_EXTENSION
+#undef CHECK_EXTENSION
- VkPhysicalDeviceFeatures2 features2{};
+ // Generate the linked list of features to test.
features2.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2;
- features2.pNext = &draw_parameters;
- physical.GetFeatures2(features2);
+ // Set next pointer.
+ void** next = &features2.pNext;
- const VkPhysicalDeviceFeatures& features{features2.features};
- std::array feature_report{
- std::make_pair(features.robustBufferAccess, "robustBufferAccess"),
- std::make_pair(features.vertexPipelineStoresAndAtomics, "vertexPipelineStoresAndAtomics"),
- std::make_pair(features.imageCubeArray, "imageCubeArray"),
- std::make_pair(features.independentBlend, "independentBlend"),
- std::make_pair(features.multiDrawIndirect, "multiDrawIndirect"),
- std::make_pair(features.drawIndirectFirstInstance, "drawIndirectFirstInstance"),
- std::make_pair(features.depthClamp, "depthClamp"),
- std::make_pair(features.samplerAnisotropy, "samplerAnisotropy"),
- std::make_pair(features.largePoints, "largePoints"),
- std::make_pair(features.multiViewport, "multiViewport"),
- std::make_pair(features.depthBiasClamp, "depthBiasClamp"),
- std::make_pair(features.fillModeNonSolid, "fillModeNonSolid"),
- std::make_pair(features.wideLines, "wideLines"),
- std::make_pair(features.geometryShader, "geometryShader"),
- std::make_pair(features.tessellationShader, "tessellationShader"),
- std::make_pair(features.sampleRateShading, "sampleRateShading"),
- std::make_pair(features.dualSrcBlend, "dualSrcBlend"),
- std::make_pair(features.logicOp, "logicOp"),
- std::make_pair(features.occlusionQueryPrecise, "occlusionQueryPrecise"),
- std::make_pair(features.fragmentStoresAndAtomics, "fragmentStoresAndAtomics"),
- std::make_pair(features.shaderImageGatherExtended, "shaderImageGatherExtended"),
- std::make_pair(features.shaderStorageImageWriteWithoutFormat,
- "shaderStorageImageWriteWithoutFormat"),
- std::make_pair(features.shaderClipDistance, "shaderClipDistance"),
- std::make_pair(features.shaderCullDistance, "shaderCullDistance"),
- std::make_pair(variable_pointers.variablePointers, "variablePointers"),
- std::make_pair(variable_pointers.variablePointersStorageBuffer,
- "variablePointersStorageBuffer"),
- std::make_pair(robustness2.robustBufferAccess2, "robustBufferAccess2"),
- std::make_pair(robustness2.robustImageAccess2, "robustImageAccess2"),
- std::make_pair(robustness2.nullDescriptor, "nullDescriptor"),
- std::make_pair(demote.shaderDemoteToHelperInvocation, "shaderDemoteToHelperInvocation"),
- std::make_pair(timeline_semaphore.timelineSemaphore, "timelineSemaphore"),
- std::make_pair(bit16_storage.storageBuffer16BitAccess, "storageBuffer16BitAccess"),
- std::make_pair(bit16_storage.uniformAndStorageBuffer16BitAccess,
- "uniformAndStorageBuffer16BitAccess"),
- std::make_pair(bit8_storage.storageBuffer8BitAccess, "storageBuffer8BitAccess"),
- std::make_pair(bit8_storage.uniformAndStorageBuffer8BitAccess,
- "uniformAndStorageBuffer8BitAccess"),
- std::make_pair(host_query_reset.hostQueryReset, "hostQueryReset"),
- std::make_pair(draw_parameters.shaderDrawParameters, "shaderDrawParameters"),
- };
+ // Test all features we know about. If the feature is not available in core at our
+ // current API version, and was not enabled by an extension, skip testing the feature.
+ // We set the structure sType explicitly here as it is zeroed by the constructor.
+#define FEATURE(prefix, struct_name, macro_name, var_name) \
+ features.var_name.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_##macro_name##_FEATURES; \
+ SetNext(next, features.var_name);
- bool has_all_required_features = true;
- for (const auto& [is_supported, name] : feature_report) {
- if (!is_supported) {
- LOG_ERROR(Render_Vulkan, "Missing required feature: {}", name);
- has_all_required_features = false;
- }
+#define EXT_FEATURE(prefix, struct_name, macro_name, var_name) \
+ if (extensions.var_name) { \
+ features.var_name.sType = \
+ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_##macro_name##_FEATURES_##prefix; \
+ SetNext(next, features.var_name); \
}
- if (!has_all_required_features) {
- throw vk::Exception(VK_ERROR_FEATURE_NOT_PRESENT);
+ FOR_EACH_VK_FEATURE_1_1(FEATURE);
+ FOR_EACH_VK_FEATURE_EXT(EXT_FEATURE);
+ if (instance_version >= VK_API_VERSION_1_2) {
+ FOR_EACH_VK_FEATURE_1_2(FEATURE);
+ } else {
+ FOR_EACH_VK_FEATURE_1_2(EXT_FEATURE);
}
-}
-
-std::vector<const char*> Device::LoadExtensions(bool requires_surface) {
- std::vector<const char*> extensions = ExtensionsRequiredForInstanceVersion(instance_version);
- if (requires_surface) {
- extensions.push_back(VK_KHR_SWAPCHAIN_EXTENSION_NAME);
+ if (instance_version >= VK_API_VERSION_1_3) {
+ FOR_EACH_VK_FEATURE_1_3(FEATURE);
+ } else {
+ FOR_EACH_VK_FEATURE_1_3(EXT_FEATURE);
}
- bool has_khr_shader_float16_int8{};
- bool has_khr_workgroup_memory_explicit_layout{};
- bool has_khr_pipeline_executable_properties{};
- bool has_khr_image_format_list{};
- bool has_khr_swapchain_mutable_format{};
- bool has_ext_subgroup_size_control{};
- bool has_ext_transform_feedback{};
- bool has_ext_custom_border_color{};
- bool has_ext_extended_dynamic_state{};
- bool has_ext_extended_dynamic_state_2{};
- bool has_ext_extended_dynamic_state_3{};
- bool has_ext_shader_atomic_int64{};
- bool has_ext_provoking_vertex{};
- bool has_ext_vertex_input_dynamic_state{};
- bool has_ext_line_rasterization{};
- bool has_ext_primitive_topology_list_restart{};
- bool has_ext_depth_clip_control{};
- for (const std::string& extension : supported_extensions) {
- const auto test = [&](std::optional<std::reference_wrapper<bool>> status, const char* name,
- bool push) {
- if (extension != name) {
- return;
- }
- if (push) {
- extensions.push_back(name);
- }
- if (status) {
- status->get() = true;
- }
- };
- test(nv_viewport_swizzle, VK_NV_VIEWPORT_SWIZZLE_EXTENSION_NAME, true);
- test(nv_viewport_array2, VK_NV_VIEWPORT_ARRAY2_EXTENSION_NAME, true);
- test(nv_geometry_shader_passthrough, VK_NV_GEOMETRY_SHADER_PASSTHROUGH_EXTENSION_NAME,
- true);
- test(khr_uniform_buffer_standard_layout,
- VK_KHR_UNIFORM_BUFFER_STANDARD_LAYOUT_EXTENSION_NAME, true);
- test(khr_spirv_1_4, VK_KHR_SPIRV_1_4_EXTENSION_NAME, true);
- test(khr_push_descriptor, VK_KHR_PUSH_DESCRIPTOR_EXTENSION_NAME, true);
- test(has_khr_shader_float16_int8, VK_KHR_SHADER_FLOAT16_INT8_EXTENSION_NAME, false);
- test(khr_draw_indirect_count, VK_KHR_DRAW_INDIRECT_COUNT_EXTENSION_NAME, true);
- test(ext_depth_range_unrestricted, VK_EXT_DEPTH_RANGE_UNRESTRICTED_EXTENSION_NAME, true);
- test(ext_index_type_uint8, VK_EXT_INDEX_TYPE_UINT8_EXTENSION_NAME, true);
- test(has_ext_primitive_topology_list_restart,
- VK_EXT_PRIMITIVE_TOPOLOGY_LIST_RESTART_EXTENSION_NAME, true);
- test(ext_sampler_filter_minmax, VK_EXT_SAMPLER_FILTER_MINMAX_EXTENSION_NAME, true);
- test(ext_shader_viewport_index_layer, VK_EXT_SHADER_VIEWPORT_INDEX_LAYER_EXTENSION_NAME,
- true);
- test(ext_tooling_info, VK_EXT_TOOLING_INFO_EXTENSION_NAME, true);
- test(ext_shader_stencil_export, VK_EXT_SHADER_STENCIL_EXPORT_EXTENSION_NAME, true);
- test(ext_conservative_rasterization, VK_EXT_CONSERVATIVE_RASTERIZATION_EXTENSION_NAME,
- true);
- test(has_ext_depth_clip_control, VK_EXT_DEPTH_CLIP_CONTROL_EXTENSION_NAME, false);
- test(has_ext_transform_feedback, VK_EXT_TRANSFORM_FEEDBACK_EXTENSION_NAME, false);
- test(has_ext_custom_border_color, VK_EXT_CUSTOM_BORDER_COLOR_EXTENSION_NAME, false);
- test(has_ext_extended_dynamic_state, VK_EXT_EXTENDED_DYNAMIC_STATE_EXTENSION_NAME, false);
- test(has_ext_extended_dynamic_state_2, VK_EXT_EXTENDED_DYNAMIC_STATE_2_EXTENSION_NAME,
- false);
- test(has_ext_extended_dynamic_state_3, VK_EXT_EXTENDED_DYNAMIC_STATE_3_EXTENSION_NAME,
- false);
- test(has_ext_subgroup_size_control, VK_EXT_SUBGROUP_SIZE_CONTROL_EXTENSION_NAME, true);
- test(has_ext_provoking_vertex, VK_EXT_PROVOKING_VERTEX_EXTENSION_NAME, false);
- test(has_ext_vertex_input_dynamic_state, VK_EXT_VERTEX_INPUT_DYNAMIC_STATE_EXTENSION_NAME,
- false);
- test(has_ext_shader_atomic_int64, VK_KHR_SHADER_ATOMIC_INT64_EXTENSION_NAME, false);
- test(has_khr_workgroup_memory_explicit_layout,
- VK_KHR_WORKGROUP_MEMORY_EXPLICIT_LAYOUT_EXTENSION_NAME, false);
- test(has_khr_image_format_list, VK_KHR_IMAGE_FORMAT_LIST_EXTENSION_NAME, false);
- test(has_khr_swapchain_mutable_format, VK_KHR_SWAPCHAIN_MUTABLE_FORMAT_EXTENSION_NAME,
- false);
- test(has_ext_line_rasterization, VK_EXT_LINE_RASTERIZATION_EXTENSION_NAME, false);
- test(ext_memory_budget, VK_EXT_MEMORY_BUDGET_EXTENSION_NAME, true);
- if (Settings::values.enable_nsight_aftermath) {
- test(nv_device_diagnostics_config, VK_NV_DEVICE_DIAGNOSTICS_CONFIG_EXTENSION_NAME,
- true);
- }
- if (Settings::values.renderer_shader_feedback) {
- test(has_khr_pipeline_executable_properties,
- VK_KHR_PIPELINE_EXECUTABLE_PROPERTIES_EXTENSION_NAME, false);
- }
- }
- VkPhysicalDeviceFeatures2 features{};
- features.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2;
-
- VkPhysicalDeviceProperties2 physical_properties{};
- physical_properties.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2;
-
- if (has_khr_shader_float16_int8) {
- VkPhysicalDeviceShaderFloat16Int8Features float16_int8_features;
- float16_int8_features.sType =
- VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_FLOAT16_INT8_FEATURES;
- float16_int8_features.pNext = nullptr;
- features.pNext = &float16_int8_features;
-
- physical.GetFeatures2(features);
- is_float16_supported = float16_int8_features.shaderFloat16;
- is_int8_supported = float16_int8_features.shaderInt8;
- extensions.push_back(VK_KHR_SHADER_FLOAT16_INT8_EXTENSION_NAME);
- }
- if (has_ext_subgroup_size_control) {
- VkPhysicalDeviceSubgroupSizeControlFeatures subgroup_features;
- subgroup_features.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SUBGROUP_SIZE_CONTROL_FEATURES;
- subgroup_features.pNext = nullptr;
- features.pNext = &subgroup_features;
- physical.GetFeatures2(features);
-
- VkPhysicalDeviceSubgroupSizeControlProperties subgroup_properties;
- subgroup_properties.sType =
- VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SUBGROUP_SIZE_CONTROL_PROPERTIES;
- subgroup_properties.pNext = nullptr;
- physical_properties.pNext = &subgroup_properties;
- physical.GetProperties2(physical_properties);
+#undef EXT_FEATURE
+#undef FEATURE
- is_warp_potentially_bigger = subgroup_properties.maxSubgroupSize > GuestWarpSize;
+ // Perform the feature test.
+ physical.GetFeatures2(features2);
+ features.features = features2.features;
- if (subgroup_features.subgroupSizeControl &&
- subgroup_properties.minSubgroupSize <= GuestWarpSize &&
- subgroup_properties.maxSubgroupSize >= GuestWarpSize) {
- extensions.push_back(VK_EXT_SUBGROUP_SIZE_CONTROL_EXTENSION_NAME);
- guest_warp_stages = subgroup_properties.requiredSubgroupSizeStages;
- ext_subgroup_size_control = true;
- }
- } else {
- is_warp_potentially_bigger = true;
- }
- if (has_ext_provoking_vertex) {
- VkPhysicalDeviceProvokingVertexFeaturesEXT provoking_vertex;
- provoking_vertex.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROVOKING_VERTEX_FEATURES_EXT;
- provoking_vertex.pNext = nullptr;
- features.pNext = &provoking_vertex;
- physical.GetFeatures2(features);
-
- if (provoking_vertex.provokingVertexLast &&
- provoking_vertex.transformFeedbackPreservesProvokingVertex) {
- extensions.push_back(VK_EXT_PROVOKING_VERTEX_EXTENSION_NAME);
- ext_provoking_vertex = true;
- }
+ // Some features are mandatory. Check those.
+#define CHECK_FEATURE(feature, name) \
+ if (!features.feature.name) { \
+ LOG_ERROR(Render_Vulkan, "Missing required feature {}", #name); \
+ suitable = false; \
}
- if (has_ext_vertex_input_dynamic_state) {
- VkPhysicalDeviceVertexInputDynamicStateFeaturesEXT vertex_input;
- vertex_input.sType =
- VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VERTEX_INPUT_DYNAMIC_STATE_FEATURES_EXT;
- vertex_input.pNext = nullptr;
- features.pNext = &vertex_input;
- physical.GetFeatures2(features);
-
- if (vertex_input.vertexInputDynamicState) {
- extensions.push_back(VK_EXT_VERTEX_INPUT_DYNAMIC_STATE_EXTENSION_NAME);
- ext_vertex_input_dynamic_state = true;
- }
- }
- if (has_ext_shader_atomic_int64) {
- VkPhysicalDeviceShaderAtomicInt64Features atomic_int64;
- atomic_int64.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_ATOMIC_INT64_FEATURES;
- atomic_int64.pNext = nullptr;
- features.pNext = &atomic_int64;
- physical.GetFeatures2(features);
-
- if (atomic_int64.shaderBufferInt64Atomics && atomic_int64.shaderSharedInt64Atomics) {
- extensions.push_back(VK_KHR_SHADER_ATOMIC_INT64_EXTENSION_NAME);
- ext_shader_atomic_int64 = true;
- }
- }
- if (has_ext_transform_feedback) {
- VkPhysicalDeviceTransformFeedbackFeaturesEXT tfb_features;
- tfb_features.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_TRANSFORM_FEEDBACK_FEATURES_EXT;
- tfb_features.pNext = nullptr;
- features.pNext = &tfb_features;
- physical.GetFeatures2(features);
-
- VkPhysicalDeviceTransformFeedbackPropertiesEXT tfb_properties;
- tfb_properties.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_TRANSFORM_FEEDBACK_PROPERTIES_EXT;
- tfb_properties.pNext = nullptr;
- physical_properties.pNext = &tfb_properties;
- physical.GetProperties2(physical_properties);
- if (tfb_features.transformFeedback && tfb_features.geometryStreams &&
- tfb_properties.maxTransformFeedbackStreams >= 4 &&
- tfb_properties.maxTransformFeedbackBuffers && tfb_properties.transformFeedbackQueries &&
- tfb_properties.transformFeedbackDraw) {
- extensions.push_back(VK_EXT_TRANSFORM_FEEDBACK_EXTENSION_NAME);
- ext_transform_feedback = true;
- }
- }
- if (has_ext_custom_border_color) {
- VkPhysicalDeviceCustomBorderColorFeaturesEXT border_features;
- border_features.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_CUSTOM_BORDER_COLOR_FEATURES_EXT;
- border_features.pNext = nullptr;
- features.pNext = &border_features;
- physical.GetFeatures2(features);
-
- if (border_features.customBorderColors && border_features.customBorderColorWithoutFormat) {
- extensions.push_back(VK_EXT_CUSTOM_BORDER_COLOR_EXTENSION_NAME);
- ext_custom_border_color = true;
- }
- }
- if (has_ext_extended_dynamic_state) {
- VkPhysicalDeviceExtendedDynamicStateFeaturesEXT extended_dynamic_state;
- extended_dynamic_state.sType =
- VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTENDED_DYNAMIC_STATE_FEATURES_EXT;
- extended_dynamic_state.pNext = nullptr;
- features.pNext = &extended_dynamic_state;
- physical.GetFeatures2(features);
-
- if (extended_dynamic_state.extendedDynamicState) {
- extensions.push_back(VK_EXT_EXTENDED_DYNAMIC_STATE_EXTENSION_NAME);
- ext_extended_dynamic_state = true;
- }
- }
- if (has_ext_extended_dynamic_state_2) {
- VkPhysicalDeviceExtendedDynamicState2FeaturesEXT extended_dynamic_state_2;
- extended_dynamic_state_2.sType =
- VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTENDED_DYNAMIC_STATE_2_FEATURES_EXT;
- extended_dynamic_state_2.pNext = nullptr;
- features.pNext = &extended_dynamic_state_2;
- physical.GetFeatures2(features);
-
- if (extended_dynamic_state_2.extendedDynamicState2) {
- extensions.push_back(VK_EXT_EXTENDED_DYNAMIC_STATE_2_EXTENSION_NAME);
- ext_extended_dynamic_state_2 = true;
- ext_extended_dynamic_state_2_extra =
- extended_dynamic_state_2.extendedDynamicState2LogicOp;
- }
+#define LOG_FEATURE(feature, name) \
+ if (!features.feature.name) { \
+ LOG_INFO(Render_Vulkan, "Device doesn't support feature {}", #name); \
}
- if (has_ext_extended_dynamic_state_3) {
- VkPhysicalDeviceExtendedDynamicState3FeaturesEXT extended_dynamic_state_3;
- extended_dynamic_state_3.sType =
- VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTENDED_DYNAMIC_STATE_3_FEATURES_EXT;
- extended_dynamic_state_3.pNext = nullptr;
- features.pNext = &extended_dynamic_state_3;
- physical.GetFeatures2(features);
-
- ext_extended_dynamic_state_3_blend =
- extended_dynamic_state_3.extendedDynamicState3ColorBlendEnable &&
- extended_dynamic_state_3.extendedDynamicState3ColorBlendEquation &&
- extended_dynamic_state_3.extendedDynamicState3ColorWriteMask;
-
- ext_extended_dynamic_state_3_enables =
- extended_dynamic_state_3.extendedDynamicState3DepthClampEnable &&
- extended_dynamic_state_3.extendedDynamicState3LogicOpEnable;
-
- ext_extended_dynamic_state_3 =
- ext_extended_dynamic_state_3_blend || ext_extended_dynamic_state_3_enables;
- if (ext_extended_dynamic_state_3) {
- extensions.push_back(VK_EXT_EXTENDED_DYNAMIC_STATE_3_EXTENSION_NAME);
- }
+
+ FOR_EACH_VK_RECOMMENDED_FEATURE(LOG_FEATURE);
+ FOR_EACH_VK_MANDATORY_FEATURE(CHECK_FEATURE);
+
+#undef LOG_FEATURE
+#undef CHECK_FEATURE
+
+ // Generate linked list of properties.
+ properties2.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2;
+
+ // Set next pointer.
+ next = &properties2.pNext;
+
+ // Get driver info.
+ properties.driver.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DRIVER_PROPERTIES;
+ SetNext(next, properties.driver);
+
+ // Retrieve relevant extension properties.
+ if (extensions.shader_float_controls) {
+ properties.float_controls.sType =
+ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FLOAT_CONTROLS_PROPERTIES;
+ SetNext(next, properties.float_controls);
}
- if (has_ext_line_rasterization) {
- VkPhysicalDeviceLineRasterizationFeaturesEXT line_raster;
- line_raster.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_LINE_RASTERIZATION_FEATURES_EXT;
- line_raster.pNext = nullptr;
- features.pNext = &line_raster;
- physical.GetFeatures2(features);
- if (line_raster.rectangularLines && line_raster.smoothLines) {
- extensions.push_back(VK_EXT_LINE_RASTERIZATION_EXTENSION_NAME);
- ext_line_rasterization = true;
- }
+ if (extensions.push_descriptor) {
+ properties.push_descriptor.sType =
+ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PUSH_DESCRIPTOR_PROPERTIES_KHR;
+ SetNext(next, properties.push_descriptor);
}
- if (has_ext_depth_clip_control) {
- VkPhysicalDeviceDepthClipControlFeaturesEXT depth_clip_control_features;
- depth_clip_control_features.sType =
- VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DEPTH_CLIP_CONTROL_FEATURES_EXT;
- depth_clip_control_features.pNext = nullptr;
- features.pNext = &depth_clip_control_features;
- physical.GetFeatures2(features);
-
- if (depth_clip_control_features.depthClipControl) {
- extensions.push_back(VK_EXT_DEPTH_CLIP_CONTROL_EXTENSION_NAME);
- ext_depth_clip_control = true;
- }
+ if (extensions.subgroup_size_control) {
+ properties.subgroup_size_control.sType =
+ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SUBGROUP_SIZE_CONTROL_PROPERTIES;
+ SetNext(next, properties.subgroup_size_control);
}
- if (has_khr_workgroup_memory_explicit_layout) {
- VkPhysicalDeviceWorkgroupMemoryExplicitLayoutFeaturesKHR layout;
- layout.sType =
- VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_WORKGROUP_MEMORY_EXPLICIT_LAYOUT_FEATURES_KHR;
- layout.pNext = nullptr;
- features.pNext = &layout;
- physical.GetFeatures2(features);
-
- if (layout.workgroupMemoryExplicitLayout &&
- layout.workgroupMemoryExplicitLayout8BitAccess &&
- layout.workgroupMemoryExplicitLayout16BitAccess &&
- layout.workgroupMemoryExplicitLayoutScalarBlockLayout) {
- extensions.push_back(VK_KHR_WORKGROUP_MEMORY_EXPLICIT_LAYOUT_EXTENSION_NAME);
- khr_workgroup_memory_explicit_layout = true;
- }
+ if (extensions.transform_feedback) {
+ properties.transform_feedback.sType =
+ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_TRANSFORM_FEEDBACK_PROPERTIES_EXT;
+ SetNext(next, properties.transform_feedback);
}
- if (has_khr_pipeline_executable_properties) {
- VkPhysicalDevicePipelineExecutablePropertiesFeaturesKHR executable_properties;
- executable_properties.sType =
- VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PIPELINE_EXECUTABLE_PROPERTIES_FEATURES_KHR;
- executable_properties.pNext = nullptr;
- features.pNext = &executable_properties;
- physical.GetFeatures2(features);
-
- if (executable_properties.pipelineExecutableInfo) {
- extensions.push_back(VK_KHR_PIPELINE_EXECUTABLE_PROPERTIES_EXTENSION_NAME);
- khr_pipeline_executable_properties = true;
+
+ // Perform the property fetch.
+ physical.GetProperties2(properties2);
+ properties.properties = properties2.properties;
+
+ // Unload extensions if feature support is insufficient.
+ RemoveUnsuitableExtensions();
+
+ // Check limits.
+ struct Limit {
+ u32 minimum;
+ u32 value;
+ const char* name;
+ };
+
+ const VkPhysicalDeviceLimits& limits{properties.properties.limits};
+ const std::array limits_report{
+ Limit{65536, limits.maxUniformBufferRange, "maxUniformBufferRange"},
+ Limit{16, limits.maxViewports, "maxViewports"},
+ Limit{8, limits.maxColorAttachments, "maxColorAttachments"},
+ Limit{8, limits.maxClipDistances, "maxClipDistances"},
+ };
+
+ for (const auto& [min, value, name] : limits_report) {
+ if (value < min) {
+ LOG_ERROR(Render_Vulkan, "{} has to be {} or greater but it is {}", name, min, value);
+ suitable = false;
}
}
- if (has_ext_primitive_topology_list_restart) {
- VkPhysicalDevicePrimitiveTopologyListRestartFeaturesEXT primitive_topology_list_restart{};
- primitive_topology_list_restart.sType =
- VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PRIMITIVE_TOPOLOGY_LIST_RESTART_FEATURES_EXT;
- primitive_topology_list_restart.pNext = nullptr;
- features.pNext = &primitive_topology_list_restart;
- physical.GetFeatures2(features);
-
- is_topology_list_restart_supported =
- primitive_topology_list_restart.primitiveTopologyListRestart;
- is_patch_list_restart_supported =
- primitive_topology_list_restart.primitiveTopologyPatchListRestart;
- }
- if (requires_surface && has_khr_image_format_list && has_khr_swapchain_mutable_format) {
- extensions.push_back(VK_KHR_IMAGE_FORMAT_LIST_EXTENSION_NAME);
- extensions.push_back(VK_KHR_SWAPCHAIN_MUTABLE_FORMAT_EXTENSION_NAME);
- khr_swapchain_mutable_format = true;
- }
- if (khr_push_descriptor) {
- VkPhysicalDevicePushDescriptorPropertiesKHR push_descriptor;
- push_descriptor.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PUSH_DESCRIPTOR_PROPERTIES_KHR;
- push_descriptor.pNext = nullptr;
- physical_properties.pNext = &push_descriptor;
- physical.GetProperties2(physical_properties);
+ // Return whether we were suitable.
+ return suitable;
+}
- max_push_descriptors = push_descriptor.maxPushDescriptors;
+void Device::RemoveExtensionIfUnsuitable(bool is_suitable, const std::string& extension_name) {
+ if (loaded_extensions.contains(extension_name) && !is_suitable) {
+ LOG_WARNING(Render_Vulkan, "Removing unsuitable extension {}", extension_name);
+ loaded_extensions.erase(extension_name);
}
+}
- has_null_descriptor = true;
-
- return extensions;
+void Device::RemoveUnsuitableExtensions() {
+ // VK_EXT_custom_border_color
+ extensions.custom_border_color = features.custom_border_color.customBorderColors &&
+ features.custom_border_color.customBorderColorWithoutFormat;
+ RemoveExtensionIfUnsuitable(extensions.custom_border_color,
+ VK_EXT_CUSTOM_BORDER_COLOR_EXTENSION_NAME);
+
+ // VK_EXT_depth_clip_control
+ extensions.depth_clip_control = features.depth_clip_control.depthClipControl;
+ RemoveExtensionIfUnsuitable(extensions.depth_clip_control,
+ VK_EXT_DEPTH_CLIP_CONTROL_EXTENSION_NAME);
+
+ // VK_EXT_extended_dynamic_state
+ extensions.extended_dynamic_state = features.extended_dynamic_state.extendedDynamicState;
+ RemoveExtensionIfUnsuitable(extensions.extended_dynamic_state,
+ VK_EXT_EXTENDED_DYNAMIC_STATE_EXTENSION_NAME);
+
+ // VK_EXT_extended_dynamic_state2
+ extensions.extended_dynamic_state2 = features.extended_dynamic_state2.extendedDynamicState2;
+ RemoveExtensionIfUnsuitable(extensions.extended_dynamic_state2,
+ VK_EXT_EXTENDED_DYNAMIC_STATE_2_EXTENSION_NAME);
+
+ // VK_EXT_extended_dynamic_state3
+ dynamic_state3_blending =
+ features.extended_dynamic_state3.extendedDynamicState3ColorBlendEnable &&
+ features.extended_dynamic_state3.extendedDynamicState3ColorBlendEquation &&
+ features.extended_dynamic_state3.extendedDynamicState3ColorWriteMask;
+ dynamic_state3_enables =
+ features.extended_dynamic_state3.extendedDynamicState3DepthClampEnable &&
+ features.extended_dynamic_state3.extendedDynamicState3LogicOpEnable;
+
+ extensions.extended_dynamic_state3 = dynamic_state3_blending || dynamic_state3_enables;
+ dynamic_state3_blending = dynamic_state3_blending && extensions.extended_dynamic_state3;
+ dynamic_state3_enables = dynamic_state3_enables && extensions.extended_dynamic_state3;
+ RemoveExtensionIfUnsuitable(extensions.extended_dynamic_state3,
+ VK_EXT_EXTENDED_DYNAMIC_STATE_3_EXTENSION_NAME);
+
+ // VK_EXT_provoking_vertex
+ extensions.provoking_vertex =
+ features.provoking_vertex.provokingVertexLast &&
+ features.provoking_vertex.transformFeedbackPreservesProvokingVertex;
+ RemoveExtensionIfUnsuitable(extensions.provoking_vertex,
+ VK_EXT_PROVOKING_VERTEX_EXTENSION_NAME);
+
+ // VK_KHR_shader_atomic_int64
+ extensions.shader_atomic_int64 = features.shader_atomic_int64.shaderBufferInt64Atomics &&
+ features.shader_atomic_int64.shaderSharedInt64Atomics;
+ RemoveExtensionIfUnsuitable(extensions.shader_atomic_int64,
+ VK_KHR_SHADER_ATOMIC_INT64_EXTENSION_NAME);
+
+ // VK_EXT_shader_demote_to_helper_invocation
+ extensions.shader_demote_to_helper_invocation =
+ features.shader_demote_to_helper_invocation.shaderDemoteToHelperInvocation;
+ RemoveExtensionIfUnsuitable(extensions.shader_demote_to_helper_invocation,
+ VK_EXT_SHADER_DEMOTE_TO_HELPER_INVOCATION_EXTENSION_NAME);
+
+ // VK_EXT_subgroup_size_control
+ extensions.subgroup_size_control =
+ features.subgroup_size_control.subgroupSizeControl &&
+ properties.subgroup_size_control.minSubgroupSize <= GuestWarpSize &&
+ properties.subgroup_size_control.maxSubgroupSize >= GuestWarpSize;
+ RemoveExtensionIfUnsuitable(extensions.subgroup_size_control,
+ VK_EXT_SUBGROUP_SIZE_CONTROL_EXTENSION_NAME);
+
+ // VK_EXT_transform_feedback
+ extensions.transform_feedback =
+ features.transform_feedback.transformFeedback &&
+ features.transform_feedback.geometryStreams &&
+ properties.transform_feedback.maxTransformFeedbackStreams >= 4 &&
+ properties.transform_feedback.maxTransformFeedbackBuffers > 0 &&
+ properties.transform_feedback.transformFeedbackQueries &&
+ properties.transform_feedback.transformFeedbackDraw;
+ RemoveExtensionIfUnsuitable(extensions.transform_feedback,
+ VK_EXT_TRANSFORM_FEEDBACK_EXTENSION_NAME);
+
+ // VK_EXT_vertex_input_dynamic_state
+ extensions.vertex_input_dynamic_state =
+ features.vertex_input_dynamic_state.vertexInputDynamicState;
+ RemoveExtensionIfUnsuitable(extensions.vertex_input_dynamic_state,
+ VK_EXT_VERTEX_INPUT_DYNAMIC_STATE_EXTENSION_NAME);
+
+ // VK_KHR_pipeline_executable_properties
+ if (Settings::values.renderer_shader_feedback.GetValue()) {
+ extensions.pipeline_executable_properties =
+ features.pipeline_executable_properties.pipelineExecutableInfo;
+ RemoveExtensionIfUnsuitable(extensions.pipeline_executable_properties,
+ VK_KHR_PIPELINE_EXECUTABLE_PROPERTIES_EXTENSION_NAME);
+ } else {
+ extensions.pipeline_executable_properties = false;
+ loaded_extensions.erase(VK_KHR_PIPELINE_EXECUTABLE_PROPERTIES_EXTENSION_NAME);
+ }
+
+ // VK_KHR_workgroup_memory_explicit_layout
+ extensions.workgroup_memory_explicit_layout =
+ features.features.shaderInt16 &&
+ features.workgroup_memory_explicit_layout.workgroupMemoryExplicitLayout &&
+ features.workgroup_memory_explicit_layout.workgroupMemoryExplicitLayout8BitAccess &&
+ features.workgroup_memory_explicit_layout.workgroupMemoryExplicitLayout16BitAccess &&
+ features.workgroup_memory_explicit_layout.workgroupMemoryExplicitLayoutScalarBlockLayout;
+ RemoveExtensionIfUnsuitable(extensions.workgroup_memory_explicit_layout,
+ VK_KHR_WORKGROUP_MEMORY_EXPLICIT_LAYOUT_EXTENSION_NAME);
}
void Device::SetupFamilies(VkSurfaceKHR surface) {
@@ -1540,53 +960,6 @@ void Device::SetupFamilies(VkSurfaceKHR surface) {
}
}
-void Device::SetupFeatures() {
- const VkPhysicalDeviceFeatures features{physical.GetFeatures()};
- is_depth_bounds_supported = features.depthBounds;
- is_formatless_image_load_supported = features.shaderStorageImageReadWithoutFormat;
- is_shader_float64_supported = features.shaderFloat64;
- is_shader_int64_supported = features.shaderInt64;
- is_shader_int16_supported = features.shaderInt16;
- is_shader_storage_image_multisample = features.shaderStorageImageMultisample;
- is_blit_depth_stencil_supported = TestDepthStencilBlits();
- is_optimal_astc_supported = IsOptimalAstcSupported(features);
-
- const VkPhysicalDeviceLimits& limits{properties.limits};
- max_vertex_input_attributes = limits.maxVertexInputAttributes;
- max_vertex_input_bindings = limits.maxVertexInputBindings;
-}
-
-void Device::SetupProperties() {
- float_controls.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FLOAT_CONTROLS_PROPERTIES;
-
- VkPhysicalDeviceProperties2KHR properties2{};
- properties2.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2;
- properties2.pNext = &float_controls;
-
- physical.GetProperties2(properties2);
-}
-
-void Device::CollectTelemetryParameters() {
- VkPhysicalDeviceDriverProperties driver{
- .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DRIVER_PROPERTIES,
- .pNext = nullptr,
- .driverID = {},
- .driverName = {},
- .driverInfo = {},
- .conformanceVersion = {},
- };
-
- VkPhysicalDeviceProperties2 device_properties{
- .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2,
- .pNext = &driver,
- .properties = {},
- };
- physical.GetProperties2(device_properties);
-
- driver_id = driver.driverID;
- vendor_name = driver.driverName;
-}
-
u64 Device::GetDeviceMemoryUsage() const {
VkPhysicalDeviceMemoryBudgetPropertiesEXT budget;
budget.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MEMORY_BUDGET_PROPERTIES_EXT;
@@ -1602,7 +975,8 @@ u64 Device::GetDeviceMemoryUsage() const {
void Device::CollectPhysicalMemoryInfo() {
VkPhysicalDeviceMemoryBudgetPropertiesEXT budget{};
budget.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MEMORY_BUDGET_PROPERTIES_EXT;
- const auto mem_info = physical.GetMemoryProperties(ext_memory_budget ? &budget : nullptr);
+ const auto mem_info =
+ physical.GetMemoryProperties(extensions.memory_budget ? &budget : nullptr);
const auto& mem_properties = mem_info.memoryProperties;
const size_t num_properties = mem_properties.memoryHeapCount;
device_access_memory = 0;
@@ -1618,7 +992,7 @@ void Device::CollectPhysicalMemoryInfo() {
if (is_heap_local) {
local_memory += mem_properties.memoryHeaps[element].size;
}
- if (ext_memory_budget) {
+ if (extensions.memory_budget) {
device_initial_usage += budget.heapUsage[element];
device_access_memory += budget.heapBudget[element];
continue;
@@ -1634,7 +1008,7 @@ void Device::CollectPhysicalMemoryInfo() {
}
void Device::CollectToolingInfo() {
- if (!ext_tooling_info) {
+ if (!extensions.tooling_info) {
return;
}
auto tools{physical.GetPhysicalDeviceToolProperties()};
diff --git a/src/video_core/vulkan_common/vulkan_device.h b/src/video_core/vulkan_common/vulkan_device.h
index 4bc267163..0662a2d9f 100644
--- a/src/video_core/vulkan_common/vulkan_device.h
+++ b/src/video_core/vulkan_common/vulkan_device.h
@@ -3,6 +3,7 @@
#pragma once
+#include <set>
#include <span>
#include <string>
#include <unordered_map>
@@ -11,6 +12,156 @@
#include "common/common_types.h"
#include "video_core/vulkan_common/vulkan_wrapper.h"
+// Define all features which may be used by the implementation here.
+// Vulkan version in the macro describes the minimum version required for feature availability.
+// If the Vulkan version is lower than the required version, the named extension is required.
+#define FOR_EACH_VK_FEATURE_1_1(FEATURE) \
+ FEATURE(EXT, SubgroupSizeControl, SUBGROUP_SIZE_CONTROL, subgroup_size_control) \
+ FEATURE(KHR, 16BitStorage, 16BIT_STORAGE, bit16_storage) \
+ FEATURE(KHR, ShaderAtomicInt64, SHADER_ATOMIC_INT64, shader_atomic_int64) \
+ FEATURE(KHR, ShaderDrawParameters, SHADER_DRAW_PARAMETERS, shader_draw_parameters) \
+ FEATURE(KHR, ShaderFloat16Int8, SHADER_FLOAT16_INT8, shader_float16_int8) \
+ FEATURE(KHR, UniformBufferStandardLayout, UNIFORM_BUFFER_STANDARD_LAYOUT, \
+ uniform_buffer_standard_layout) \
+ FEATURE(KHR, VariablePointer, VARIABLE_POINTERS, variable_pointer)
+
+#define FOR_EACH_VK_FEATURE_1_2(FEATURE) \
+ FEATURE(EXT, HostQueryReset, HOST_QUERY_RESET, host_query_reset) \
+ FEATURE(KHR, 8BitStorage, 8BIT_STORAGE, bit8_storage) \
+ FEATURE(KHR, TimelineSemaphore, TIMELINE_SEMAPHORE, timeline_semaphore)
+
+#define FOR_EACH_VK_FEATURE_1_3(FEATURE) \
+ FEATURE(EXT, ShaderDemoteToHelperInvocation, SHADER_DEMOTE_TO_HELPER_INVOCATION, \
+ shader_demote_to_helper_invocation)
+
+// Define all features which may be used by the implementation and require an extension here.
+#define FOR_EACH_VK_FEATURE_EXT(FEATURE) \
+ FEATURE(EXT, CustomBorderColor, CUSTOM_BORDER_COLOR, custom_border_color) \
+ FEATURE(EXT, DepthClipControl, DEPTH_CLIP_CONTROL, depth_clip_control) \
+ FEATURE(EXT, ExtendedDynamicState, EXTENDED_DYNAMIC_STATE, extended_dynamic_state) \
+ FEATURE(EXT, ExtendedDynamicState2, EXTENDED_DYNAMIC_STATE_2, extended_dynamic_state2) \
+ FEATURE(EXT, ExtendedDynamicState3, EXTENDED_DYNAMIC_STATE_3, extended_dynamic_state3) \
+ FEATURE(EXT, IndexTypeUint8, INDEX_TYPE_UINT8, index_type_uint8) \
+ FEATURE(EXT, LineRasterization, LINE_RASTERIZATION, line_rasterization) \
+ FEATURE(EXT, PrimitiveTopologyListRestart, PRIMITIVE_TOPOLOGY_LIST_RESTART, \
+ primitive_topology_list_restart) \
+ FEATURE(EXT, ProvokingVertex, PROVOKING_VERTEX, provoking_vertex) \
+ FEATURE(EXT, Robustness2, ROBUSTNESS_2, robustness2) \
+ FEATURE(EXT, TransformFeedback, TRANSFORM_FEEDBACK, transform_feedback) \
+ FEATURE(EXT, VertexInputDynamicState, VERTEX_INPUT_DYNAMIC_STATE, vertex_input_dynamic_state) \
+ FEATURE(KHR, PipelineExecutableProperties, PIPELINE_EXECUTABLE_PROPERTIES, \
+ pipeline_executable_properties) \
+ FEATURE(KHR, WorkgroupMemoryExplicitLayout, WORKGROUP_MEMORY_EXPLICIT_LAYOUT, \
+ workgroup_memory_explicit_layout)
+
+// Define miscellaneous extensions which may be used by the implementation here.
+#define FOR_EACH_VK_EXTENSION(EXTENSION) \
+ EXTENSION(EXT, CONSERVATIVE_RASTERIZATION, conservative_rasterization) \
+ EXTENSION(EXT, DEPTH_RANGE_UNRESTRICTED, depth_range_unrestricted) \
+ EXTENSION(EXT, MEMORY_BUDGET, memory_budget) \
+ EXTENSION(EXT, ROBUSTNESS_2, robustness_2) \
+ EXTENSION(EXT, SAMPLER_FILTER_MINMAX, sampler_filter_minmax) \
+ EXTENSION(EXT, SHADER_STENCIL_EXPORT, shader_stencil_export) \
+ EXTENSION(EXT, SHADER_VIEWPORT_INDEX_LAYER, shader_viewport_index_layer) \
+ EXTENSION(EXT, TOOLING_INFO, tooling_info) \
+ EXTENSION(EXT, VERTEX_ATTRIBUTE_DIVISOR, vertex_attribute_divisor) \
+ EXTENSION(KHR, DRAW_INDIRECT_COUNT, draw_indirect_count) \
+ EXTENSION(KHR, DRIVER_PROPERTIES, driver_properties) \
+ EXTENSION(KHR, EXTERNAL_MEMORY_FD, external_memory_fd) \
+ EXTENSION(KHR, PUSH_DESCRIPTOR, push_descriptor) \
+ EXTENSION(KHR, SAMPLER_MIRROR_CLAMP_TO_EDGE, sampler_mirror_clamp_to_edge) \
+ EXTENSION(KHR, SHADER_FLOAT_CONTROLS, shader_float_controls) \
+ EXTENSION(KHR, SPIRV_1_4, spirv_1_4) \
+ EXTENSION(KHR, SWAPCHAIN, swapchain) \
+ EXTENSION(KHR, SWAPCHAIN_MUTABLE_FORMAT, swapchain_mutable_format) \
+ EXTENSION(NV, DEVICE_DIAGNOSTICS_CONFIG, device_diagnostics_config) \
+ EXTENSION(NV, GEOMETRY_SHADER_PASSTHROUGH, geometry_shader_passthrough) \
+ EXTENSION(NV, VIEWPORT_ARRAY2, viewport_array2) \
+ EXTENSION(NV, VIEWPORT_SWIZZLE, viewport_swizzle)
+
+#define FOR_EACH_VK_EXTENSION_WIN32(EXTENSION) \
+ EXTENSION(KHR, EXTERNAL_MEMORY_WIN32, external_memory_win32)
+
+// Define extensions which must be supported.
+#define FOR_EACH_VK_MANDATORY_EXTENSION(EXTENSION_NAME) \
+ EXTENSION_NAME(VK_EXT_ROBUSTNESS_2_EXTENSION_NAME) \
+ EXTENSION_NAME(VK_EXT_VERTEX_ATTRIBUTE_DIVISOR_EXTENSION_NAME) \
+ EXTENSION_NAME(VK_KHR_DRIVER_PROPERTIES_EXTENSION_NAME) \
+ EXTENSION_NAME(VK_KHR_SAMPLER_MIRROR_CLAMP_TO_EDGE_EXTENSION_NAME) \
+ EXTENSION_NAME(VK_KHR_SHADER_FLOAT_CONTROLS_EXTENSION_NAME)
+
+#define FOR_EACH_VK_MANDATORY_EXTENSION_GENERIC(EXTENSION_NAME) \
+ EXTENSION_NAME(VK_KHR_EXTERNAL_MEMORY_FD_EXTENSION_NAME)
+
+#define FOR_EACH_VK_MANDATORY_EXTENSION_WIN32(EXTENSION_NAME) \
+ EXTENSION_NAME(VK_KHR_EXTERNAL_MEMORY_WIN32_EXTENSION_NAME)
+
+// Define extensions where the absence of the extension may result in a degraded experience.
+#define FOR_EACH_VK_RECOMMENDED_EXTENSION(EXTENSION_NAME) \
+ EXTENSION_NAME(VK_EXT_CONSERVATIVE_RASTERIZATION_EXTENSION_NAME) \
+ EXTENSION_NAME(VK_EXT_DEPTH_RANGE_UNRESTRICTED_EXTENSION_NAME) \
+ EXTENSION_NAME(VK_EXT_EXTENDED_DYNAMIC_STATE_EXTENSION_NAME) \
+ EXTENSION_NAME(VK_EXT_EXTENDED_DYNAMIC_STATE_2_EXTENSION_NAME) \
+ EXTENSION_NAME(VK_EXT_EXTENDED_DYNAMIC_STATE_3_EXTENSION_NAME) \
+ EXTENSION_NAME(VK_EXT_LINE_RASTERIZATION_EXTENSION_NAME) \
+ EXTENSION_NAME(VK_EXT_VERTEX_INPUT_DYNAMIC_STATE_EXTENSION_NAME) \
+ EXTENSION_NAME(VK_NV_GEOMETRY_SHADER_PASSTHROUGH_EXTENSION_NAME) \
+ EXTENSION_NAME(VK_NV_VIEWPORT_ARRAY2_EXTENSION_NAME) \
+ EXTENSION_NAME(VK_NV_VIEWPORT_SWIZZLE_EXTENSION_NAME)
+
+// Define features which must be supported.
+#define FOR_EACH_VK_MANDATORY_FEATURE(FEATURE_NAME) \
+ FEATURE_NAME(bit16_storage, storageBuffer16BitAccess) \
+ FEATURE_NAME(bit16_storage, uniformAndStorageBuffer16BitAccess) \
+ FEATURE_NAME(bit8_storage, storageBuffer8BitAccess) \
+ FEATURE_NAME(bit8_storage, uniformAndStorageBuffer8BitAccess) \
+ FEATURE_NAME(features, depthBiasClamp) \
+ FEATURE_NAME(features, depthClamp) \
+ FEATURE_NAME(features, drawIndirectFirstInstance) \
+ FEATURE_NAME(features, dualSrcBlend) \
+ FEATURE_NAME(features, fillModeNonSolid) \
+ FEATURE_NAME(features, fragmentStoresAndAtomics) \
+ FEATURE_NAME(features, geometryShader) \
+ FEATURE_NAME(features, imageCubeArray) \
+ FEATURE_NAME(features, independentBlend) \
+ FEATURE_NAME(features, largePoints) \
+ FEATURE_NAME(features, logicOp) \
+ FEATURE_NAME(features, multiDrawIndirect) \
+ FEATURE_NAME(features, multiViewport) \
+ FEATURE_NAME(features, occlusionQueryPrecise) \
+ FEATURE_NAME(features, robustBufferAccess) \
+ FEATURE_NAME(features, samplerAnisotropy) \
+ FEATURE_NAME(features, sampleRateShading) \
+ FEATURE_NAME(features, shaderClipDistance) \
+ FEATURE_NAME(features, shaderCullDistance) \
+ FEATURE_NAME(features, shaderImageGatherExtended) \
+ FEATURE_NAME(features, shaderStorageImageWriteWithoutFormat) \
+ FEATURE_NAME(features, tessellationShader) \
+ FEATURE_NAME(features, vertexPipelineStoresAndAtomics) \
+ FEATURE_NAME(features, wideLines) \
+ FEATURE_NAME(host_query_reset, hostQueryReset) \
+ FEATURE_NAME(robustness2, nullDescriptor) \
+ FEATURE_NAME(robustness2, robustBufferAccess2) \
+ FEATURE_NAME(robustness2, robustImageAccess2) \
+ FEATURE_NAME(shader_demote_to_helper_invocation, shaderDemoteToHelperInvocation) \
+ FEATURE_NAME(shader_draw_parameters, shaderDrawParameters) \
+ FEATURE_NAME(timeline_semaphore, timelineSemaphore) \
+ FEATURE_NAME(variable_pointer, variablePointers) \
+ FEATURE_NAME(variable_pointer, variablePointersStorageBuffer)
+
+// Define features where the absence of the feature may result in a degraded experience.
+#define FOR_EACH_VK_RECOMMENDED_FEATURE(FEATURE_NAME) \
+ FEATURE_NAME(custom_border_color, customBorderColors) \
+ FEATURE_NAME(extended_dynamic_state, extendedDynamicState) \
+ FEATURE_NAME(index_type_uint8, indexTypeUint8) \
+ FEATURE_NAME(primitive_topology_list_restart, primitiveTopologyListRestart) \
+ FEATURE_NAME(provoking_vertex, provokingVertexLast) \
+ FEATURE_NAME(shader_float16_int8, shaderFloat16) \
+ FEATURE_NAME(shader_float16_int8, shaderInt8) \
+ FEATURE_NAME(transform_feedback, transformFeedback) \
+ FEATURE_NAME(uniform_buffer_standard_layout, uniformBufferStandardLayout) \
+ FEATURE_NAME(vertex_input_dynamic_state, vertexInputDynamicState)
+
namespace Vulkan {
class NsightAftermathTracker;
@@ -88,69 +239,69 @@ public:
/// Returns the current Vulkan API version provided in Vulkan-formatted version numbers.
u32 ApiVersion() const {
- return properties.apiVersion;
+ return properties.properties.apiVersion;
}
/// Returns the current driver version provided in Vulkan-formatted version numbers.
u32 GetDriverVersion() const {
- return properties.driverVersion;
+ return properties.properties.driverVersion;
}
/// Returns the device name.
std::string_view GetModelName() const {
- return properties.deviceName;
+ return properties.properties.deviceName;
}
/// Returns the driver ID.
VkDriverIdKHR GetDriverID() const {
- return driver_id;
+ return properties.driver.driverID;
}
bool ShouldBoostClocks() const;
/// Returns uniform buffer alignment requeriment.
VkDeviceSize GetUniformBufferAlignment() const {
- return properties.limits.minUniformBufferOffsetAlignment;
+ return properties.properties.limits.minUniformBufferOffsetAlignment;
}
/// Returns storage alignment requeriment.
VkDeviceSize GetStorageBufferAlignment() const {
- return properties.limits.minStorageBufferOffsetAlignment;
+ return properties.properties.limits.minStorageBufferOffsetAlignment;
}
/// Returns the maximum range for storage buffers.
VkDeviceSize GetMaxStorageBufferRange() const {
- return properties.limits.maxStorageBufferRange;
+ return properties.properties.limits.maxStorageBufferRange;
}
/// Returns the maximum size for push constants.
VkDeviceSize GetMaxPushConstantsSize() const {
- return properties.limits.maxPushConstantsSize;
+ return properties.properties.limits.maxPushConstantsSize;
}
/// Returns the maximum size for shared memory.
u32 GetMaxComputeSharedMemorySize() const {
- return properties.limits.maxComputeSharedMemorySize;
+ return properties.properties.limits.maxComputeSharedMemorySize;
}
/// Returns float control properties of the device.
const VkPhysicalDeviceFloatControlsPropertiesKHR& FloatControlProperties() const {
- return float_controls;
+ return properties.float_controls;
}
/// Returns true if ASTC is natively supported.
bool IsOptimalAstcSupported() const {
- return is_optimal_astc_supported;
+ return features.features.textureCompressionASTC_LDR;
}
/// Returns true if the device supports float16 natively.
bool IsFloat16Supported() const {
- return is_float16_supported;
+ return features.shader_float16_int8.shaderFloat16;
}
/// Returns true if the device supports int8 natively.
bool IsInt8Supported() const {
- return is_int8_supported;
+ return features.shader_float16_int8.shaderInt8;
}
/// Returns true if the device warp size can potentially be bigger than guest's warp size.
@@ -160,32 +311,32 @@ public:
/// Returns true if the device can be forced to use the guest warp size.
bool IsGuestWarpSizeSupported(VkShaderStageFlagBits stage) const {
- return guest_warp_stages & stage;
+ return properties.subgroup_size_control.requiredSubgroupSizeStages & stage;
}
/// Returns the maximum number of push descriptors.
u32 MaxPushDescriptors() const {
- return max_push_descriptors;
+ return properties.push_descriptor.maxPushDescriptors;
}
/// Returns true if formatless image load is supported.
bool IsFormatlessImageLoadSupported() const {
- return is_formatless_image_load_supported;
+ return features.features.shaderStorageImageReadWithoutFormat;
}
/// Returns true if shader int64 is supported.
bool IsShaderInt64Supported() const {
- return is_shader_int64_supported;
+ return features.features.shaderInt64;
}
/// Returns true if shader int16 is supported.
bool IsShaderInt16Supported() const {
- return is_shader_int16_supported;
+ return features.features.shaderInt16;
}
// Returns true if depth bounds is supported.
bool IsDepthBoundsSupported() const {
- return is_depth_bounds_supported;
+ return features.features.depthBounds;
}
/// Returns true when blitting from and to depth stencil images is supported.
@@ -195,151 +346,151 @@ public:
/// Returns true if the device supports VK_NV_viewport_swizzle.
bool IsNvViewportSwizzleSupported() const {
- return nv_viewport_swizzle;
+ return extensions.viewport_swizzle;
}
/// Returns true if the device supports VK_NV_viewport_array2.
bool IsNvViewportArray2Supported() const {
- return nv_viewport_array2;
+ return extensions.viewport_array2;
}
/// Returns true if the device supports VK_NV_geometry_shader_passthrough.
bool IsNvGeometryShaderPassthroughSupported() const {
- return nv_geometry_shader_passthrough;
+ return extensions.geometry_shader_passthrough;
}
/// Returns true if the device supports VK_KHR_uniform_buffer_standard_layout.
bool IsKhrUniformBufferStandardLayoutSupported() const {
- return khr_uniform_buffer_standard_layout;
+ return extensions.uniform_buffer_standard_layout;
}
/// Returns true if the device supports VK_KHR_push_descriptor.
bool IsKhrPushDescriptorSupported() const {
- return khr_push_descriptor;
+ return extensions.push_descriptor;
}
/// Returns true if VK_KHR_pipeline_executable_properties is enabled.
bool IsKhrPipelineExecutablePropertiesEnabled() const {
- return khr_pipeline_executable_properties;
+ return extensions.pipeline_executable_properties;
}
/// Returns true if VK_KHR_swapchain_mutable_format is enabled.
bool IsKhrSwapchainMutableFormatEnabled() const {
- return khr_swapchain_mutable_format;
+ return extensions.swapchain_mutable_format;
}
/// Returns true if the device supports VK_KHR_workgroup_memory_explicit_layout.
bool IsKhrWorkgroupMemoryExplicitLayoutSupported() const {
- return khr_workgroup_memory_explicit_layout;
+ return extensions.workgroup_memory_explicit_layout;
}
/// Returns true if the device supports VK_EXT_primitive_topology_list_restart.
bool IsTopologyListPrimitiveRestartSupported() const {
- return is_topology_list_restart_supported;
+ return features.primitive_topology_list_restart.primitiveTopologyListRestart;
}
/// Returns true if the device supports VK_EXT_primitive_topology_list_restart.
bool IsPatchListPrimitiveRestartSupported() const {
- return is_patch_list_restart_supported;
+ return features.primitive_topology_list_restart.primitiveTopologyPatchListRestart;
}
/// Returns true if the device supports VK_EXT_index_type_uint8.
bool IsExtIndexTypeUint8Supported() const {
- return ext_index_type_uint8;
+ return extensions.index_type_uint8;
}
/// Returns true if the device supports VK_EXT_sampler_filter_minmax.
bool IsExtSamplerFilterMinmaxSupported() const {
- return ext_sampler_filter_minmax;
+ return extensions.sampler_filter_minmax;
}
/// Returns true if the device supports VK_EXT_depth_range_unrestricted.
bool IsExtDepthRangeUnrestrictedSupported() const {
- return ext_depth_range_unrestricted;
+ return extensions.depth_range_unrestricted;
}
/// Returns true if the device supports VK_EXT_depth_clip_control.
bool IsExtDepthClipControlSupported() const {
- return ext_depth_clip_control;
+ return extensions.depth_clip_control;
}
/// Returns true if the device supports VK_EXT_shader_viewport_index_layer.
bool IsExtShaderViewportIndexLayerSupported() const {
- return ext_shader_viewport_index_layer;
+ return extensions.shader_viewport_index_layer;
}
/// Returns true if the device supports VK_EXT_subgroup_size_control.
bool IsExtSubgroupSizeControlSupported() const {
- return ext_subgroup_size_control;
+ return extensions.subgroup_size_control;
}
/// Returns true if the device supports VK_EXT_transform_feedback.
bool IsExtTransformFeedbackSupported() const {
- return ext_transform_feedback;
+ return extensions.transform_feedback;
}
/// Returns true if the device supports VK_EXT_custom_border_color.
bool IsExtCustomBorderColorSupported() const {
- return ext_custom_border_color;
+ return extensions.custom_border_color;
}
/// Returns true if the device supports VK_EXT_extended_dynamic_state.
bool IsExtExtendedDynamicStateSupported() const {
- return ext_extended_dynamic_state;
+ return extensions.extended_dynamic_state;
}
/// Returns true if the device supports VK_EXT_extended_dynamic_state2.
bool IsExtExtendedDynamicState2Supported() const {
- return ext_extended_dynamic_state_2;
+ return extensions.extended_dynamic_state2;
}
bool IsExtExtendedDynamicState2ExtrasSupported() const {
- return ext_extended_dynamic_state_2_extra;
+ return features.extended_dynamic_state2.extendedDynamicState2LogicOp;
}
/// Returns true if the device supports VK_EXT_extended_dynamic_state3.
bool IsExtExtendedDynamicState3Supported() const {
- return ext_extended_dynamic_state_3;
+ return extensions.extended_dynamic_state3;
}
/// Returns true if the device supports VK_EXT_extended_dynamic_state3.
bool IsExtExtendedDynamicState3BlendingSupported() const {
- return ext_extended_dynamic_state_3_blend;
+ return dynamic_state3_blending;
}
/// Returns true if the device supports VK_EXT_extended_dynamic_state3.
bool IsExtExtendedDynamicState3EnablesSupported() const {
- return ext_extended_dynamic_state_3_enables;
+ return dynamic_state3_enables;
}
/// Returns true if the device supports VK_EXT_line_rasterization.
bool IsExtLineRasterizationSupported() const {
- return ext_line_rasterization;
+ return extensions.line_rasterization;
}
/// Returns true if the device supports VK_EXT_vertex_input_dynamic_state.
bool IsExtVertexInputDynamicStateSupported() const {
- return ext_vertex_input_dynamic_state;
+ return extensions.vertex_input_dynamic_state;
}
/// Returns true if the device supports VK_EXT_shader_stencil_export.
bool IsExtShaderStencilExportSupported() const {
- return ext_shader_stencil_export;
+ return extensions.shader_stencil_export;
}
/// Returns true if the device supports VK_EXT_conservative_rasterization.
bool IsExtConservativeRasterizationSupported() const {
- return ext_conservative_rasterization;
+ return extensions.conservative_rasterization;
}
/// Returns true if the device supports VK_EXT_provoking_vertex.
bool IsExtProvokingVertexSupported() const {
- return ext_provoking_vertex;
+ return extensions.provoking_vertex;
}
/// Returns true if the device supports VK_KHR_shader_atomic_int64.
bool IsExtShaderAtomicInt64Supported() const {
- return ext_shader_atomic_int64;
+ return extensions.shader_atomic_int64;
}
/// Returns the minimum supported version of SPIR-V.
@@ -347,7 +498,7 @@ public:
if (instance_version >= VK_API_VERSION_1_3) {
return 0x00010600U;
}
- if (khr_spirv_1_4) {
+ if (extensions.spirv_1_4) {
return 0x00010400U;
}
return 0x00010000U;
@@ -365,11 +516,11 @@ public:
/// Returns the vendor name reported from Vulkan.
std::string_view GetVendorName() const {
- return vendor_name;
+ return properties.driver.driverName;
}
/// Returns the list of available extensions.
- const std::vector<std::string>& GetAvailableExtensions() const {
+ const std::set<std::string, std::less<>>& GetAvailableExtensions() const {
return supported_extensions;
}
@@ -378,7 +529,7 @@ public:
}
bool CanReportMemoryUsage() const {
- return ext_memory_budget;
+ return extensions.memory_budget;
}
u64 GetDeviceMemoryUsage() const;
@@ -400,36 +551,29 @@ public:
}
bool HasNullDescriptor() const {
- return has_null_descriptor;
+ return features.robustness2.nullDescriptor;
}
u32 GetMaxVertexInputAttributes() const {
- return max_vertex_input_attributes;
+ return properties.properties.limits.maxVertexInputAttributes;
}
u32 GetMaxVertexInputBindings() const {
- return max_vertex_input_bindings;
+ return properties.properties.limits.maxVertexInputBindings;
}
private:
- /// Checks if the physical device is suitable.
- void CheckSuitability(bool requires_swapchain) const;
+ /// Checks if the physical device is suitable and configures the object state
+ /// with all necessary info about its properties.
+ bool GetSuitability(bool requires_swapchain);
- /// Loads extensions into a vector and stores available ones in this object.
- std::vector<const char*> LoadExtensions(bool requires_surface);
+ // Remove extensions which have incomplete feature support.
+ void RemoveUnsuitableExtensions();
+ void RemoveExtensionIfUnsuitable(bool is_suitable, const std::string& extension_name);
/// Sets up queue families.
void SetupFamilies(VkSurfaceKHR surface);
- /// Sets up device features.
- void SetupFeatures();
-
- /// Sets up device properties.
- void SetupProperties();
-
- /// Collects telemetry information from the device.
- void CollectTelemetryParameters();
-
/// Collects information about attached tools.
void CollectToolingInfo();
@@ -440,91 +584,93 @@ private:
std::vector<VkDeviceQueueCreateInfo> GetDeviceQueueCreateInfos() const;
/// Returns true if ASTC textures are natively supported.
- bool IsOptimalAstcSupported(const VkPhysicalDeviceFeatures& features) const;
+ bool ComputeIsOptimalAstcSupported() const;
/// Returns true if the device natively supports blitting depth stencil images.
bool TestDepthStencilBlits() const;
- VkInstance instance; ///< Vulkan instance.
- vk::DeviceDispatch dld; ///< Device function pointers.
- vk::PhysicalDevice physical; ///< Physical device.
- VkPhysicalDeviceProperties properties; ///< Device properties.
- VkPhysicalDeviceFloatControlsPropertiesKHR float_controls{}; ///< Float control properties.
- vk::Device logical; ///< Logical device.
- vk::Queue graphics_queue; ///< Main graphics queue.
- vk::Queue present_queue; ///< Main present queue.
- u32 instance_version{}; ///< Vulkan onstance version.
- u32 graphics_family{}; ///< Main graphics queue family index.
- u32 present_family{}; ///< Main present queue family index.
- VkDriverIdKHR driver_id{}; ///< Driver ID.
- VkShaderStageFlags guest_warp_stages{}; ///< Stages where the guest warp size can be forced.
- u64 device_access_memory{}; ///< Total size of device local memory in bytes.
- u32 max_push_descriptors{}; ///< Maximum number of push descriptors
- u32 sets_per_pool{}; ///< Sets per Description Pool
- bool is_optimal_astc_supported{}; ///< Support for native ASTC.
- bool is_float16_supported{}; ///< Support for float16 arithmetic.
- bool is_int8_supported{}; ///< Support for int8 arithmetic.
- bool is_warp_potentially_bigger{}; ///< Host warp size can be bigger than guest.
- bool is_formatless_image_load_supported{}; ///< Support for shader image read without format.
- bool is_depth_bounds_supported{}; ///< Support for depth bounds.
- bool is_shader_float64_supported{}; ///< Support for float64.
- bool is_shader_int64_supported{}; ///< Support for int64.
- bool is_shader_int16_supported{}; ///< Support for int16.
- bool is_shader_storage_image_multisample{}; ///< Support for image operations on MSAA images.
- bool is_blit_depth_stencil_supported{}; ///< Support for blitting from and to depth stencil.
- bool is_topology_list_restart_supported{}; ///< Support for primitive restart with list
- ///< topologies.
- bool is_patch_list_restart_supported{}; ///< Support for primitive restart with list patch.
- bool is_integrated{}; ///< Is GPU an iGPU.
- bool is_virtual{}; ///< Is GPU a virtual GPU.
- bool is_non_gpu{}; ///< Is SoftwareRasterizer, FPGA, non-GPU device.
- bool nv_viewport_swizzle{}; ///< Support for VK_NV_viewport_swizzle.
- bool nv_viewport_array2{}; ///< Support for VK_NV_viewport_array2.
- bool nv_geometry_shader_passthrough{}; ///< Support for VK_NV_geometry_shader_passthrough.
- bool khr_draw_indirect_count{}; ///< Support for VK_KHR_draw_indirect_count.
- bool khr_uniform_buffer_standard_layout{}; ///< Support for scalar uniform buffer layouts.
- bool khr_spirv_1_4{}; ///< Support for VK_KHR_spirv_1_4.
- bool khr_workgroup_memory_explicit_layout{}; ///< Support for explicit workgroup layouts.
- bool khr_push_descriptor{}; ///< Support for VK_KHR_push_descritor.
- bool khr_pipeline_executable_properties{}; ///< Support for executable properties.
- bool khr_swapchain_mutable_format{}; ///< Support for VK_KHR_swapchain_mutable_format.
- bool ext_index_type_uint8{}; ///< Support for VK_EXT_index_type_uint8.
- bool ext_sampler_filter_minmax{}; ///< Support for VK_EXT_sampler_filter_minmax.
- bool ext_depth_clip_control{}; ///< Support for VK_EXT_depth_clip_control
- bool ext_depth_range_unrestricted{}; ///< Support for VK_EXT_depth_range_unrestricted.
- bool ext_shader_viewport_index_layer{}; ///< Support for VK_EXT_shader_viewport_index_layer.
- bool ext_tooling_info{}; ///< Support for VK_EXT_tooling_info.
- bool ext_subgroup_size_control{}; ///< Support for VK_EXT_subgroup_size_control.
- bool ext_transform_feedback{}; ///< Support for VK_EXT_transform_feedback.
- bool ext_custom_border_color{}; ///< Support for VK_EXT_custom_border_color.
- bool ext_extended_dynamic_state{}; ///< Support for VK_EXT_extended_dynamic_state.
- bool ext_extended_dynamic_state_2{}; ///< Support for VK_EXT_extended_dynamic_state2.
- bool ext_extended_dynamic_state_2_extra{}; ///< Support for VK_EXT_extended_dynamic_state2.
- bool ext_extended_dynamic_state_3{}; ///< Support for VK_EXT_extended_dynamic_state3.
- bool ext_extended_dynamic_state_3_blend{}; ///< Support for VK_EXT_extended_dynamic_state3.
- bool ext_extended_dynamic_state_3_enables{}; ///< Support for VK_EXT_extended_dynamic_state3.
- bool ext_line_rasterization{}; ///< Support for VK_EXT_line_rasterization.
- bool ext_vertex_input_dynamic_state{}; ///< Support for VK_EXT_vertex_input_dynamic_state.
- bool ext_shader_stencil_export{}; ///< Support for VK_EXT_shader_stencil_export.
- bool ext_shader_atomic_int64{}; ///< Support for VK_KHR_shader_atomic_int64.
- bool ext_conservative_rasterization{}; ///< Support for VK_EXT_conservative_rasterization.
- bool ext_provoking_vertex{}; ///< Support for VK_EXT_provoking_vertex.
- bool ext_memory_budget{}; ///< Support for VK_EXT_memory_budget.
- bool nv_device_diagnostics_config{}; ///< Support for VK_NV_device_diagnostics_config.
- bool has_broken_cube_compatibility{}; ///< Has broken cube compatiblity bit
- bool has_renderdoc{}; ///< Has RenderDoc attached
- bool has_nsight_graphics{}; ///< Has Nsight Graphics attached
- bool supports_d24_depth{}; ///< Supports D24 depth buffers.
- bool cant_blit_msaa{}; ///< Does not support MSAA<->MSAA blitting.
- bool must_emulate_bgr565{}; ///< Emulates BGR565 by swizzling RGB565 format.
- bool has_null_descriptor{}; ///< Has support for null descriptors.
- u32 max_vertex_input_attributes{}; ///< Max vertex input attributes in pipeline
- u32 max_vertex_input_bindings{}; ///< Max vertex input buffers in pipeline
+private:
+ VkInstance instance; ///< Vulkan instance.
+ vk::DeviceDispatch dld; ///< Device function pointers.
+ vk::PhysicalDevice physical; ///< Physical device.
+ vk::Device logical; ///< Logical device.
+ vk::Queue graphics_queue; ///< Main graphics queue.
+ vk::Queue present_queue; ///< Main present queue.
+ u32 instance_version{}; ///< Vulkan instance version.
+ u32 graphics_family{}; ///< Main graphics queue family index.
+ u32 present_family{}; ///< Main present queue family index.
+
+ struct Extensions {
+#define EXTENSION(prefix, macro_name, var_name) bool var_name{};
+#define FEATURE(prefix, struct_name, macro_name, var_name) bool var_name{};
+
+ FOR_EACH_VK_FEATURE_1_1(FEATURE);
+ FOR_EACH_VK_FEATURE_1_2(FEATURE);
+ FOR_EACH_VK_FEATURE_1_3(FEATURE);
+ FOR_EACH_VK_FEATURE_EXT(FEATURE);
+ FOR_EACH_VK_EXTENSION(EXTENSION);
+ FOR_EACH_VK_EXTENSION_WIN32(EXTENSION);
+
+#undef EXTENSION
+#undef FEATURE
+ };
+
+ struct Features {
+#define FEATURE_CORE(prefix, struct_name, macro_name, var_name) \
+ VkPhysicalDevice##struct_name##Features var_name{};
+#define FEATURE_EXT(prefix, struct_name, macro_name, var_name) \
+ VkPhysicalDevice##struct_name##Features##prefix var_name{};
+
+ FOR_EACH_VK_FEATURE_1_1(FEATURE_CORE);
+ FOR_EACH_VK_FEATURE_1_2(FEATURE_CORE);
+ FOR_EACH_VK_FEATURE_1_3(FEATURE_CORE);
+ FOR_EACH_VK_FEATURE_EXT(FEATURE_EXT);
+
+#undef FEATURE_CORE
+#undef FEATURE_EXT
+
+ VkPhysicalDeviceFeatures features{};
+ };
+
+ struct Properties {
+ VkPhysicalDeviceDriverProperties driver{};
+ VkPhysicalDeviceFloatControlsProperties float_controls{};
+ VkPhysicalDevicePushDescriptorPropertiesKHR push_descriptor{};
+ VkPhysicalDeviceSubgroupSizeControlProperties subgroup_size_control{};
+ VkPhysicalDeviceTransformFeedbackPropertiesEXT transform_feedback{};
+
+ VkPhysicalDeviceProperties properties{};
+ };
+
+ Extensions extensions{};
+ Features features{};
+ Properties properties{};
+
+ VkPhysicalDeviceFeatures2 features2{};
+ VkPhysicalDeviceProperties2 properties2{};
+
+ // Misc features
+ bool is_optimal_astc_supported{}; ///< Support for all guest ASTC formats.
+ bool is_blit_depth_stencil_supported{}; ///< Support for blitting from and to depth stencil.
+ bool is_warp_potentially_bigger{}; ///< Host warp size can be bigger than guest.
+ bool is_integrated{}; ///< Is GPU an iGPU.
+ bool is_virtual{}; ///< Is GPU a virtual GPU.
+ bool is_non_gpu{}; ///< Is SoftwareRasterizer, FPGA, non-GPU device.
+ bool has_broken_cube_compatibility{}; ///< Has broken cube compatiblity bit
+ bool has_renderdoc{}; ///< Has RenderDoc attached
+ bool has_nsight_graphics{}; ///< Has Nsight Graphics attached
+ bool supports_d24_depth{}; ///< Supports D24 depth buffers.
+ bool cant_blit_msaa{}; ///< Does not support MSAA<->MSAA blitting.
+ bool must_emulate_bgr565{}; ///< Emulates BGR565 by swizzling RGB565 format.
+ bool dynamic_state3_blending{}; ///< Has all blending features of dynamic_state3.
+ bool dynamic_state3_enables{}; ///< Has all enables features of dynamic_state3.
+ u64 device_access_memory{}; ///< Total size of device local memory in bytes.
+ u32 sets_per_pool{}; ///< Sets per Description Pool
// Telemetry parameters
- std::string vendor_name; ///< Device's driver name.
- std::vector<std::string> supported_extensions; ///< Reported Vulkan extensions.
- std::vector<size_t> valid_heap_memory; ///< Heaps used.
+ std::set<std::string, std::less<>> supported_extensions; ///< Reported Vulkan extensions.
+ std::set<std::string, std::less<>> loaded_extensions; ///< Loaded Vulkan extensions.
+ std::vector<size_t> valid_heap_memory; ///< Heaps used.
/// Format properties dictionary.
std::unordered_map<VkFormat, VkFormatProperties> format_properties;
diff --git a/src/video_core/vulkan_common/vulkan_wrapper.cpp b/src/video_core/vulkan_common/vulkan_wrapper.cpp
index 61be1fce1..486d4dfaf 100644
--- a/src/video_core/vulkan_common/vulkan_wrapper.cpp
+++ b/src/video_core/vulkan_common/vulkan_wrapper.cpp
@@ -96,8 +96,8 @@ void Load(VkDevice device, DeviceDispatch& dld) noexcept {
X(vkCmdDrawIndexed);
X(vkCmdDrawIndirect);
X(vkCmdDrawIndexedIndirect);
- X(vkCmdDrawIndirectCountKHR);
- X(vkCmdDrawIndexedIndirectCountKHR);
+ X(vkCmdDrawIndirectCount);
+ X(vkCmdDrawIndexedIndirectCount);
X(vkCmdEndQuery);
X(vkCmdEndRenderPass);
X(vkCmdEndTransformFeedbackEXT);
@@ -221,6 +221,12 @@ void Load(VkDevice device, DeviceDispatch& dld) noexcept {
if (!dld.vkResetQueryPool) {
Proc(dld.vkResetQueryPool, dld, "vkResetQueryPoolEXT", device);
}
+
+ // Support for draw indirect with count is optional in Vulkan 1.2
+ if (!dld.vkCmdDrawIndirectCount) {
+ Proc(dld.vkCmdDrawIndirectCount, dld, "vkCmdDrawIndirectCountKHR", device);
+ Proc(dld.vkCmdDrawIndexedIndirectCount, dld, "vkCmdDrawIndexedIndirectCountKHR", device);
+ }
#undef X
}
diff --git a/src/video_core/vulkan_common/vulkan_wrapper.h b/src/video_core/vulkan_common/vulkan_wrapper.h
index 412779b51..e86f661cb 100644
--- a/src/video_core/vulkan_common/vulkan_wrapper.h
+++ b/src/video_core/vulkan_common/vulkan_wrapper.h
@@ -215,8 +215,8 @@ struct DeviceDispatch : InstanceDispatch {
PFN_vkCmdDrawIndexed vkCmdDrawIndexed{};
PFN_vkCmdDrawIndirect vkCmdDrawIndirect{};
PFN_vkCmdDrawIndexedIndirect vkCmdDrawIndexedIndirect{};
- PFN_vkCmdDrawIndirectCountKHR vkCmdDrawIndirectCountKHR{};
- PFN_vkCmdDrawIndexedIndirectCountKHR vkCmdDrawIndexedIndirectCountKHR{};
+ PFN_vkCmdDrawIndirectCount vkCmdDrawIndirectCount{};
+ PFN_vkCmdDrawIndexedIndirectCount vkCmdDrawIndexedIndirectCount{};
PFN_vkCmdEndDebugUtilsLabelEXT vkCmdEndDebugUtilsLabelEXT{};
PFN_vkCmdEndQuery vkCmdEndQuery{};
PFN_vkCmdEndRenderPass vkCmdEndRenderPass{};
@@ -1065,15 +1065,15 @@ public:
void DrawIndirectCount(VkBuffer src_buffer, VkDeviceSize src_offset, VkBuffer count_buffer,
VkDeviceSize count_offset, u32 draw_count, u32 stride) const noexcept {
- dld->vkCmdDrawIndirectCountKHR(handle, src_buffer, src_offset, count_buffer, count_offset,
- draw_count, stride);
+ dld->vkCmdDrawIndirectCount(handle, src_buffer, src_offset, count_buffer, count_offset,
+ draw_count, stride);
}
void DrawIndexedIndirectCount(VkBuffer src_buffer, VkDeviceSize src_offset,
VkBuffer count_buffer, VkDeviceSize count_offset, u32 draw_count,
u32 stride) const noexcept {
- dld->vkCmdDrawIndexedIndirectCountKHR(handle, src_buffer, src_offset, count_buffer,
- count_offset, draw_count, stride);
+ dld->vkCmdDrawIndexedIndirectCount(handle, src_buffer, src_offset, count_buffer,
+ count_offset, draw_count, stride);
}
void ClearAttachments(Span<VkClearAttachment> attachments,
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)
discord_impl.cpp
discord_impl.h
)
- target_link_libraries(yuzu PRIVATE DiscordRPC::discord-rpc)
+ target_link_libraries(yuzu PRIVATE DiscordRPC::discord-rpc httplib::httplib)
target_compile_definitions(yuzu PRIVATE -DUSE_DISCORD_PRESENCE)
endif()
diff --git a/src/yuzu/Info.plist b/src/yuzu/Info.plist
index 0eb377926..f05f3186c 100644
--- a/src/yuzu/Info.plist
+++ b/src/yuzu/Info.plist
@@ -34,6 +34,8 @@ SPDX-License-Identifier: GPL-2.0-or-later
<string></string>
<key>CSResourcesFileMapped</key>
<true/>
+ <key>LSApplicationCategoryType</key>
+ <string>public.app-category.games</string>
<key>LSRequiresCarbon</key>
<true/>
<key>NSHumanReadableCopyright</key>
diff --git a/src/yuzu/applets/qt_software_keyboard.cpp b/src/yuzu/applets/qt_software_keyboard.cpp
index 734b0ea40..4ae49506d 100644
--- a/src/yuzu/applets/qt_software_keyboard.cpp
+++ b/src/yuzu/applets/qt_software_keyboard.cpp
@@ -575,7 +575,7 @@ void QtSoftwareKeyboardDialog::MoveAndResizeWindow(QPoint pos, QSize size) {
QDialog::resize(size);
// High DPI
- const float dpi_scale = qApp->screenAt(pos)->logicalDotsPerInch() / 96.0f;
+ const float dpi_scale = screen()->logicalDotsPerInch() / 96.0f;
RescaleKeyboardElements(size.width(), size.height(), dpi_scale);
}
diff --git a/src/yuzu/bootmanager.cpp b/src/yuzu/bootmanager.cpp
index 3d560f303..17acd3933 100644
--- a/src/yuzu/bootmanager.cpp
+++ b/src/yuzu/bootmanager.cpp
@@ -67,7 +67,7 @@ void EmuThread::run() {
emit LoadProgress(VideoCore::LoadCallbackStage::Prepare, 0, 0);
if (Settings::values.use_disk_shader_cache.GetValue()) {
m_system.Renderer().ReadRasterizer()->LoadDiskResources(
- m_system.GetCurrentProcessProgramID(), stop_token,
+ m_system.GetApplicationProcessProgramID(), stop_token,
[this](VideoCore::LoadCallbackStage stage, std::size_t value, std::size_t total) {
emit LoadProgress(stage, value, total);
});
@@ -96,9 +96,9 @@ void EmuThread::run() {
m_is_running.store(false);
m_is_running.notify_all();
- emit DebugModeEntered();
+ EmulationPaused(lk);
Common::CondvarWait(m_should_run_cv, lk, stop_token, [&] { return m_should_run; });
- emit DebugModeLeft();
+ EmulationResumed(lk);
}
}
@@ -111,6 +111,21 @@ void EmuThread::run() {
#endif
}
+// Unlock while emitting signals so that the main thread can
+// continue pumping events.
+
+void EmuThread::EmulationPaused(std::unique_lock<std::mutex>& lk) {
+ lk.unlock();
+ emit DebugModeEntered();
+ lk.lock();
+}
+
+void EmuThread::EmulationResumed(std::unique_lock<std::mutex>& lk) {
+ lk.unlock();
+ emit DebugModeLeft();
+ lk.lock();
+}
+
#ifdef HAS_OPENGL
class OpenGLSharedContext : public Core::Frontend::GraphicsContext {
public:
@@ -386,12 +401,6 @@ qreal GRenderWindow::windowPixelRatio() const {
return devicePixelRatioF();
}
-std::pair<u32, u32> GRenderWindow::ScaleTouch(const QPointF& pos) const {
- const qreal pixel_ratio = windowPixelRatio();
- return {static_cast<u32>(std::max(std::round(pos.x() * pixel_ratio), qreal{0.0})),
- static_cast<u32>(std::max(std::round(pos.y() * pixel_ratio), qreal{0.0}))};
-}
-
void GRenderWindow::closeEvent(QCloseEvent* event) {
emit Closed();
QWidget::closeEvent(event);
@@ -634,10 +643,12 @@ void GRenderWindow::mousePressEvent(QMouseEvent* event) {
// Qt sometimes returns the parent coordinates. To avoid this we read the global mouse
// coordinates and map them to the current render area
const auto pos = mapFromGlobal(QCursor::pos());
- const auto [x, y] = ScaleTouch(pos);
- const auto [touch_x, touch_y] = MapToTouchScreen(x, y);
+ const auto [touch_x, touch_y] = MapToTouchScreen(pos.x(), pos.y());
const auto button = QtButtonToMouseButton(event->button());
- input_subsystem->GetMouse()->PressButton(x, y, touch_x, touch_y, button);
+
+ input_subsystem->GetMouse()->PressMouseButton(button);
+ input_subsystem->GetMouse()->PressButton(pos.x(), pos.y(), button);
+ input_subsystem->GetMouse()->PressTouchButton(touch_x, touch_y, button);
emit MouseActivity();
}
@@ -650,11 +661,13 @@ void GRenderWindow::mouseMoveEvent(QMouseEvent* event) {
// Qt sometimes returns the parent coordinates. To avoid this we read the global mouse
// coordinates and map them to the current render area
const auto pos = mapFromGlobal(QCursor::pos());
- const auto [x, y] = ScaleTouch(pos);
- const auto [touch_x, touch_y] = MapToTouchScreen(x, y);
+ const auto [touch_x, touch_y] = MapToTouchScreen(pos.x(), pos.y());
const int center_x = width() / 2;
const int center_y = height() / 2;
- input_subsystem->GetMouse()->MouseMove(x, y, touch_x, touch_y, center_x, center_y);
+
+ input_subsystem->GetMouse()->MouseMove(touch_x, touch_y);
+ input_subsystem->GetMouse()->TouchMove(touch_x, touch_y);
+ input_subsystem->GetMouse()->Move(pos.x(), pos.y(), center_x, center_y);
if (Settings::values.mouse_panning && !Settings::values.mouse_enabled) {
QCursor::setPos(mapToGlobal(QPoint{center_x, center_y}));
@@ -682,8 +695,8 @@ void GRenderWindow::wheelEvent(QWheelEvent* event) {
void GRenderWindow::TouchBeginEvent(const QTouchEvent* event) {
QList<QTouchEvent::TouchPoint> touch_points = event->touchPoints();
for (const auto& touch_point : touch_points) {
- const auto [x, y] = ScaleTouch(touch_point.pos());
- const auto [touch_x, touch_y] = MapToTouchScreen(x, y);
+ const auto pos = touch_point.pos();
+ const auto [touch_x, touch_y] = MapToTouchScreen(pos.x(), pos.y());
input_subsystem->GetTouchScreen()->TouchPressed(touch_x, touch_y, touch_point.id());
}
}
@@ -692,8 +705,8 @@ void GRenderWindow::TouchUpdateEvent(const QTouchEvent* event) {
QList<QTouchEvent::TouchPoint> touch_points = event->touchPoints();
input_subsystem->GetTouchScreen()->ClearActiveFlag();
for (const auto& touch_point : touch_points) {
- const auto [x, y] = ScaleTouch(touch_point.pos());
- const auto [touch_x, touch_y] = MapToTouchScreen(x, y);
+ const auto pos = touch_point.pos();
+ const auto [touch_x, touch_y] = MapToTouchScreen(pos.x(), pos.y());
input_subsystem->GetTouchScreen()->TouchMoved(touch_x, touch_y, touch_point.id());
}
input_subsystem->GetTouchScreen()->ReleaseInactiveTouch();
diff --git a/src/yuzu/bootmanager.h b/src/yuzu/bootmanager.h
index eca16b313..627e19f42 100644
--- a/src/yuzu/bootmanager.h
+++ b/src/yuzu/bootmanager.h
@@ -92,6 +92,10 @@ public:
}
private:
+ void EmulationPaused(std::unique_lock<std::mutex>& lk);
+ void EmulationResumed(std::unique_lock<std::mutex>& lk);
+
+private:
Core::System& m_system;
std::stop_source m_stop_source;
@@ -180,8 +184,6 @@ public:
void CaptureScreenshot(const QString& screenshot_path);
- std::pair<u32, u32> ScaleTouch(const QPointF& pos) const;
-
/**
* Instructs the window to re-launch the application using the specified program_index.
* @param program_index Specifies the index within the application of the program to launch.
diff --git a/src/yuzu/configuration/config.cpp b/src/yuzu/configuration/config.cpp
index 0db62baa3..db68ed259 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{{
// UISetting::values.shortcuts, which is alphabetically ordered.
// clang-format off
const std::array<UISettings::Shortcut, 22> Config::default_hotkeys{{
- {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Audio Mute/Unmute")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("Ctrl+M"), QStringLiteral("Home+Dpad_Right"), Qt::WindowShortcut}},
- {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Audio Volume Down")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("-"), QStringLiteral("Home+Dpad_Down"), Qt::ApplicationShortcut}},
- {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Audio Volume Up")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("="), QStringLiteral("Home+Dpad_Up"), Qt::ApplicationShortcut}},
- {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Capture Screenshot")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("Ctrl+P"), QStringLiteral("Screenshot"), Qt::WidgetWithChildrenShortcut}},
- {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Change Adapting Filter")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("F8"), QStringLiteral("Home+L"), Qt::ApplicationShortcut}},
- {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Change Docked Mode")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("F10"), QStringLiteral("Home+X"), Qt::ApplicationShortcut}},
- {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Change GPU Accuracy")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("F9"), QStringLiteral("Home+R"), Qt::ApplicationShortcut}},
- {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Continue/Pause Emulation")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("F4"), QStringLiteral("Home+Plus"), Qt::WindowShortcut}},
- {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Exit Fullscreen")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("Esc"), QStringLiteral(""), Qt::WindowShortcut}},
- {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Exit yuzu")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("Ctrl+Q"), QStringLiteral("Home+Minus"), Qt::WindowShortcut}},
- {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Fullscreen")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("F11"), QStringLiteral("Home+B"), Qt::WindowShortcut}},
- {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Load File")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("Ctrl+O"), QStringLiteral(""), Qt::WidgetWithChildrenShortcut}},
- {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Load/Remove Amiibo")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("F2"), QStringLiteral("Home+A"), Qt::WidgetWithChildrenShortcut}},
- {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Restart Emulation")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("F6"), QStringLiteral(""), Qt::WindowShortcut}},
- {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Stop Emulation")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("F5"), QStringLiteral(""), Qt::WindowShortcut}},
- {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "TAS Record")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("Ctrl+F7"), QStringLiteral(""), Qt::ApplicationShortcut}},
- {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "TAS Reset")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("Ctrl+F6"), QStringLiteral(""), Qt::ApplicationShortcut}},
- {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "TAS Start/Stop")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("Ctrl+F5"), QStringLiteral(""), Qt::ApplicationShortcut}},
- {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Toggle Filter Bar")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("Ctrl+F"), QStringLiteral(""), Qt::WindowShortcut}},
- {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Toggle Framerate Limit")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("Ctrl+U"), QStringLiteral("Home+Y"), Qt::ApplicationShortcut}},
- {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Toggle Mouse Panning")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("Ctrl+F9"), QStringLiteral(""), Qt::ApplicationShortcut}},
- {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Toggle Status Bar")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("Ctrl+S"), QStringLiteral(""), Qt::WindowShortcut}},
+ {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}},
+ {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Audio Volume Down")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("-"), QStringLiteral("Home+Dpad_Down"), Qt::ApplicationShortcut, true}},
+ {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Audio Volume Up")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("="), QStringLiteral("Home+Dpad_Up"), Qt::ApplicationShortcut, true}},
+ {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Capture Screenshot")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("Ctrl+P"), QStringLiteral("Screenshot"), Qt::WidgetWithChildrenShortcut, false}},
+ {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Change Adapting Filter")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("F8"), QStringLiteral("Home+L"), Qt::ApplicationShortcut, false}},
+ {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Change Docked Mode")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("F10"), QStringLiteral("Home+X"), Qt::ApplicationShortcut, false}},
+ {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Change GPU Accuracy")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("F9"), QStringLiteral("Home+R"), Qt::ApplicationShortcut, false}},
+ {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Continue/Pause Emulation")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("F4"), QStringLiteral("Home+Plus"), Qt::WindowShortcut, false}},
+ {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Exit Fullscreen")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("Esc"), QStringLiteral(""), Qt::WindowShortcut, false}},
+ {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Exit yuzu")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("Ctrl+Q"), QStringLiteral("Home+Minus"), Qt::WindowShortcut, false}},
+ {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Fullscreen")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("F11"), QStringLiteral("Home+B"), Qt::WindowShortcut, false}},
+ {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Load File")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("Ctrl+O"), QStringLiteral(""), Qt::WidgetWithChildrenShortcut, false}},
+ {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Load/Remove Amiibo")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("F2"), QStringLiteral("Home+A"), Qt::WidgetWithChildrenShortcut, false}},
+ {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Restart Emulation")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("F6"), QStringLiteral(""), Qt::WindowShortcut, false}},
+ {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Stop Emulation")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("F5"), QStringLiteral(""), Qt::WindowShortcut, false}},
+ {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "TAS Record")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("Ctrl+F7"), QStringLiteral(""), Qt::ApplicationShortcut, false}},
+ {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "TAS Reset")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("Ctrl+F6"), QStringLiteral(""), Qt::ApplicationShortcut, false}},
+ {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "TAS Start/Stop")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("Ctrl+F5"), QStringLiteral(""), Qt::ApplicationShortcut, false}},
+ {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Toggle Filter Bar")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("Ctrl+F"), QStringLiteral(""), Qt::WindowShortcut, false}},
+ {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Toggle Framerate Limit")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("Ctrl+U"), QStringLiteral("Home+Y"), Qt::ApplicationShortcut, false}},
+ {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Toggle Mouse Panning")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("Ctrl+F9"), QStringLiteral(""), Qt::ApplicationShortcut, false}},
+ {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Toggle Status Bar")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("Ctrl+S"), QStringLiteral(""), Qt::WindowShortcut, false}},
}};
// clang-format on
@@ -440,6 +440,8 @@ void Config::ReadControlValues() {
ReadBasicSetting(Settings::values.emulate_analog_keyboard);
Settings::values.mouse_panning = false;
ReadBasicSetting(Settings::values.mouse_panning_sensitivity);
+ ReadBasicSetting(Settings::values.enable_joycon_driver);
+ ReadBasicSetting(Settings::values.enable_procon_driver);
ReadBasicSetting(Settings::values.tas_enable);
ReadBasicSetting(Settings::values.tas_loop);
@@ -747,7 +749,7 @@ void Config::ReadShortcutValues() {
for (const auto& [name, group, shortcut] : default_hotkeys) {
qt_config->beginGroup(group);
qt_config->beginGroup(name);
- // No longer using ReadSetting for shortcut.second as it innacurately returns a value of 1
+ // No longer using ReadSetting for shortcut.second as it inaccurately returns a value of 1
// for WidgetWithChildrenShortcut which is a value of 3. Needed to fix shortcuts the open
// a file dialog in windowed mode
UISettings::values.shortcuts.push_back(
@@ -756,7 +758,7 @@ void Config::ReadShortcutValues() {
{ReadSetting(QStringLiteral("KeySeq"), shortcut.keyseq).toString(),
ReadSetting(QStringLiteral("Controller_KeySeq"), shortcut.controller_keyseq)
.toString(),
- shortcut.context}});
+ shortcut.context, ReadSetting(QStringLiteral("Repeat"), shortcut.repeat).toBool()}});
qt_config->endGroup();
qt_config->endGroup();
}
@@ -1101,6 +1103,7 @@ void Config::SaveValues() {
SaveRendererValues();
SaveAudioValues();
SaveSystemValues();
+ qt_config->sync();
}
void Config::SaveAudioValues() {
@@ -1139,6 +1142,8 @@ void Config::SaveControlValues() {
WriteGlobalSetting(Settings::values.enable_accurate_vibrations);
WriteGlobalSetting(Settings::values.motion_enabled);
WriteBasicSetting(Settings::values.enable_raw_input);
+ WriteBasicSetting(Settings::values.enable_joycon_driver);
+ WriteBasicSetting(Settings::values.enable_procon_driver);
WriteBasicSetting(Settings::values.keyboard_enabled);
WriteBasicSetting(Settings::values.emulate_analog_keyboard);
WriteBasicSetting(Settings::values.mouse_panning_sensitivity);
@@ -1393,6 +1398,7 @@ void Config::SaveShortcutValues() {
WriteSetting(QStringLiteral("Controller_KeySeq"), shortcut.controller_keyseq,
default_hotkey.controller_keyseq);
WriteSetting(QStringLiteral("Context"), shortcut.context, default_hotkey.context);
+ WriteSetting(QStringLiteral("Repeat"), shortcut.repeat, default_hotkey.repeat);
qt_config->endGroup();
qt_config->endGroup();
}
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
combobox->insertItem(ConfigurationShared::USE_GLOBAL_INDEX, use_global_text);
combobox->insertSeparator(ConfigurationShared::USE_GLOBAL_SEPARATOR_INDEX);
}
+
+int ConfigurationShared::GetComboboxIndex(int global_setting_index, const QComboBox* combobox) {
+ if (Settings::IsConfiguringGlobal()) {
+ return combobox->currentIndex();
+ }
+ if (combobox->currentIndex() == ConfigurationShared::USE_GLOBAL_INDEX) {
+ return global_setting_index;
+ }
+ return combobox->currentIndex() - ConfigurationShared::USE_GLOBAL_OFFSET;
+}
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);
// Adds the "Use Global Configuration" selection and separator to the beginning of a QComboBox
void InsertGlobalItem(QComboBox* combobox, int global_index);
+// Returns the correct index of a QComboBox taking into account global configuration
+int GetComboboxIndex(int global_setting_index, const QComboBox* combobox);
+
} // 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_,
web_tab->SetWebServiceConfigEnabled(enable_web_config);
hotkeys_tab->Populate(registry);
- setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);
input_tab->Initialize(input_subsystem);
diff --git a/src/yuzu/configuration/configure_graphics.ui b/src/yuzu/configuration/configure_graphics.ui
index aa02cc63c..a45ec69ec 100644
--- a/src/yuzu/configuration/configure_graphics.ui
+++ b/src/yuzu/configuration/configure_graphics.ui
@@ -366,6 +366,11 @@
</item>
<item>
<property name="text">
+ <string>1.5X (1080p/1620p) [EXPERIMENTAL]</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
<string>2X (1440p/2160p)</string>
</property>
</item>
@@ -389,6 +394,16 @@
<string>6X (4320p/6480p)</string>
</property>
</item>
+ <item>
+ <property name="text">
+ <string>7X (5040p/7560p)</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>8X (5760p/8640p)</string>
+ </property>
+ </item>
</widget>
</item>
</layout>
@@ -445,7 +460,7 @@
</item>
<item>
<property name="text">
- <string>AMD FidelityFX™️ Super Resolution (Vulkan Only)</string>
+ <string>AMD FidelityFX™️ Super Resolution</string>
</property>
</item>
</widget>
diff --git a/src/yuzu/configuration/configure_graphics_advanced.cpp b/src/yuzu/configuration/configure_graphics_advanced.cpp
index fdf8485ce..cc0155a2c 100644
--- a/src/yuzu/configuration/configure_graphics_advanced.cpp
+++ b/src/yuzu/configuration/configure_graphics_advanced.cpp
@@ -22,6 +22,7 @@ ConfigureGraphicsAdvanced::~ConfigureGraphicsAdvanced() = default;
void ConfigureGraphicsAdvanced::SetConfiguration() {
const bool runtime_lock = !system.IsPoweredOn();
ui->use_vsync->setEnabled(runtime_lock);
+ ui->renderer_force_max_clock->setEnabled(runtime_lock);
ui->use_asynchronous_shaders->setEnabled(runtime_lock);
ui->anisotropic_filtering_combobox->setEnabled(runtime_lock);
@@ -40,12 +41,12 @@ void ConfigureGraphicsAdvanced::SetConfiguration() {
Settings::values.max_anisotropy.GetValue());
} else {
ConfigurationShared::SetPerGameSetting(ui->gpu_accuracy, &Settings::values.gpu_accuracy);
- ConfigurationShared::SetPerGameSetting(ui->renderer_force_max_clock,
- &Settings::values.renderer_force_max_clock);
ConfigurationShared::SetPerGameSetting(ui->anisotropic_filtering_combobox,
&Settings::values.max_anisotropy);
ConfigurationShared::SetHighlight(ui->label_gpu_accuracy,
!Settings::values.gpu_accuracy.UsingGlobal());
+ ConfigurationShared::SetHighlight(ui->renderer_force_max_clock,
+ !Settings::values.renderer_force_max_clock.UsingGlobal());
ConfigurationShared::SetHighlight(ui->af_label,
!Settings::values.max_anisotropy.UsingGlobal());
}
diff --git a/src/yuzu/configuration/configure_input_advanced.cpp b/src/yuzu/configuration/configure_input_advanced.cpp
index 235b813d9..8d81322f3 100644
--- a/src/yuzu/configuration/configure_input_advanced.cpp
+++ b/src/yuzu/configuration/configure_input_advanced.cpp
@@ -138,6 +138,8 @@ void ConfigureInputAdvanced::ApplyConfiguration() {
Settings::values.controller_navigation = ui->controller_navigation->isChecked();
Settings::values.enable_ring_controller = ui->enable_ring_controller->isChecked();
Settings::values.enable_ir_sensor = ui->enable_ir_sensor->isChecked();
+ Settings::values.enable_joycon_driver = ui->enable_joycon_driver->isChecked();
+ Settings::values.enable_procon_driver = ui->enable_procon_driver->isChecked();
}
void ConfigureInputAdvanced::LoadConfiguration() {
@@ -172,6 +174,8 @@ void ConfigureInputAdvanced::LoadConfiguration() {
ui->controller_navigation->setChecked(Settings::values.controller_navigation.GetValue());
ui->enable_ring_controller->setChecked(Settings::values.enable_ring_controller.GetValue());
ui->enable_ir_sensor->setChecked(Settings::values.enable_ir_sensor.GetValue());
+ ui->enable_joycon_driver->setChecked(Settings::values.enable_joycon_driver.GetValue());
+ ui->enable_procon_driver->setChecked(Settings::values.enable_procon_driver.GetValue());
UpdateUIEnabled();
}
diff --git a/src/yuzu/configuration/configure_input_advanced.ui b/src/yuzu/configuration/configure_input_advanced.ui
index fac8cf827..0eb2b34bc 100644
--- a/src/yuzu/configuration/configure_input_advanced.ui
+++ b/src/yuzu/configuration/configure_input_advanced.ui
@@ -2696,6 +2696,38 @@
</widget>
</item>
<item row="5" column="0">
+ <widget class="QCheckBox" name="enable_joycon_driver">
+ <property name="toolTip">
+ <string>Requires restarting yuzu</string>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>0</width>
+ <height>23</height>
+ </size>
+ </property>
+ <property name="text">
+ <string>Enable direct JoyCon driver</string>
+ </property>
+ </widget>
+ </item>
+ <item row="6" column="0">
+ <widget class="QCheckBox" name="enable_procon_driver">
+ <property name="toolTip">
+ <string>Requires restarting yuzu</string>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>0</width>
+ <height>23</height>
+ </size>
+ </property>
+ <property name="text">
+ <string>Enable direct Pro Controller driver [EXPERIMENTAL]</string>
+ </property>
+ </widget>
+ </item>
+ <item row="7" column="0">
<widget class="QCheckBox" name="mouse_panning">
<property name="minimumSize">
<size>
@@ -2708,7 +2740,7 @@
</property>
</widget>
</item>
- <item row="5" column="2">
+ <item row="7" column="2">
<widget class="QSpinBox" name="mouse_panning_sensitivity">
<property name="toolTip">
<string>Mouse sensitivity</string>
@@ -2730,14 +2762,14 @@
</property>
</widget>
</item>
- <item row="6" column="0">
+ <item row="8" column="0">
<widget class="QLabel" name="motion_touch">
<property name="text">
<string>Motion / Touch</string>
</property>
</widget>
</item>
- <item row="6" column="2">
+ <item row="8" column="2">
<widget class="QPushButton" name="buttonMotionTouch">
<property name="text">
<string>Configure</string>
diff --git a/src/yuzu/configuration/configure_input_player.cpp b/src/yuzu/configuration/configure_input_player.cpp
index 183cbe562..50b62293e 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) {
return QObject::tr("R");
case Common::Input::ButtonNames::TriggerL:
return QObject::tr("L");
+ case Common::Input::ButtonNames::TriggerZR:
+ return QObject::tr("ZR");
+ case Common::Input::ButtonNames::TriggerZL:
+ return QObject::tr("ZL");
+ case Common::Input::ButtonNames::TriggerSR:
+ return QObject::tr("SR");
+ case Common::Input::ButtonNames::TriggerSL:
+ return QObject::tr("SL");
+ case Common::Input::ButtonNames::ButtonStickL:
+ return QObject::tr("Stick L");
+ case Common::Input::ButtonNames::ButtonStickR:
+ return QObject::tr("Stick R");
case Common::Input::ButtonNames::ButtonA:
return QObject::tr("A");
case Common::Input::ButtonNames::ButtonB:
@@ -76,6 +88,14 @@ QString GetButtonName(Common::Input::ButtonNames button_name) {
return QObject::tr("Y");
case Common::Input::ButtonNames::ButtonStart:
return QObject::tr("Start");
+ case Common::Input::ButtonNames::ButtonPlus:
+ return QObject::tr("Plus");
+ case Common::Input::ButtonNames::ButtonMinus:
+ return QObject::tr("Minus");
+ case Common::Input::ButtonNames::ButtonHome:
+ return QObject::tr("Home");
+ case Common::Input::ButtonNames::ButtonCapture:
+ return QObject::tr("Capture");
case Common::Input::ButtonNames::L1:
return QObject::tr("L1");
case Common::Input::ButtonNames::L2:
@@ -162,12 +182,13 @@ QString ConfigureInputPlayer::ButtonToText(const Common::ParamPackage& param) {
const QString toggle = QString::fromStdString(param.Get("toggle", false) ? "~" : "");
const QString inverted = QString::fromStdString(param.Get("inverted", false) ? "!" : "");
const QString invert = QString::fromStdString(param.Get("invert", "+") == "-" ? "-" : "");
+ const QString turbo = QString::fromStdString(param.Get("turbo", false) ? "$" : "");
const auto common_button_name = input_subsystem->GetButtonName(param);
// Retrieve the names from Qt
if (param.Get("engine", "") == "keyboard") {
const QString button_str = GetKeyName(param.Get("code", 0));
- return QObject::tr("%1%2%3").arg(toggle, inverted, button_str);
+ return QObject::tr("%1%2%3%4").arg(turbo, toggle, inverted, button_str);
}
if (common_button_name == Common::Input::ButtonNames::Invalid) {
@@ -181,7 +202,7 @@ QString ConfigureInputPlayer::ButtonToText(const Common::ParamPackage& param) {
if (common_button_name == Common::Input::ButtonNames::Value) {
if (param.Has("hat")) {
const QString hat = GetDirectionName(param.Get("direction", ""));
- return QObject::tr("%1%2Hat %3").arg(toggle, inverted, hat);
+ return QObject::tr("%1%2%3Hat %4").arg(turbo, toggle, inverted, hat);
}
if (param.Has("axis")) {
const QString axis = QString::fromStdString(param.Get("axis", ""));
@@ -199,13 +220,13 @@ QString ConfigureInputPlayer::ButtonToText(const Common::ParamPackage& param) {
}
if (param.Has("button")) {
const QString button = QString::fromStdString(param.Get("button", ""));
- return QObject::tr("%1%2Button %3").arg(toggle, inverted, button);
+ return QObject::tr("%1%2%3Button %4").arg(turbo, toggle, inverted, button);
}
}
QString button_name = GetButtonName(common_button_name);
if (param.Has("hat")) {
- return QObject::tr("%1%2Hat %3").arg(toggle, inverted, button_name);
+ return QObject::tr("%1%2%3Hat %4").arg(turbo, toggle, inverted, button_name);
}
if (param.Has("axis")) {
return QObject::tr("%1%2Axis %3").arg(toggle, inverted, button_name);
@@ -214,7 +235,7 @@ QString ConfigureInputPlayer::ButtonToText(const Common::ParamPackage& param) {
return QObject::tr("%1%2Axis %3").arg(toggle, inverted, button_name);
}
if (param.Has("button")) {
- return QObject::tr("%1%2Button %3").arg(toggle, inverted, button_name);
+ return QObject::tr("%1%2%3Button %4").arg(turbo, toggle, inverted, button_name);
}
return QObject::tr("[unknown]");
@@ -375,6 +396,12 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_i
button_map[button_id]->setText(ButtonToText(param));
emulated_controller->SetButtonParam(button_id, param);
});
+ context_menu.addAction(tr("Turbo button"), [&] {
+ const bool turbo_value = !param.Get("turbo", false);
+ param.Set("turbo", turbo_value);
+ button_map[button_id]->setText(ButtonToText(param));
+ emulated_controller->SetButtonParam(button_id, param);
+ });
}
if (param.Has("axis")) {
context_menu.addAction(tr("Invert axis"), [&] {
@@ -1463,7 +1490,13 @@ void ConfigureInputPlayer::mousePressEvent(QMouseEvent* event) {
}
const auto button = GRenderWindow::QtButtonToMouseButton(event->button());
- input_subsystem->GetMouse()->PressButton(0, 0, 0, 0, button);
+ input_subsystem->GetMouse()->PressButton(0, 0, button);
+}
+
+void ConfigureInputPlayer::wheelEvent(QWheelEvent* event) {
+ const int x = event->angleDelta().x();
+ const int y = event->angleDelta().y();
+ input_subsystem->GetMouse()->MouseWheelChange(x, y);
}
void ConfigureInputPlayer::keyPressEvent(QKeyEvent* event) {
diff --git a/src/yuzu/configuration/configure_input_player.h b/src/yuzu/configuration/configure_input_player.h
index 6d1876f2b..99a9c875d 100644
--- a/src/yuzu/configuration/configure_input_player.h
+++ b/src/yuzu/configuration/configure_input_player.h
@@ -116,6 +116,9 @@ private:
/// Handle mouse button press events.
void mousePressEvent(QMouseEvent* event) override;
+ /// Handle mouse wheel move events.
+ void wheelEvent(QWheelEvent* event) override;
+
/// Handle key press events.
void keyPressEvent(QKeyEvent* event) override;
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() {
colors.outline = QColor(0, 0, 0);
colors.primary = QColor(225, 225, 225);
colors.button = QColor(109, 111, 114);
- colors.button2 = QColor(109, 111, 114);
colors.button2 = QColor(77, 80, 84);
colors.slider_arrow = QColor(65, 68, 73);
colors.font2 = QColor(0, 0, 0);
@@ -100,12 +99,17 @@ void PlayerControlPreview::UpdateColors() {
colors.led_off = QColor(170, 238, 255);
colors.indicator2 = QColor(59, 165, 93);
colors.charging = QColor(250, 168, 26);
+ colors.button_turbo = QColor(217, 158, 4);
colors.left = colors.primary;
colors.right = colors.primary;
- // Possible alternative to set colors from settings
- // colors.left = QColor(controller->GetColors().left.body);
- // colors.right = QColor(controller->GetColors().right.body);
+
+ const auto color_left = controller->GetColorsValues()[0].body;
+ const auto color_right = controller->GetColorsValues()[1].body;
+ if (color_left != 0 && color_right != 0) {
+ colors.left = QColor(color_left);
+ colors.right = QColor(color_right);
+ }
}
void PlayerControlPreview::ResetInputs() {
@@ -2465,7 +2469,6 @@ void PlayerControlPreview::DrawJoystickDot(QPainter& p, const QPointF center,
void PlayerControlPreview::DrawRoundButton(QPainter& p, QPointF center,
const Common::Input::ButtonStatus& pressed, float width,
float height, Direction direction, float radius) {
- p.setBrush(button_color);
if (pressed.value) {
switch (direction) {
case Direction::Left:
@@ -2483,16 +2486,16 @@ void PlayerControlPreview::DrawRoundButton(QPainter& p, QPointF center,
case Direction::None:
break;
}
- p.setBrush(colors.highlight);
}
QRectF rect = {center.x() - width, center.y() - height, width * 2.0f, height * 2.0f};
+ p.setBrush(GetButtonColor(button_color, pressed.value, pressed.turbo));
p.drawRoundedRect(rect, radius, radius);
}
void PlayerControlPreview::DrawMinusButton(QPainter& p, const QPointF center,
const Common::Input::ButtonStatus& pressed,
int button_size) {
p.setPen(colors.outline);
- p.setBrush(pressed.value ? colors.highlight : colors.button);
+ p.setBrush(GetButtonColor(colors.button, pressed.value, pressed.turbo));
DrawRectangle(p, center, button_size, button_size / 3.0f);
}
void PlayerControlPreview::DrawPlusButton(QPainter& p, const QPointF center,
@@ -2500,7 +2503,7 @@ void PlayerControlPreview::DrawPlusButton(QPainter& p, const QPointF center,
int button_size) {
// Draw outer line
p.setPen(colors.outline);
- p.setBrush(pressed.value ? colors.highlight : colors.button);
+ p.setBrush(GetButtonColor(colors.button, pressed.value, pressed.turbo));
DrawRectangle(p, center, button_size, button_size / 3.0f);
DrawRectangle(p, center, button_size / 3.0f, button_size);
@@ -2522,7 +2525,7 @@ void PlayerControlPreview::DrawGCButtonX(QPainter& p, const QPointF center,
}
p.setPen(colors.outline);
- p.setBrush(pressed.value ? colors.highlight : colors.button);
+ p.setBrush(GetButtonColor(colors.button, pressed.value, pressed.turbo));
DrawPolygon(p, button_x);
}
@@ -2535,7 +2538,7 @@ void PlayerControlPreview::DrawGCButtonY(QPainter& p, const QPointF center,
}
p.setPen(colors.outline);
- p.setBrush(pressed.value ? colors.highlight : colors.button);
+ p.setBrush(GetButtonColor(colors.button, pressed.value, pressed.turbo));
DrawPolygon(p, button_x);
}
@@ -2549,17 +2552,15 @@ void PlayerControlPreview::DrawGCButtonZ(QPainter& p, const QPointF center,
}
p.setPen(colors.outline);
- p.setBrush(pressed.value ? colors.highlight : colors.button2);
+ p.setBrush(GetButtonColor(colors.button2, pressed.value, pressed.turbo));
DrawPolygon(p, button_x);
}
void PlayerControlPreview::DrawCircleButton(QPainter& p, const QPointF center,
const Common::Input::ButtonStatus& pressed,
float button_size) {
- p.setBrush(button_color);
- if (pressed.value) {
- p.setBrush(colors.highlight);
- }
+
+ p.setBrush(GetButtonColor(button_color, pressed.value, pressed.turbo));
p.drawEllipse(center, button_size, button_size);
}
@@ -2616,7 +2617,7 @@ void PlayerControlPreview::DrawArrowButton(QPainter& p, const QPointF center,
// Draw arrow button
p.setPen(pressed.value ? colors.highlight : colors.button);
- p.setBrush(pressed.value ? colors.highlight : colors.button);
+ p.setBrush(GetButtonColor(colors.button, pressed.value, pressed.turbo));
DrawPolygon(p, arrow_button);
switch (direction) {
@@ -2668,10 +2669,20 @@ void PlayerControlPreview::DrawTriggerButton(QPainter& p, const QPointF center,
// Draw arrow button
p.setPen(colors.outline);
- p.setBrush(pressed.value ? colors.highlight : colors.button);
+ p.setBrush(GetButtonColor(colors.button, pressed.value, pressed.turbo));
DrawPolygon(p, qtrigger_button);
}
+QColor PlayerControlPreview::GetButtonColor(QColor default_color, bool is_pressed, bool turbo) {
+ if (is_pressed && turbo) {
+ return colors.button_turbo;
+ }
+ if (is_pressed) {
+ return colors.highlight;
+ }
+ return default_color;
+}
+
void PlayerControlPreview::DrawBattery(QPainter& p, QPointF center,
Common::Input::BatteryLevel battery) {
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:
QColor right{};
QColor button{};
QColor button2{};
+ QColor button_turbo{};
QColor font{};
QColor font2{};
QColor highlight{};
@@ -183,6 +184,7 @@ private:
const Common::Input::ButtonStatus& pressed, float size = 1.0f);
void DrawTriggerButton(QPainter& p, QPointF center, Direction direction,
const Common::Input::ButtonStatus& pressed);
+ QColor GetButtonColor(QColor default_color, bool is_pressed, bool turbo);
// Draw battery functions
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,
"using-a-controller-or-android-phone-for-motion-or-touch-input'><span "
"style=\"text-decoration: underline; color:#039be5;\">Learn More</span></a>"));
- setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);
SetConfiguration();
UpdateUiDisplay();
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
setFocusPolicy(Qt::ClickFocus);
setWindowTitle(tr("Properties"));
- // remove Help question mark button from the title bar
- setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);
addons_tab->SetTitleId(title_id);
diff --git a/src/yuzu/configuration/configure_ringcon.cpp b/src/yuzu/configuration/configure_ringcon.cpp
index 688c2dd38..71afbc423 100644
--- a/src/yuzu/configuration/configure_ringcon.cpp
+++ b/src/yuzu/configuration/configure_ringcon.cpp
@@ -4,9 +4,11 @@
#include <memory>
#include <QKeyEvent>
#include <QMenu>
+#include <QMessageBox>
#include <QTimer>
+#include <fmt/format.h>
-#include "core/hid/emulated_devices.h"
+#include "core/hid/emulated_controller.h"
#include "core/hid/hid_core.h"
#include "input_common/drivers/keyboard.h"
#include "input_common/drivers/mouse.h"
@@ -126,9 +128,16 @@ ConfigureRingController::ConfigureRingController(QWidget* parent,
ui->buttonRingAnalogPush,
};
- emulated_device = hid_core_.GetEmulatedDevices();
- emulated_device->SaveCurrentConfig();
- emulated_device->EnableConfiguration();
+ emulated_controller = hid_core_.GetEmulatedController(Core::HID::NpadIdType::Player1);
+ emulated_controller->SaveCurrentConfig();
+ emulated_controller->EnableConfiguration();
+
+ Core::HID::ControllerUpdateCallback engine_callback{
+ .on_change = [this](Core::HID::ControllerTriggerType type) { ControllerUpdate(type); },
+ .is_npad_service = false,
+ };
+ callback_key = emulated_controller->SetCallback(engine_callback);
+ is_controller_set = true;
LoadConfiguration();
@@ -143,9 +152,9 @@ ConfigureRingController::ConfigureRingController(QWidget* parent,
HandleClick(
analog_map_buttons[sub_button_id],
[=, this](const Common::ParamPackage& params) {
- Common::ParamPackage param = emulated_device->GetRingParam();
+ Common::ParamPackage param = emulated_controller->GetRingParam();
SetAnalogParam(params, param, analog_sub_buttons[sub_button_id]);
- emulated_device->SetRingParam(param);
+ emulated_controller->SetRingParam(param);
},
InputCommon::Polling::InputType::Stick);
});
@@ -155,16 +164,16 @@ ConfigureRingController::ConfigureRingController(QWidget* parent,
connect(analog_button, &QPushButton::customContextMenuRequested,
[=, this](const QPoint& menu_location) {
QMenu context_menu;
- Common::ParamPackage param = emulated_device->GetRingParam();
+ Common::ParamPackage param = emulated_controller->GetRingParam();
context_menu.addAction(tr("Clear"), [&] {
- emulated_device->SetRingParam({});
+ emulated_controller->SetRingParam(param);
analog_map_buttons[sub_button_id]->setText(tr("[not set]"));
});
context_menu.addAction(tr("Invert axis"), [&] {
const bool invert_value = param.Get("invert_x", "+") == "-";
const std::string invert_str = invert_value ? "+" : "-";
param.Set("invert_x", invert_str);
- emulated_device->SetRingParam(param);
+ emulated_controller->SetRingParam(param);
for (int sub_button_id2 = 0; sub_button_id2 < ANALOG_SUB_BUTTONS_NUM;
++sub_button_id2) {
analog_map_buttons[sub_button_id2]->setText(
@@ -177,16 +186,19 @@ ConfigureRingController::ConfigureRingController(QWidget* parent,
}
connect(ui->sliderRingAnalogDeadzone, &QSlider::valueChanged, [=, this] {
- Common::ParamPackage param = emulated_device->GetRingParam();
+ Common::ParamPackage param = emulated_controller->GetRingParam();
const auto slider_value = ui->sliderRingAnalogDeadzone->value();
ui->labelRingAnalogDeadzone->setText(tr("Deadzone: %1%").arg(slider_value));
param.Set("deadzone", slider_value / 100.0f);
- emulated_device->SetRingParam(param);
+ emulated_controller->SetRingParam(param);
});
connect(ui->restore_defaults_button, &QPushButton::clicked, this,
&ConfigureRingController::RestoreDefaults);
+ connect(ui->enable_ring_controller_button, &QPushButton::clicked, this,
+ &ConfigureRingController::EnableRingController);
+
timeout_timer->setSingleShot(true);
connect(timeout_timer.get(), &QTimer::timeout, [this] { SetPollingResult({}, true); });
@@ -202,7 +214,14 @@ ConfigureRingController::ConfigureRingController(QWidget* parent,
}
ConfigureRingController::~ConfigureRingController() {
- emulated_device->DisableConfiguration();
+ emulated_controller->SetPollingMode(Core::HID::EmulatedDeviceIndex::RightIndex,
+ Common::Input::PollingMode::Active);
+ emulated_controller->DisableConfiguration();
+
+ if (is_controller_set) {
+ emulated_controller->DeleteCallback(callback_key);
+ is_controller_set = false;
+ }
};
void ConfigureRingController::changeEvent(QEvent* event) {
@@ -219,7 +238,7 @@ void ConfigureRingController::RetranslateUI() {
void ConfigureRingController::UpdateUI() {
RetranslateUI();
- const Common::ParamPackage param = emulated_device->GetRingParam();
+ const Common::ParamPackage param = emulated_controller->GetRingParam();
for (int sub_button_id = 0; sub_button_id < ANALOG_SUB_BUTTONS_NUM; ++sub_button_id) {
auto* const analog_button = analog_map_buttons[sub_button_id];
@@ -240,9 +259,9 @@ void ConfigureRingController::UpdateUI() {
}
void ConfigureRingController::ApplyConfiguration() {
- emulated_device->DisableConfiguration();
- emulated_device->SaveCurrentConfig();
- emulated_device->EnableConfiguration();
+ emulated_controller->DisableConfiguration();
+ emulated_controller->SaveCurrentConfig();
+ emulated_controller->EnableConfiguration();
}
void ConfigureRingController::LoadConfiguration() {
@@ -252,10 +271,62 @@ void ConfigureRingController::LoadConfiguration() {
void ConfigureRingController::RestoreDefaults() {
const std::string default_ring_string = InputCommon::GenerateAnalogParamFromKeys(
0, 0, Config::default_ringcon_analogs[0], Config::default_ringcon_analogs[1], 0, 0.05f);
- emulated_device->SetRingParam(Common::ParamPackage(default_ring_string));
+ emulated_controller->SetRingParam(Common::ParamPackage(default_ring_string));
UpdateUI();
}
+void ConfigureRingController::EnableRingController() {
+ const auto dialog_title = tr("Error enabling ring input");
+
+ is_ring_enabled = false;
+ ui->ring_controller_sensor_value->setText(tr("Not connected"));
+
+ if (!Settings::values.enable_joycon_driver) {
+ QMessageBox::warning(this, dialog_title, tr("Direct Joycon driver is not enabled"));
+ return;
+ }
+
+ ui->enable_ring_controller_button->setEnabled(false);
+ ui->enable_ring_controller_button->setText(tr("Configuring"));
+ // SetPollingMode is blocking. Allow to update the button status before calling the command
+ repaint();
+
+ const auto result = emulated_controller->SetPollingMode(
+ Core::HID::EmulatedDeviceIndex::RightIndex, Common::Input::PollingMode::Ring);
+ switch (result) {
+ case Common::Input::DriverResult::Success:
+ is_ring_enabled = true;
+ break;
+ case Common::Input::DriverResult::NotSupported:
+ QMessageBox::warning(this, dialog_title,
+ tr("The current mapped device doesn't support the ring controller"));
+ break;
+ case Common::Input::DriverResult::NoDeviceDetected:
+ QMessageBox::warning(this, dialog_title,
+ tr("The current mapped device doesn't have a ring attached"));
+ break;
+ default:
+ QMessageBox::warning(this, dialog_title,
+ tr("Unexpected driver result %1").arg(static_cast<int>(result)));
+ break;
+ }
+ ui->enable_ring_controller_button->setEnabled(true);
+ ui->enable_ring_controller_button->setText(tr("Enable"));
+}
+
+void ConfigureRingController::ControllerUpdate(Core::HID::ControllerTriggerType type) {
+ if (!is_ring_enabled) {
+ return;
+ }
+ if (type != Core::HID::ControllerTriggerType::RingController) {
+ return;
+ }
+
+ const auto value = emulated_controller->GetRingSensorValues();
+ const auto tex_value = QString::fromStdString(fmt::format("{:.3f}", value.raw_value));
+ ui->ring_controller_sensor_value->setText(tex_value);
+}
+
void ConfigureRingController::HandleClick(
QPushButton* button, std::function<void(const Common::ParamPackage&)> new_input_setter,
InputCommon::Polling::InputType type) {
@@ -300,7 +371,7 @@ void ConfigureRingController::mousePressEvent(QMouseEvent* event) {
}
const auto button = GRenderWindow::QtButtonToMouseButton(event->button());
- input_subsystem->GetMouse()->PressButton(0, 0, 0, 0, button);
+ input_subsystem->GetMouse()->PressButton(0, 0, button);
}
void ConfigureRingController::keyPressEvent(QKeyEvent* event) {
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;
namespace Core::HID {
class HIDCore;
-class EmulatedDevices;
+class EmulatedController;
} // namespace Core::HID
namespace Ui {
@@ -42,6 +42,12 @@ private:
/// Restore all buttons to their default values.
void RestoreDefaults();
+ /// Sets current polling mode to ring input
+ void EnableRingController();
+
+ // Handles emulated controller events
+ void ControllerUpdate(Core::HID::ControllerTriggerType type);
+
/// Called when the button was pressed.
void HandleClick(QPushButton* button,
std::function<void(const Common::ParamPackage&)> new_input_setter,
@@ -78,7 +84,11 @@ private:
std::optional<std::function<void(const Common::ParamPackage&)>> input_setter;
InputCommon::InputSubsystem* input_subsystem;
- Core::HID::EmulatedDevices* emulated_device;
+ Core::HID::EmulatedController* emulated_controller;
+
+ bool is_ring_enabled{};
+ bool is_controller_set{};
+ int callback_key;
std::unique_ptr<Ui::ConfigureRingController> ui;
};
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 @@
<rect>
<x>0</x>
<y>0</y>
- <width>298</width>
- <height>339</height>
+ <width>315</width>
+ <height>400</height>
</rect>
</property>
<property name="windowTitle">
@@ -46,187 +46,283 @@
</property>
</spacer>
</item>
- <item>
- <widget class="QGroupBox" name="RingAnalog">
- <property name="title">
- <string>Ring Sensor Parameters</string>
- </property>
- <property name="alignment">
- <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
- </property>
- <layout class="QVBoxLayout" name="verticalLayout_3">
- <property name="spacing">
- <number>0</number>
- </property>
- <property name="sizeConstraint">
- <enum>QLayout::SetDefaultConstraint</enum>
- </property>
- <property name="leftMargin">
- <number>3</number>
- </property>
- <property name="topMargin">
- <number>6</number>
- </property>
- <property name="rightMargin">
- <number>3</number>
- </property>
- <property name="bottomMargin">
- <number>0</number>
- </property>
- <item>
- <layout class="QHBoxLayout" name="buttonRingAnalogPullHorizontaLayout">
+ <item>
+ <widget class="QGroupBox" name="RingAnalog">
+ <property name="title">
+ <string>Virtual Ring Sensor Parameters</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout_1">
<property name="spacing">
- <number>3</number>
+ <number>0</number>
</property>
- <item alignment="Qt::AlignHCenter">
- <widget class="QGroupBox" name="buttonRingAnalogPullGroup">
- <property name="title">
- <string>Pull</string>
- </property>
- <property name="alignment">
- <set>Qt::AlignCenter</set>
+ <property name="sizeConstraint">
+ <enum>QLayout::SetDefaultConstraint</enum>
+ </property>
+ <property name="leftMargin">
+ <number>3</number>
+ </property>
+ <property name="topMargin">
+ <number>6</number>
+ </property>
+ <property name="rightMargin">
+ <number>3</number>
+ </property>
+ <property name="bottomMargin">
+ <number>0</number>
+ </property>
+ <item>
+ <layout class="QHBoxLayout" name="buttonRingAnalogPullHorizontaLayout">
+ <property name="spacing">
+ <number>3</number>
</property>
- <layout class="QVBoxLayout" name="buttonRingAnalogPullVerticalLayout">
- <property name="spacing">
- <number>3</number>
+ <item alignment="Qt::AlignHCenter">
+ <widget class="QGroupBox" name="buttonRingAnalogPullGroup">
+ <property name="title">
+ <string>Pull</string>
</property>
- <property name="leftMargin">
- <number>3</number>
- </property>
- <property name="topMargin">
- <number>3</number>
+ <property name="alignment">
+ <set>Qt::AlignCenter</set>
</property>
- <property name="rightMargin">
- <number>3</number>
+ <layout class="QVBoxLayout" name="buttonRingAnalogPullVerticalLayout">
+ <property name="spacing">
+ <number>3</number>
+ </property>
+ <property name="leftMargin">
+ <number>3</number>
+ </property>
+ <property name="topMargin">
+ <number>3</number>
+ </property>
+ <property name="rightMargin">
+ <number>3</number>
+ </property>
+ <property name="bottomMargin">
+ <number>3</number>
+ </property>
+ <item>
+ <widget class="QPushButton" name="buttonRingAnalogPull">
+ <property name="minimumSize">
+ <size>
+ <width>70</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>68</width>
+ <height>16777215</height>
+ </size>
+ </property>
+ <property name="styleSheet">
+ <string notr="true">min-width: 68px;</string>
+ </property>
+ <property name="text">
+ <string>Pull</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item alignment="Qt::AlignHCenter">
+ <widget class="QGroupBox" name="buttonRingAnalogPushGroup">
+ <property name="title">
+ <string>Push</string>
</property>
- <property name="bottomMargin">
- <number>3</number>
+ <property name="alignment">
+ <set>Qt::AlignCenter</set>
</property>
- <item>
- <widget class="QPushButton" name="buttonRingAnalogPull">
- <property name="minimumSize">
- <size>
- <width>68</width>
- <height>0</height>
- </size>
- </property>
- <property name="maximumSize">
- <size>
- <width>68</width>
- <height>16777215</height>
- </size>
- </property>
- <property name="styleSheet">
- <string notr="true">min-width: 68px;</string>
- </property>
- <property name="text">
- <string>Pull</string>
- </property>
- </widget>
- </item>
- </layout>
- </widget>
+ <layout class="QVBoxLayout" name="buttonRingAnalogPushVerticalLayout">
+ <property name="spacing">
+ <number>3</number>
+ </property>
+ <property name="leftMargin">
+ <number>3</number>
+ </property>
+ <property name="topMargin">
+ <number>3</number>
+ </property>
+ <property name="rightMargin">
+ <number>3</number>
+ </property>
+ <property name="bottomMargin">
+ <number>3</number>
+ </property>
+ <item>
+ <widget class="QPushButton" name="buttonRingAnalogPush">
+ <property name="minimumSize">
+ <size>
+ <width>70</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>68</width>
+ <height>16777215</height>
+ </size>
+ </property>
+ <property name="styleSheet">
+ <string notr="true">min-width: 68px;</string>
+ </property>
+ <property name="text">
+ <string>Push</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ </layout>
</item>
- <item alignment="Qt::AlignHCenter">
- <widget class="QGroupBox" name="buttonRingAnalogPushGroup">
- <property name="title">
- <string>Push</string>
+ <item>
+ <layout class="QVBoxLayout" name="sliderRingAnalogDeadzoneVerticalLayout">
+ <property name="spacing">
+ <number>3</number>
</property>
- <property name="alignment">
- <set>Qt::AlignCenter</set>
+ <property name="sizeConstraint">
+ <enum>QLayout::SetDefaultConstraint</enum>
</property>
- <layout class="QVBoxLayout" name="buttonRingAnalogPushVerticalLayout">
- <property name="spacing">
- <number>3</number>
- </property>
- <property name="leftMargin">
- <number>3</number>
- </property>
- <property name="topMargin">
- <number>3</number>
- </property>
- <property name="rightMargin">
- <number>3</number>
- </property>
- <property name="bottomMargin">
- <number>3</number>
- </property>
+ <property name="leftMargin">
+ <number>0</number>
+ </property>
+ <property name="topMargin">
+ <number>10</number>
+ </property>
+ <property name="rightMargin">
+ <number>0</number>
+ </property>
+ <property name="bottomMargin">
+ <number>3</number>
+ </property>
+ <item>
+ <layout class="QHBoxLayout" name="sliderRingAnalogDeadzoneHorizontalLayout">
<item>
- <widget class="QPushButton" name="buttonRingAnalogPush">
- <property name="minimumSize">
- <size>
- <width>68</width>
- <height>0</height>
- </size>
- </property>
- <property name="maximumSize">
- <size>
- <width>68</width>
- <height>16777215</height>
- </size>
- </property>
- <property name="styleSheet">
- <string notr="true">min-width: 68px;</string>
- </property>
+ <widget class="QLabel" name="labelRingAnalogDeadzone">
<property name="text">
- <string>Push</string>
+ <string>Deadzone: 0%</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignHCenter</set>
</property>
- </widget>
+ </widget>
</item>
- </layout>
- </widget>
+ </layout>
+ </item>
+ <item>
+ <widget class="QSlider" name="sliderRingAnalogDeadzone">
+ <property name="maximum">
+ <number>100</number>
+ </property>
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ </widget>
+ </item>
+ </layout>
</item>
- </layout>
- </item>
- <item>
- <layout class="QVBoxLayout" name="sliderRingAnalogDeadzoneVerticalLayout">
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <widget class="QGroupBox" name="RingDriver">
+ <property name="title">
+ <string>Direct Joycon Driver</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout_2">
<property name="spacing">
- <number>3</number>
+ <number>0</number>
</property>
<property name="sizeConstraint">
- <enum>QLayout::SetDefaultConstraint</enum>
+ <enum>QLayout::SetDefaultConstraint</enum>
</property>
<property name="leftMargin">
- <number>0</number>
+ <number>3</number>
</property>
<property name="topMargin">
- <number>10</number>
+ <number>6</number>
</property>
<property name="rightMargin">
- <number>0</number>
+ <number>3</number>
</property>
<property name="bottomMargin">
- <number>3</number>
+ <number>10</number>
</property>
<item>
- <layout class="QHBoxLayout" name="sliderRingAnalogDeadzoneHorizontalLayout">
- <item>
- <widget class="QLabel" name="labelRingAnalogDeadzone">
+ <layout class="QGridLayout" name="gridLayout">
+ <property name="leftMargin">
+ <number>10</number>
+ </property>
+ <property name="topMargin">
+ <number>6</number>
+ </property>
+ <property name="rightMargin">
+ <number>10</number>
+ </property>
+ <property name="bottomMargin">
+ <number>10</number>
+ </property>
+ <property name="verticalSpacing">
+ <number>10</number>
+ </property>
+ <item row="0" column="1">
+ <spacer name="horizontalSpacer">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>QSizePolicy::Fixed</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>76</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item row="0" column="0">
+ <widget class="QLabel" name="enable_ring_controller_label">
+ <property name="text">
+ <string>Enable Ring Input</string>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="2">
+ <widget class="QPushButton" name="enable_ring_controller_button">
<property name="text">
- <string>Deadzone: 0%</string>
+ <string>Enable</string>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="0">
+ <widget class="QLabel" name="ring_controller_sensor_label">
+ <property name="text">
+ <string>Ring Sensor Value</string>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="2">
+ <widget class="QLabel" name="ring_controller_sensor_value">
+ <property name="text">
+ <string>Not connected</string>
</property>
<property name="alignment">
- <set>Qt::AlignHCenter</set>
+ <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
- </widget>
+ </widget>
</item>
- </layout>
- </item>
- <item>
- <widget class="QSlider" name="sliderRingAnalogDeadzone">
- <property name="maximum">
- <number>100</number>
- </property>
- <property name="orientation">
- <enum>Qt::Horizontal</enum>
- </property>
- </widget>
+ </layout>
</item>
- </layout>
- </item>
- </layout>
- </widget>
- </item>
+ </layout>
+ </widget>
+ </item>
<item>
<spacer name="verticalSpacer">
<property name="orientation">
@@ -273,6 +369,6 @@
<signal>rejected()</signal>
<receiver>ConfigureRingController</receiver>
<slot>reject()</slot>
- </connection>
+ </connection>
</connections>
</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{
};
static bool IsValidLocale(u32 region_index, u32 language_index) {
+ if (region_index >= LOCALE_BLOCKLIST.size()) {
+ return false;
+ }
return ((LOCALE_BLOCKLIST.at(region_index) >> language_index) & 1) == 0;
}
@@ -55,8 +58,11 @@ ConfigureSystem::ConfigureSystem(Core::System& system_, QWidget* parent)
});
const auto locale_check = [this](int index) {
- const bool valid_locale =
- IsValidLocale(ui->combo_region->currentIndex(), ui->combo_language->currentIndex());
+ const auto region_index = ConfigurationShared::GetComboboxIndex(
+ Settings::values.region_index.GetValue(true), ui->combo_region);
+ const auto language_index = ConfigurationShared::GetComboboxIndex(
+ Settings::values.language_index.GetValue(true), ui->combo_language);
+ const bool valid_locale = IsValidLocale(region_index, language_index);
ui->label_warn_invalid_locale->setVisible(!valid_locale);
if (!valid_locale) {
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:
std::unique_ptr<Ui::ConfigureSystem> ui;
bool enabled = false;
- int language_index = 0;
- int region_index = 0;
- int time_zone_index = 0;
- int sound_index = 0;
-
ConfigurationShared::CheckState use_rng_seed;
- ConfigurationShared::CheckState use_custom_rtc;
Core::System& system;
};
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)
setFocusPolicy(Qt::ClickFocus);
setWindowTitle(tr("TAS Configuration"));
- setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);
connect(ui->tas_path_button, &QToolButton::pressed, this,
[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() {
std::vector<std::string> profile_names;
profile_names.reserve(map_profiles.size());
- for (const auto& [profile_name, config] : map_profiles) {
+ auto it = map_profiles.cbegin();
+ while (it != map_profiles.cend()) {
+ const auto& [profile_name, config] = *it;
if (!ProfileExistsInFilesystem(profile_name)) {
- DeleteProfile(profile_name);
+ it = map_profiles.erase(it);
continue;
}
profile_names.push_back(profile_name);
+ ++it;
}
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_,
setWindowTitle(tr("Controller P1"));
resize(500, 350);
setMinimumSize(500, 350);
- // Remove the "?" button from the titlebar and enable the maximize button
- setWindowFlags((windowFlags() & ~Qt::WindowContextHelpButtonHint) |
- Qt::WindowMaximizeButtonHint);
+ // Enable the maximize button
+ setWindowFlags(windowFlags() | Qt::WindowMaximizeButtonHint);
widget = new PlayerControlPreview(this);
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
setObjectName(QStringLiteral("MicroProfile"));
setWindowTitle(tr("&MicroProfile"));
resize(1000, 600);
- // Remove the "?" button from the titlebar and enable the maximize button
- setWindowFlags((windowFlags() & ~Qt::WindowContextHelpButtonHint) |
- Qt::WindowMaximizeButtonHint);
+ // Enable the maximize button
+ setWindowFlags(windowFlags() | Qt::WindowMaximizeButtonHint);
#if MICROPROFILE_ENABLED
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 @@
#include <chrono>
#include <string>
#include <discord_rpc.h>
+#include <fmt/format.h>
+#include <httplib.h>
#include "common/common_types.h"
+#include "common/string_util.h"
#include "core/core.h"
#include "core/loader/loader.h"
#include "yuzu/discord_impl.h"
@@ -14,7 +17,6 @@ namespace DiscordRPC {
DiscordImpl::DiscordImpl(Core::System& system_) : system{system_} {
DiscordEventHandlers handlers{};
-
// The number is the client ID for yuzu, it's used for images and the
// application name
Discord_Initialize("712465656758665259", &handlers, 1, nullptr);
@@ -29,23 +31,74 @@ void DiscordImpl::Pause() {
Discord_ClearPresence();
}
+static std::string GetGameString(const std::string& title) {
+ // Convert to lowercase
+ std::string icon_name = Common::ToLower(title);
+
+ // Replace spaces with dashes
+ std::replace(icon_name.begin(), icon_name.end(), ' ', '-');
+
+ // Remove non-alphanumeric characters but keep dashes
+ std::erase_if(icon_name, [](char c) { return !std::isalnum(c) && c != '-'; });
+
+ // Remove dashes from the start and end of the string
+ icon_name.erase(icon_name.begin(), std::find_if(icon_name.begin(), icon_name.end(),
+ [](int ch) { return ch != '-'; }));
+ icon_name.erase(
+ std::find_if(icon_name.rbegin(), icon_name.rend(), [](int ch) { return ch != '-'; }).base(),
+ icon_name.end());
+
+ // Remove double dashes
+ icon_name.erase(std::unique(icon_name.begin(), icon_name.end(),
+ [](char a, char b) { return a == '-' && b == '-'; }),
+ icon_name.end());
+
+ return icon_name;
+}
+
void DiscordImpl::Update() {
s64 start_time = std::chrono::duration_cast<std::chrono::seconds>(
std::chrono::system_clock::now().time_since_epoch())
.count();
+ const std::string default_text = "yuzu is an emulator for the Nintendo Switch";
+ const std::string default_image = "yuzu_logo";
+ std::string game_cover_url = "https://yuzu-emu.org";
std::string title;
- if (system.IsPoweredOn()) {
- system.GetAppLoader().ReadTitle(title);
- }
+
DiscordRichPresence presence{};
- presence.largeImageKey = "yuzu_logo";
- presence.largeImageText = "yuzu is an emulator for the Nintendo Switch";
+
if (system.IsPoweredOn()) {
+ system.GetAppLoader().ReadTitle(title);
+
+ // Used to format Icon URL for yuzu website game compatibility page
+ std::string icon_name = GetGameString(title);
+
+ // New Check for game cover
+ httplib::Client cli(game_cover_url);
+
+ if (auto res = cli.Head(fmt::format("/images/game/boxart/{}.png", icon_name).c_str())) {
+ if (res->status == 200) {
+ game_cover_url += fmt::format("/images/game/boxart/{}.png", icon_name);
+ } else {
+ game_cover_url = "yuzu_logo";
+ }
+ } else {
+ game_cover_url = "yuzu_logo";
+ }
+
+ presence.largeImageKey = game_cover_url.c_str();
+ presence.largeImageText = title.c_str();
+
+ presence.smallImageKey = default_image.c_str();
+ presence.smallImageText = default_text.c_str();
presence.state = title.c_str();
presence.details = "Currently in game";
} else {
- presence.details = "Not in game";
+ presence.largeImageKey = default_image.c_str();
+ presence.largeImageText = default_text.c_str();
+ presence.details = "Currently not in game";
}
+
presence.startTimestamp = start_time;
Discord_UpdatePresence(&presence);
}
diff --git a/src/yuzu/game_list.cpp b/src/yuzu/game_list.cpp
index 22aa19c56..c21828b1d 100644
--- a/src/yuzu/game_list.cpp
+++ b/src/yuzu/game_list.cpp
@@ -870,6 +870,7 @@ void GameList::ToggleFavorite(u64 program_id) {
tree_view->setRowHidden(0, item_model->invisibleRootItem()->index(), true);
}
}
+ SaveConfig();
}
void GameList::AddFavorite(u64 program_id) {
diff --git a/src/yuzu/game_list.h b/src/yuzu/game_list.h
index f7ff93ed9..64e5af4c1 100644
--- a/src/yuzu/game_list.h
+++ b/src/yuzu/game_list.h
@@ -122,6 +122,7 @@ signals:
void AddDirectory();
void ShowList(bool show);
void PopulatingCompleted();
+ void SaveConfig();
private slots:
void OnItemExpanded(const QModelIndex& item);
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() {
{hotkey.first, group.first,
UISettings::ContextualShortcut({hotkey.second.keyseq.toString(),
hotkey.second.controller_keyseq,
- hotkey.second.context})});
+ hotkey.second.context, hotkey.second.repeat})});
}
}
}
@@ -47,6 +47,7 @@ void HotkeyRegistry::LoadHotkeys() {
hk.controller_shortcut->disconnect();
hk.controller_shortcut->SetKey(hk.controller_keyseq);
}
+ hk.repeat = shortcut.shortcut.repeat;
}
}
@@ -57,8 +58,7 @@ QShortcut* HotkeyRegistry::GetHotkey(const QString& group, const QString& action
hk.shortcut = new QShortcut(hk.keyseq, widget, nullptr, nullptr, hk.context);
}
- hk.shortcut->setAutoRepeat(false);
-
+ hk.shortcut->setAutoRepeat(hk.repeat);
return hk.shortcut;
}
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:
QShortcut* shortcut = nullptr;
ControllerShortcut* controller_shortcut = nullptr;
Qt::ShortcutContext context = Qt::WindowShortcut;
+ bool repeat;
};
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
vbox_layout->addLayout(hbox_layout);
setLayout(vbox_layout);
- setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);
setWindowTitle(tr("Install Files to NAND"));
}
diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp
index c55f81c2f..f233b065e 100644
--- a/src/yuzu/main.cpp
+++ b/src/yuzu/main.cpp
@@ -219,7 +219,7 @@ static void LogRuntimes() {
#ifdef _MSC_VER
// It is possible that the name of the dll will change.
// vcruntime140.dll is for 2015 and onwards
- constexpr char runtime_dll_name[] = "vcruntime140.dll";
+ static constexpr char runtime_dll_name[] = "vcruntime140.dll";
UINT sz = GetFileVersionInfoSizeA(runtime_dll_name, nullptr);
bool runtime_version_inspection_worked = false;
if (sz > 0) {
@@ -680,8 +680,10 @@ void GMainWindow::SoftwareKeyboardShowNormal() {
const auto y = layout.screen.top;
const auto w = layout.screen.GetWidth();
const auto h = layout.screen.GetHeight();
+ const auto scale_ratio = devicePixelRatioF();
- software_keyboard->ShowNormalKeyboard(render_window->mapToGlobal(QPoint(x, y)), QSize(w, h));
+ software_keyboard->ShowNormalKeyboard(render_window->mapToGlobal(QPoint(x, y) / scale_ratio),
+ QSize(w, h) / scale_ratio);
}
void GMainWindow::SoftwareKeyboardShowTextCheck(
@@ -714,9 +716,11 @@ void GMainWindow::SoftwareKeyboardShowInline(
(1.0f - appear_parameters.key_top_scale_y))));
const auto w = static_cast<int>(layout.screen.GetWidth() * appear_parameters.key_top_scale_x);
const auto h = static_cast<int>(layout.screen.GetHeight() * appear_parameters.key_top_scale_y);
+ const auto scale_ratio = devicePixelRatioF();
software_keyboard->ShowInlineKeyboard(std::move(appear_parameters),
- render_window->mapToGlobal(QPoint(x, y)), QSize(w, h));
+ render_window->mapToGlobal(QPoint(x, y) / scale_ratio),
+ QSize(w, h) / scale_ratio);
}
void GMainWindow::SoftwareKeyboardHideInline() {
@@ -796,9 +800,12 @@ void GMainWindow::WebBrowserOpenWebPage(const std::string& main_url,
}
const auto& layout = render_window->GetFramebufferLayout();
- web_browser_view.resize(layout.screen.GetWidth(), layout.screen.GetHeight());
- web_browser_view.move(layout.screen.left, layout.screen.top + menuBar()->height());
- web_browser_view.setZoomFactor(static_cast<qreal>(layout.screen.GetWidth()) /
+ const auto scale_ratio = devicePixelRatioF();
+ web_browser_view.resize(layout.screen.GetWidth() / scale_ratio,
+ layout.screen.GetHeight() / scale_ratio);
+ web_browser_view.move(layout.screen.left / scale_ratio,
+ (layout.screen.top / scale_ratio) + menuBar()->height());
+ web_browser_view.setZoomFactor(static_cast<qreal>(layout.screen.GetWidth() / scale_ratio) /
static_cast<qreal>(Layout::ScreenUndocked::Width));
web_browser_view.setFocus();
@@ -957,6 +964,38 @@ void GMainWindow::InitializeWidgets() {
tas_label->setFocusPolicy(Qt::NoFocus);
statusBar()->insertPermanentWidget(0, tas_label);
+ volume_popup = new QWidget(this);
+ volume_popup->setWindowFlags(Qt::FramelessWindowHint | Qt::NoDropShadowWindowHint | Qt::Popup);
+ volume_popup->setLayout(new QVBoxLayout());
+ volume_popup->setMinimumWidth(200);
+
+ volume_slider = new QSlider(Qt::Horizontal);
+ volume_slider->setObjectName(QStringLiteral("volume_slider"));
+ volume_slider->setMaximum(200);
+ volume_slider->setPageStep(5);
+ connect(volume_slider, &QSlider::valueChanged, this, [this](int percentage) {
+ Settings::values.audio_muted = false;
+ const auto volume = static_cast<u8>(percentage);
+ Settings::values.volume.SetValue(volume);
+ UpdateVolumeUI();
+ });
+ volume_popup->layout()->addWidget(volume_slider);
+
+ volume_button = new QPushButton();
+ volume_button->setObjectName(QStringLiteral("TogglableStatusBarButton"));
+ volume_button->setFocusPolicy(Qt::NoFocus);
+ volume_button->setCheckable(true);
+ UpdateVolumeUI();
+ connect(volume_button, &QPushButton::clicked, this, [&] {
+ UpdateVolumeUI();
+ volume_popup->setVisible(!volume_popup->isVisible());
+ QRect rect = volume_button->geometry();
+ QPoint bottomLeft = statusBar()->mapToGlobal(rect.topLeft());
+ bottomLeft.setY(bottomLeft.y() - volume_popup->geometry().height());
+ volume_popup->setGeometry(QRect(bottomLeft, QSize(rect.width(), rect.height())));
+ });
+ statusBar()->insertPermanentWidget(0, volume_button);
+
// setup AA button
aa_status_button = new QPushButton();
aa_status_button->setObjectName(QStringLiteral("TogglableStatusBarButton"));
@@ -983,11 +1022,6 @@ void GMainWindow::InitializeWidgets() {
filter_status_button->setFocusPolicy(Qt::NoFocus);
connect(filter_status_button, &QPushButton::clicked, this,
&GMainWindow::OnToggleAdaptingFilter);
- auto filter = Settings::values.scaling_filter.GetValue();
- if (Settings::values.renderer_backend.GetValue() == Settings::RendererBackend::OpenGL &&
- filter == Settings::ScalingFilter::Fsr) {
- Settings::values.scaling_filter.SetValue(Settings::ScalingFilter::NearestNeighbor);
- }
UpdateFilterText();
filter_status_button->setCheckable(true);
filter_status_button->setChecked(true);
@@ -1124,34 +1158,21 @@ void GMainWindow::InitializeHotkeys() {
&GMainWindow::OnToggleAdaptingFilter);
connect_shortcut(QStringLiteral("Change Docked Mode"), &GMainWindow::OnToggleDockedMode);
connect_shortcut(QStringLiteral("Change GPU Accuracy"), &GMainWindow::OnToggleGpuAccuracy);
- connect_shortcut(QStringLiteral("Audio Mute/Unmute"),
- [] { Settings::values.audio_muted = !Settings::values.audio_muted; });
- connect_shortcut(QStringLiteral("Audio Volume Down"), [] {
- const auto current_volume = static_cast<s32>(Settings::values.volume.GetValue());
- int step = 5;
- if (current_volume <= 30) {
- step = 2;
- }
- if (current_volume <= 6) {
- step = 1;
- }
- Settings::values.volume.SetValue(std::max(current_volume - step, 0));
- });
- connect_shortcut(QStringLiteral("Audio Volume Up"), [] {
- const auto current_volume = static_cast<s32>(Settings::values.volume.GetValue());
- int step = 5;
- if (current_volume < 30) {
- step = 2;
- }
- if (current_volume < 6) {
- step = 1;
- }
- Settings::values.volume.SetValue(current_volume + step);
- });
+ connect_shortcut(QStringLiteral("Audio Mute/Unmute"), &GMainWindow::OnMute);
+ connect_shortcut(QStringLiteral("Audio Volume Down"), &GMainWindow::OnDecreaseVolume);
+ connect_shortcut(QStringLiteral("Audio Volume Up"), &GMainWindow::OnIncreaseVolume);
connect_shortcut(QStringLiteral("Toggle Framerate Limit"), [] {
Settings::values.use_speed_limit.SetValue(!Settings::values.use_speed_limit.GetValue());
});
connect_shortcut(QStringLiteral("Toggle Mouse Panning"), [&] {
+ if (Settings::values.mouse_enabled) {
+ Settings::values.mouse_panning = false;
+ QMessageBox::warning(
+ this, tr("Emulated mouse is enabled"),
+ tr("Real mouse input and mouse panning are incompatible. Please disable the "
+ "emulated mouse in input advanced settings to allow mouse panning."));
+ return;
+ }
Settings::values.mouse_panning = !Settings::values.mouse_panning;
if (Settings::values.mouse_panning) {
render_window->installEventFilter(render_window);
@@ -1258,6 +1279,7 @@ void GMainWindow::ConnectWidgetEvents() {
connect(game_list, &GameList::ShowList, this, &GMainWindow::OnGameListShowList);
connect(game_list, &GameList::PopulatingCompleted,
[this] { multiplayer_state->UpdateGameList(game_list->GetModel()); });
+ connect(game_list, &GameList::SaveConfig, this, &GMainWindow::OnSaveConfig);
connect(game_list, &GameList::OpenPerGameGeneralRequested, this,
&GMainWindow::OnGameListOpenPerGameProperties);
@@ -1766,7 +1788,7 @@ void GMainWindow::BootGame(const QString& filename, u64 program_id, std::size_t
std::filesystem::path{Common::U16StringFromBuffer(filename.utf16(), filename.size())}
.filename());
}
- const bool is_64bit = system->Kernel().CurrentProcess()->Is64BitProcess();
+ const bool is_64bit = system->Kernel().ApplicationProcess()->Is64BitProcess();
const auto instruction_set_suffix = is_64bit ? tr("(64-bit)") : tr("(32-bit)");
title_name = tr("%1 %2", "%1 is the title name. %2 indicates if the title is 64-bit or 32-bit")
.arg(QString::fromStdString(title_name), instruction_set_suffix)
@@ -1839,9 +1861,11 @@ void GMainWindow::OnEmulationStopTimeExpired() {
void GMainWindow::OnEmulationStopped() {
shutdown_timer.stop();
- emu_thread->disconnect();
- emu_thread->wait();
- emu_thread = nullptr;
+ if (emu_thread) {
+ emu_thread->disconnect();
+ emu_thread->wait();
+ emu_thread.reset();
+ }
if (shutdown_dialog) {
shutdown_dialog->deleteLater();
@@ -2639,6 +2663,8 @@ void GMainWindow::OnGameListAddDirectory() {
} else {
LOG_WARNING(Frontend, "Selected directory is already in the game list");
}
+
+ OnSaveConfig();
}
void GMainWindow::OnGameListShowList(bool show) {
@@ -2756,8 +2782,7 @@ void GMainWindow::OnMenuInstallToNAND() {
ui->action_Install_File_NAND->setEnabled(false);
install_progress = new QProgressDialog(QString{}, tr("Cancel"), 0, total_size, this);
- install_progress->setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint &
- ~Qt::WindowMaximizeButtonHint);
+ install_progress->setWindowFlags(windowFlags() & ~Qt::WindowMaximizeButtonHint);
install_progress->setAttribute(Qt::WA_DeleteOnClose, true);
install_progress->setFixedWidth(installDialog.GetMinimumWidth() + 40);
install_progress->show();
@@ -3001,8 +3026,10 @@ void GMainWindow::OnRestartGame() {
if (!system->IsPoweredOn()) {
return;
}
- // Make a copy since BootGame edits game_path
- BootGame(QString(current_game_path));
+ // Make a copy since ShutdownGame edits game_path
+ const auto current_game = QString(current_game_path);
+ ShutdownGame();
+ BootGame(current_game);
}
void GMainWindow::OnPauseGame() {
@@ -3029,6 +3056,8 @@ void GMainWindow::OnStopGame() {
if (OnShutdownBegin()) {
OnShutdownBeginDialog();
+ } else {
+ OnEmulationStopped();
}
}
@@ -3364,6 +3393,7 @@ void GMainWindow::OnConfigureTas() {
return;
} else if (result == QDialog::Accepted) {
dialog.ApplyConfiguration();
+ OnSaveConfig();
}
}
@@ -3458,6 +3488,39 @@ void GMainWindow::OnToggleGpuAccuracy() {
UpdateGPUAccuracyButton();
}
+void GMainWindow::OnMute() {
+ Settings::values.audio_muted = !Settings::values.audio_muted;
+ UpdateVolumeUI();
+}
+
+void GMainWindow::OnDecreaseVolume() {
+ Settings::values.audio_muted = false;
+ const auto current_volume = static_cast<s32>(Settings::values.volume.GetValue());
+ int step = 5;
+ if (current_volume <= 30) {
+ step = 2;
+ }
+ if (current_volume <= 6) {
+ step = 1;
+ }
+ Settings::values.volume.SetValue(std::max(current_volume - step, 0));
+ UpdateVolumeUI();
+}
+
+void GMainWindow::OnIncreaseVolume() {
+ Settings::values.audio_muted = false;
+ const auto current_volume = static_cast<s32>(Settings::values.volume.GetValue());
+ int step = 5;
+ if (current_volume < 30) {
+ step = 2;
+ }
+ if (current_volume < 6) {
+ step = 1;
+ }
+ Settings::values.volume.SetValue(current_volume + step);
+ UpdateVolumeUI();
+}
+
void GMainWindow::OnToggleAdaptingFilter() {
auto filter = Settings::values.scaling_filter.GetValue();
if (filter == Settings::ScalingFilter::LastFilter) {
@@ -3465,10 +3528,6 @@ void GMainWindow::OnToggleAdaptingFilter() {
} else {
filter = static_cast<Settings::ScalingFilter>(static_cast<u32>(filter) + 1);
}
- if (Settings::values.renderer_backend.GetValue() == Settings::RendererBackend::OpenGL &&
- filter == Settings::ScalingFilter::Fsr) {
- filter = Settings::ScalingFilter::NearestNeighbor;
- }
Settings::values.scaling_filter.SetValue(filter);
filter_status_button->setChecked(true);
UpdateFilterText();
@@ -3487,7 +3546,7 @@ void GMainWindow::OnToggleGraphicsAPI() {
}
void GMainWindow::OnConfigurePerGame() {
- const u64 title_id = system->GetCurrentProcessProgramID();
+ const u64 title_id = system->GetApplicationProcessProgramID();
OpenPerGameConfiguration(title_id, current_game_path.toStdString());
}
@@ -3646,7 +3705,7 @@ void GMainWindow::OnCaptureScreenshot() {
return;
}
- const u64 title_id = system->GetCurrentProcessProgramID();
+ const u64 title_id = system->GetApplicationProcessProgramID();
const auto screenshot_path =
QString::fromStdString(Common::FS::GetYuzuPathString(Common::FS::YuzuPath::ScreenshotsDir));
const auto date =
@@ -3726,15 +3785,36 @@ void GMainWindow::UpdateWindowTitle(std::string_view title_name, std::string_vie
}
}
+std::string GMainWindow::CreateTASFramesString(
+ std::array<size_t, InputCommon::TasInput::PLAYER_NUMBER> frames) const {
+ std::string string = "";
+ size_t maxPlayerIndex = 0;
+ for (size_t i = 0; i < frames.size(); i++) {
+ if (frames[i] != 0) {
+ if (maxPlayerIndex != 0)
+ string += ", ";
+ while (maxPlayerIndex++ != i)
+ string += "0, ";
+ string += std::to_string(frames[i]);
+ }
+ }
+ return string;
+}
+
QString GMainWindow::GetTasStateDescription() const {
auto [tas_status, current_tas_frame, total_tas_frames] = input_subsystem->GetTas()->GetStatus();
+ std::string tas_frames_string = CreateTASFramesString(total_tas_frames);
switch (tas_status) {
case InputCommon::TasInput::TasState::Running:
- return tr("TAS state: Running %1/%2").arg(current_tas_frame).arg(total_tas_frames);
+ return tr("TAS state: Running %1/%2")
+ .arg(current_tas_frame)
+ .arg(QString::fromStdString(tas_frames_string));
case InputCommon::TasInput::TasState::Recording:
- return tr("TAS state: Recording %1").arg(total_tas_frames);
+ return tr("TAS state: Recording %1").arg(total_tas_frames[0]);
case InputCommon::TasInput::TasState::Stopped:
- return tr("TAS state: Idle %1/%2").arg(current_tas_frame).arg(total_tas_frames);
+ return tr("TAS state: Idle %1/%2")
+ .arg(current_tas_frame)
+ .arg(QString::fromStdString(tas_frames_string));
default:
return tr("TAS State: Invalid");
}
@@ -3899,6 +3979,18 @@ void GMainWindow::UpdateAAText() {
}
}
+void GMainWindow::UpdateVolumeUI() {
+ const auto volume_value = static_cast<int>(Settings::values.volume.GetValue());
+ volume_slider->setValue(volume_value);
+ if (Settings::values.audio_muted) {
+ volume_button->setChecked(false);
+ volume_button->setText(tr("VOLUME: MUTE"));
+ } else {
+ volume_button->setChecked(true);
+ volume_button->setText(tr("VOLUME: %1%", "Volume percentage (e.g. 50%)").arg(volume_value));
+ }
+}
+
void GMainWindow::UpdateStatusButtons() {
renderer_status_button->setChecked(Settings::values.renderer_backend.GetValue() ==
Settings::RendererBackend::Vulkan);
@@ -3907,6 +3999,7 @@ void GMainWindow::UpdateStatusButtons() {
UpdateDockedButton();
UpdateFilterText();
UpdateAAText();
+ UpdateVolumeUI();
}
void GMainWindow::UpdateUISettings() {
@@ -4376,6 +4469,55 @@ void GMainWindow::changeEvent(QEvent* event) {
#undef main
#endif
+static void SetHighDPIAttributes() {
+#ifdef _WIN32
+ // For Windows, we want to avoid scaling artifacts on fractional scaling ratios.
+ // This is done by setting the optimal scaling policy for the primary screen.
+
+ // Create a temporary QApplication.
+ int temp_argc = 0;
+ char** temp_argv = nullptr;
+ QApplication temp{temp_argc, temp_argv};
+
+ // Get the current screen geometry.
+ const QScreen* primary_screen = QGuiApplication::primaryScreen();
+ if (primary_screen == nullptr) {
+ return;
+ }
+
+ const QRect screen_rect = primary_screen->geometry();
+ const int real_width = screen_rect.width();
+ const int real_height = screen_rect.height();
+ const float real_ratio = primary_screen->logicalDotsPerInch() / 96.0f;
+
+ // Recommended minimum width and height for proper window fit.
+ // Any screen with a lower resolution than this will still have a scale of 1.
+ constexpr float minimum_width = 1350.0f;
+ constexpr float minimum_height = 900.0f;
+
+ const float width_ratio = std::max(1.0f, real_width / minimum_width);
+ const float height_ratio = std::max(1.0f, real_height / minimum_height);
+
+ // Get the lower of the 2 ratios and truncate, this is the maximum integer scale.
+ const float max_ratio = std::trunc(std::min(width_ratio, height_ratio));
+
+ if (max_ratio > real_ratio) {
+ QApplication::setHighDpiScaleFactorRoundingPolicy(
+ Qt::HighDpiScaleFactorRoundingPolicy::Round);
+ } else {
+ QApplication::setHighDpiScaleFactorRoundingPolicy(
+ Qt::HighDpiScaleFactorRoundingPolicy::Floor);
+ }
+#else
+ // Other OSes should be better than Windows at fractional scaling.
+ QApplication::setHighDpiScaleFactorRoundingPolicy(
+ Qt::HighDpiScaleFactorRoundingPolicy::PassThrough);
+#endif
+
+ QApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
+ QApplication::setAttribute(Qt::AA_UseHighDpiPixmaps);
+}
+
int main(int argc, char* argv[]) {
std::unique_ptr<Config> config = std::make_unique<Config>();
bool has_broken_vulkan = false;
@@ -4431,8 +4573,16 @@ int main(int argc, char* argv[]) {
}
#endif
+ SetHighDPIAttributes();
+
+#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
+ // Disables the "?" button on all dialogs. Disabled by default on Qt6.
+ QCoreApplication::setAttribute(Qt::AA_DisableWindowContextHelpButton);
+#endif
+
// Enables the core to make the qt created contexts current on std::threads
QCoreApplication::setAttribute(Qt::AA_DontCheckOpenGLContextThreadAffinity);
+
QApplication app(argc, argv);
#ifdef _WIN32
diff --git a/src/yuzu/main.h b/src/yuzu/main.h
index f25ce65a8..a23b373a5 100644
--- a/src/yuzu/main.h
+++ b/src/yuzu/main.h
@@ -12,6 +12,7 @@
#include "common/announce_multiplayer_room.h"
#include "common/common_types.h"
+#include "input_common/drivers/tas_input.h"
#include "yuzu/compatibility_list.h"
#include "yuzu/hotkeys.h"
@@ -36,6 +37,8 @@ class QLabel;
class MultiplayerState;
class QPushButton;
class QProgressDialog;
+class QSlider;
+class QHBoxLayout;
class WaitTreeWidget;
enum class GameListOpenTarget;
enum class GameListRemoveTarget;
@@ -266,6 +269,9 @@ private:
void changeEvent(QEvent* event) override;
void closeEvent(QCloseEvent* event) override;
+ std::string CreateTASFramesString(
+ std::array<size_t, InputCommon::TasInput::PLAYER_NUMBER> frames) const;
+
#ifdef __unix__
void SetupSigInterrupts();
static void HandleSigInterrupt(int);
@@ -308,6 +314,9 @@ private slots:
void OnMenuRecentFile();
void OnConfigure();
void OnConfigureTas();
+ void OnDecreaseVolume();
+ void OnIncreaseVolume();
+ void OnMute();
void OnTasStartStop();
void OnTasRecord();
void OnTasReset();
@@ -360,6 +369,7 @@ private:
void UpdateAPIText();
void UpdateFilterText();
void UpdateAAText();
+ void UpdateVolumeUI();
void UpdateStatusBar();
void UpdateGPUAccuracyButton();
void UpdateStatusButtons();
@@ -408,6 +418,9 @@ private:
QPushButton* dock_status_button = nullptr;
QPushButton* filter_status_button = nullptr;
QPushButton* aa_status_button = nullptr;
+ QPushButton* volume_button = nullptr;
+ QWidget* volume_popup = nullptr;
+ QSlider* volume_slider = nullptr;
QTimer status_bar_update_timer;
std::unique_ptr<Config> config;
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() {
}
}
}
- switch (static_cast<ConnectionType>(ui->connection_type->currentIndex())) {
- case ConnectionType::TraversalServer:
- break;
- case ConnectionType::IP:
- if (!ui->ip->hasAcceptableInput()) {
- NetworkMessage::ErrorManager::ShowError(
- NetworkMessage::ErrorManager::IP_ADDRESS_NOT_VALID);
- return;
- }
- if (!ui->port->hasAcceptableInput()) {
- NetworkMessage::ErrorManager::ShowError(NetworkMessage::ErrorManager::PORT_NOT_VALID);
- return;
- }
- break;
+ if (!ui->ip->hasAcceptableInput()) {
+ NetworkMessage::ErrorManager::ShowError(NetworkMessage::ErrorManager::IP_ADDRESS_NOT_VALID);
+ return;
+ }
+ if (!ui->port->hasAcceptableInput()) {
+ NetworkMessage::ErrorManager::ShowError(NetworkMessage::ErrorManager::PORT_NOT_VALID);
+ return;
}
// 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 @@
<number>0</number>
</property>
<item>
- <widget class="QComboBox" name="connection_type">
- <item>
- <property name="text">
- <string>IP Address</string>
- </property>
- </item>
- </widget>
- </item>
- <item>
<widget class="QWidget" name="ip_container" native="true">
<layout class="QHBoxLayout" name="ip_layout">
<property name="leftMargin">
- <number>5</number>
+ <number>0</number>
</property>
<property name="topMargin">
<number>0</number>
@@ -53,17 +44,17 @@
<item>
<widget class="QLabel" name="label_2">
<property name="text">
- <string>IP</string>
+ <string>Server Address</string>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="ip">
<property name="toolTip">
- <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>
+ <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>
</property>
<property name="maxLength">
- <number>16</number>
+ <number>253</number>
</property>
</widget>
</item>
@@ -85,6 +76,12 @@
<property name="placeholderText">
<string notr="true" extracomment="placeholder string that tells user default port">24872</string>
</property>
+ <property name="maximumSize">
+ <size>
+ <width>65</width>
+ <height>50</height>
+ </size>
+ </property>
</widget>
</item>
</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,
// UI Buttons
connect(ui->refresh_list, &QPushButton::clicked, this, &Lobby::RefreshLobby);
connect(ui->games_owned, &QCheckBox::toggled, proxy, &LobbyFilterProxyModel::SetFilterOwned);
+ connect(ui->hide_empty, &QCheckBox::toggled, proxy, &LobbyFilterProxyModel::SetFilterEmpty);
connect(ui->hide_full, &QCheckBox::toggled, proxy, &LobbyFilterProxyModel::SetFilterFull);
connect(ui->search, &QLineEdit::textChanged, proxy, &LobbyFilterProxyModel::SetFilterSearch);
connect(ui->room_list, &QTreeView::doubleClicked, this, &Lobby::OnJoinRoom);
@@ -329,6 +330,16 @@ bool LobbyFilterProxyModel::filterAcceptsRow(int sourceRow, const QModelIndex& s
return true;
}
+ // filter by empty rooms
+ if (filter_empty) {
+ QModelIndex member_list = sourceModel()->index(sourceRow, Column::MEMBER, sourceParent);
+ int player_count =
+ sourceModel()->data(member_list, LobbyItemMemberList::MemberListRole).toList().size();
+ if (player_count == 0) {
+ return false;
+ }
+ }
+
// filter by filled rooms
if (filter_full) {
QModelIndex member_list = sourceModel()->index(sourceRow, Column::MEMBER, sourceParent);
@@ -399,6 +410,11 @@ void LobbyFilterProxyModel::SetFilterOwned(bool filter) {
invalidate();
}
+void LobbyFilterProxyModel::SetFilterEmpty(bool filter) {
+ filter_empty = filter;
+ invalidate();
+}
+
void LobbyFilterProxyModel::SetFilterFull(bool filter) {
filter_full = filter;
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:
public slots:
void SetFilterOwned(bool);
+ void SetFilterEmpty(bool);
void SetFilterFull(bool);
void SetFilterSearch(const QString&);
private:
QStandardItemModel* game_list;
bool filter_owned = false;
+ bool filter_empty = false;
bool filter_full = false;
QString filter_search;
};
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 @@
</widget>
</item>
<item>
+ <widget class="QCheckBox" name="hide_empty">
+ <property name="text">
+ <string>Hide Empty Rooms</string>
+ </property>
+ </widget>
+ </item>
+ <item>
<widget class="QCheckBox" name="hide_full">
<property name="text">
<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:
QRegularExpression(QStringLiteral("^[a-zA-Z0-9._ -]{4,20}"));
QRegularExpressionValidator nickname;
- /// ipv4 address only
- // TODO remove this when we support hostnames in direct connect
+ /// ipv4 / ipv6 / hostnames
QRegularExpression ip_regex = QRegularExpression(QStringLiteral(
- "(([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}|"
- "2[0-4][0-9]|25[0-5])"));
+ // IPv4 regex
+ "^((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]?)$|"
+ // IPv6 regex
+ "^((([0-9A-Fa-f]{1,4}:){7}([0-9A-Fa-f]{1,4}|:))|"
+ "(([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-"
+ "5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)){3})|:))|"
+ "(([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)"
+ "(\\.(25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)){3})|:))|"
+ "(([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]"
+ "\\d|1\\d\\d|[1-9]?\\d)(\\.(25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)){3}))|:))|"
+ "(([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["
+ "0-4]\\d|1\\d\\d|[1-9]?\\d)(\\.(25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)){3}))|:))|"
+ "(([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["
+ "0-4]\\d|1\\d\\d|[1-9]?\\d)(\\.(25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)){3}))|:))|"
+ "(([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["
+ "0-4]\\d|1\\d\\d|[1-9]?\\d)(\\.(25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)){3}))|:))|"
+ "(:(((:[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]?"
+ "\\d)(\\.(25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)){3}))|:)))(%.+)?$|"
+ // Hostname regex
+ "^([a-zA-Z0-9]+(-[a-zA-Z0-9]+)*\\.)+[a-zA-Z]{2,}$"));
QRegularExpressionValidator ip;
/// 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 {
QString keyseq;
QString controller_keyseq;
int context;
+ bool repeat;
};
struct 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} {
LimitableInputDialog::~LimitableInputDialog() = default;
void LimitableInputDialog::CreateUI() {
- setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);
-
text_label = new QLabel(this);
text_entry = new QLineEdit(this);
text_label_invalid = new QLabel(this);
diff --git a/src/yuzu/util/overlay_dialog.cpp b/src/yuzu/util/overlay_dialog.cpp
index 796f5bf41..ee35a3e15 100644
--- a/src/yuzu/util/overlay_dialog.cpp
+++ b/src/yuzu/util/overlay_dialog.cpp
@@ -163,7 +163,7 @@ void OverlayDialog::MoveAndResizeWindow() {
const auto height = static_cast<float>(parentWidget()->height());
// High DPI
- const float dpi_scale = parentWidget()->windowHandle()->screen()->logicalDotsPerInch() / 96.0f;
+ const float dpi_scale = screen()->logicalDotsPerInch() / 96.0f;
const auto title_text_font_size = BASE_TITLE_FONT_SIZE * (height / BASE_HEIGHT) / dpi_scale;
const auto body_text_font_size =
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 @@
SequenceDialog::SequenceDialog(QWidget* parent) : QDialog(parent) {
setWindowTitle(tr("Enter a hotkey"));
- setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);
key_sequence = new QKeySequenceEdit;
diff --git a/src/yuzu_cmd/config.cpp b/src/yuzu_cmd/config.cpp
index 527017282..3b6dce296 100644
--- a/src/yuzu_cmd/config.cpp
+++ b/src/yuzu_cmd/config.cpp
@@ -176,6 +176,10 @@ void Config::ReadValues() {
Settings::values.debug_pad_analogs[i] = default_param;
}
+ ReadSetting("ControlsGeneral", Settings::values.enable_raw_input);
+ ReadSetting("ControlsGeneral", Settings::values.enable_joycon_driver);
+ ReadSetting("ControlsGeneral", Settings::values.enable_procon_driver);
+ ReadSetting("ControlsGeneral", Settings::values.emulate_analog_keyboard);
ReadSetting("ControlsGeneral", Settings::values.vibration_enabled);
ReadSetting("ControlsGeneral", Settings::values.enable_accurate_vibrations);
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 @@
namespace DefaultINI {
-const char* sdl2_config_file = R"(
-
+const char* sdl2_config_file =
+ R"(
[ControlsP0]
# The input devices and parameters for each Switch native input
# 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"(
# Escape characters $0 (for ':'), $1 (for ',') and $2 (for '$') can be used in values
# Indicates if this player should be connected at boot
+# 0 (default): Disabled, 1: Enabled
connected=
# for button input, the following devices are available:
@@ -94,6 +95,18 @@ motionright=
# 0 (default): Disabled, 1: Enabled
debug_pad_enabled =
+# Enable sdl raw input. Allows to configure up to 8 xinput controllers.
+# 0 (default): Disabled, 1: Enabled
+enable_raw_input =
+
+# Enable yuzu joycon driver instead of SDL drive.
+# 0: Disabled, 1 (default): Enabled
+enable_joycon_driver =
+
+# Emulates an analog input from buttons. Allowing to dial any angle.
+# 0 (default): Disabled, 1: Enabled
+emulate_analog_keyboard =
+
# Whether to enable or disable vibration
# 0: Disabled, 1 (default): Enabled
vibration_enabled=
@@ -143,6 +156,8 @@ mouse_enabled =
# 0 (default): Disabled, 1: Enabled
keyboard_enabled =
+)"
+ R"(
[Core]
# Whether to use multi-core for CPU emulation
# 0: Disabled, 1 (default): Enabled
@@ -242,6 +257,8 @@ cpuopt_unsafe_fastmem_check =
# 0: Disabled, 1 (default): Enabled
cpuopt_unsafe_ignore_global_monitor =
+)"
+ R"(
[Renderer]
# Which backend API to use.
# 0: OpenGL, 1 (default): Vulkan
@@ -269,11 +286,14 @@ vulkan_device =
# 0: 0.5x (360p/540p) [EXPERIMENTAL]
# 1: 0.75x (540p/810p) [EXPERIMENTAL]
# 2 (default): 1x (720p/1080p)
-# 3: 2x (1440p/2160p)
-# 4: 3x (2160p/3240p)
-# 5: 4x (2880p/4320p)
-# 6: 5x (3600p/5400p)
-# 7: 6x (4320p/6480p)
+# 3: 1.5x (1080p/1620p) [EXPERIMENTAL]
+# 4: 2x (1440p/2160p)
+# 5: 3x (2160p/3240p)
+# 6: 4x (2880p/4320p)
+# 7: 5x (3600p/5400p)
+# 8: 6x (4320p/6480p)
+# 9: 7x (5040p/7560p)
+# 10: 8x (5760/8640p)
resolution_setup =
# Pixel filter to use when up- or down-sampling rendered frames.
@@ -282,11 +302,11 @@ resolution_setup =
# 2: Bicubic
# 3: Gaussian
# 4: ScaleForce
-# 5: AMD FidelityFX™️ Super Resolution [Vulkan Only]
+# 5: AMD FidelityFX™️ Super Resolution
scaling_filter =
# Anti-Aliasing (AA)
-# 0 (default): None, 1: FXAA
+# 0 (default): None, 1: FXAA, 2: SMAA
anti_aliasing =
# Whether to use fullscreen or borderless window mode
@@ -360,6 +380,8 @@ bg_red =
bg_blue =
bg_green =
+)"
+ R"(
[Audio]
# Which audio output engine to use.
# 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..5153cdb79 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 @@
EmuWindow_SDL2::EmuWindow_SDL2(InputCommon::InputSubsystem* input_subsystem_, Core::System& system_)
: input_subsystem{input_subsystem_}, system{system_} {
- if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_JOYSTICK) < 0) {
+ input_subsystem->Initialize();
+ if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_JOYSTICK | SDL_INIT_GAMECONTROLLER) < 0) {
LOG_CRITICAL(Frontend, "Failed to initialize SDL2! Exiting...");
exit(1);
}
- input_subsystem->Initialize();
SDL_SetMainReady();
}
@@ -32,10 +32,6 @@ EmuWindow_SDL2::~EmuWindow_SDL2() {
SDL_Quit();
}
-void EmuWindow_SDL2::OnMouseMotion(s32 x, s32 y) {
- input_subsystem->GetMouse()->MouseMove(x, y, 0, 0, 0, 0);
-}
-
InputCommon::MouseButton EmuWindow_SDL2::SDLButtonToMouseButton(u32 button) const {
switch (button) {
case SDL_BUTTON_LEFT:
@@ -53,44 +49,40 @@ InputCommon::MouseButton EmuWindow_SDL2::SDLButtonToMouseButton(u32 button) cons
}
}
+std::pair<float, float> EmuWindow_SDL2::MouseToTouchPos(s32 touch_x, s32 touch_y) const {
+ int w, h;
+ SDL_GetWindowSize(render_window, &w, &h);
+ const float fx = static_cast<float>(touch_x) / w;
+ const float fy = static_cast<float>(touch_y) / h;
+
+ return {std::clamp<float>(fx, 0.0f, 1.0f), std::clamp<float>(fy, 0.0f, 1.0f)};
+}
+
void EmuWindow_SDL2::OnMouseButton(u32 button, u8 state, s32 x, s32 y) {
const auto mouse_button = SDLButtonToMouseButton(button);
if (state == SDL_PRESSED) {
- input_subsystem->GetMouse()->PressButton(x, y, 0, 0, mouse_button);
+ const auto [touch_x, touch_y] = MouseToTouchPos(x, y);
+ input_subsystem->GetMouse()->PressButton(x, y, mouse_button);
+ input_subsystem->GetMouse()->PressMouseButton(mouse_button);
+ input_subsystem->GetMouse()->PressTouchButton(touch_x, touch_y, mouse_button);
} else {
input_subsystem->GetMouse()->ReleaseButton(mouse_button);
}
}
-std::pair<unsigned, unsigned> EmuWindow_SDL2::TouchToPixelPos(float touch_x, float touch_y) const {
- int w, h;
- SDL_GetWindowSize(render_window, &w, &h);
-
- touch_x *= w;
- touch_y *= h;
-
- return {static_cast<unsigned>(std::max(std::round(touch_x), 0.0f)),
- static_cast<unsigned>(std::max(std::round(touch_y), 0.0f))};
+void EmuWindow_SDL2::OnMouseMotion(s32 x, s32 y) {
+ const auto [touch_x, touch_y] = MouseToTouchPos(x, y);
+ input_subsystem->GetMouse()->Move(x, y, 0, 0);
+ input_subsystem->GetMouse()->MouseMove(touch_x, touch_y);
+ input_subsystem->GetMouse()->TouchMove(touch_x, touch_y);
}
void EmuWindow_SDL2::OnFingerDown(float x, float y, std::size_t id) {
- int width, height;
- SDL_GetWindowSize(render_window, &width, &height);
- const auto [px, py] = TouchToPixelPos(x, y);
- const float fx = px * 1.0f / width;
- const float fy = py * 1.0f / height;
-
- input_subsystem->GetTouchScreen()->TouchPressed(fx, fy, id);
+ input_subsystem->GetTouchScreen()->TouchPressed(x, y, id);
}
void EmuWindow_SDL2::OnFingerMotion(float x, float y, std::size_t id) {
- int width, height;
- SDL_GetWindowSize(render_window, &width, &height);
- const auto [px, py] = TouchToPixelPos(x, y);
- const float fx = px * 1.0f / width;
- const float fy = py * 1.0f / height;
-
- input_subsystem->GetTouchScreen()->TouchMoved(fx, fy, id);
+ input_subsystem->GetTouchScreen()->TouchMoved(x, y, id);
}
void 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:
/// Called by WaitEvent when a key is pressed or released.
void OnKeyEvent(int key, u8 state);
- /// Called by WaitEvent when the mouse moves.
- void OnMouseMotion(s32 x, s32 y);
-
/// Converts a SDL mouse button into MouseInput mouse button
InputCommon::MouseButton SDLButtonToMouseButton(u32 button) const;
+ /// Translates pixel position to float position
+ std::pair<float, float> MouseToTouchPos(s32 touch_x, s32 touch_y) const;
+
/// Called by WaitEvent when a mouse button is pressed or released
void OnMouseButton(u32 button, u8 state, s32 x, s32 y);
- /// Translates pixel position (0..1) to pixel positions
- std::pair<unsigned, unsigned> TouchToPixelPos(float touch_x, float touch_y) const;
+ /// Called by WaitEvent when the mouse moves.
+ void OnMouseMotion(s32 x, s32 y);
/// Called by WaitEvent when a finger starts touching the touchscreen
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..77edd58ca 100644
--- a/src/yuzu_cmd/yuzu.cpp
+++ b/src/yuzu_cmd/yuzu.cpp
@@ -62,13 +62,15 @@ __declspec(dllexport) int AmdPowerXpressRequestHighPerformance = 1;
static void PrintHelp(const char* argv0) {
std::cout << "Usage: " << argv0
<< " [options] <filename>\n"
- "-m, --multiplayer=nick:password@address:port"
- " Nickname, password, address and port for multiplayer\n"
+ "-c, --config Load the specified configuration file\n"
"-f, --fullscreen Start in fullscreen mode\n"
+ "-g, --game File path of the game to load\n"
"-h, --help Display this help and exit\n"
- "-v, --version Output version information and exit\n"
+ "-m, --multiplayer=nick:password@address:port"
+ " Nickname, password, address and port for multiplayer\n"
"-p, --program Pass following string as arguments to executable\n"
- "-c, --config Load the specified configuration file\n";
+ "-u, --user Select a specific user profile from 0 to 7\n"
+ "-v, --version Output version information and exit\n";
}
static void PrintVersion() {
@@ -199,6 +201,7 @@ int main(int argc, char** argv) {
std::string filepath;
std::optional<std::string> config_path;
std::string program_args;
+ std::optional<int> selected_user;
bool use_multiplayer = false;
bool fullscreen = false;
@@ -209,12 +212,14 @@ int main(int argc, char** argv) {
static struct option long_options[] = {
// clang-format off
- {"multiplayer", required_argument, 0, 'm'},
+ {"config", required_argument, 0, 'c'},
{"fullscreen", no_argument, 0, 'f'},
{"help", no_argument, 0, 'h'},
- {"version", no_argument, 0, 'v'},
+ {"game", required_argument, 0, 'g'},
+ {"multiplayer", required_argument, 0, 'm'},
{"program", optional_argument, 0, 'p'},
- {"config", required_argument, 0, 'c'},
+ {"user", required_argument, 0, 'u'},
+ {"version", no_argument, 0, 'v'},
{0, 0, 0, 0},
// clang-format on
};
@@ -223,6 +228,21 @@ int main(int argc, char** argv) {
int arg = getopt_long(argc, argv, "g:fhvp::c:", long_options, &option_index);
if (arg != -1) {
switch (static_cast<char>(arg)) {
+ case 'c':
+ config_path = optarg;
+ break;
+ case 'f':
+ fullscreen = true;
+ LOG_INFO(Frontend, "Starting in fullscreen mode...");
+ break;
+ case 'h':
+ PrintHelp(argv[0]);
+ return 0;
+ case 'g': {
+ const std::string str_arg(optarg);
+ filepath = str_arg;
+ break;
+ }
case 'm': {
use_multiplayer = true;
const std::string str_arg(optarg);
@@ -255,23 +275,16 @@ int main(int argc, char** argv) {
}
break;
}
- case 'f':
- fullscreen = true;
- LOG_INFO(Frontend, "Starting in fullscreen mode...");
+ case 'p':
+ program_args = argv[optind];
+ ++optind;
break;
- case 'h':
- PrintHelp(argv[0]);
+ case 'u':
+ selected_user = atoi(optarg);
return 0;
case 'v':
PrintVersion();
return 0;
- case 'p':
- program_args = argv[optind];
- ++optind;
- break;
- case 'c':
- config_path = optarg;
- break;
}
} else {
#ifdef _WIN32
@@ -295,6 +308,10 @@ int main(int argc, char** argv) {
Settings::values.program_args = program_args;
}
+ if (selected_user.has_value()) {
+ Settings::values.current_user = std::clamp(*selected_user, 0, 7);
+ }
+
#ifdef _WIN32
LocalFree(argv_w);
#endif
@@ -388,7 +405,7 @@ int main(int argc, char** argv) {
if (Settings::values.use_disk_shader_cache.GetValue()) {
system.Renderer().ReadRasterizer()->LoadDiskResources(
- system.GetCurrentProcessProgramID(), std::stop_token{},
+ system.GetApplicationProcessProgramID(), std::stop_token{},
[](VideoCore::LoadCallbackStage, size_t value, size_t total) {});
}